1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/translator.py Wed Oct 19 23:40:41 2016 +0200
1.3 @@ -0,0 +1,1105 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Translate programs.
1.8 +
1.9 +Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk>
1.10 +
1.11 +This program is free software; you can redistribute it and/or modify it under
1.12 +the terms of the GNU General Public License as published by the Free Software
1.13 +Foundation; either version 3 of the License, or (at your option) any later
1.14 +version.
1.15 +
1.16 +This program is distributed in the hope that it will be useful, but WITHOUT
1.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.19 +details.
1.20 +
1.21 +You should have received a copy of the GNU General Public License along with
1.22 +this program. If not, see <http://www.gnu.org/licenses/>.
1.23 +"""
1.24 +
1.25 +from common import *
1.26 +from encoders import *
1.27 +from os.path import exists, join
1.28 +from os import makedirs
1.29 +import compiler
1.30 +import results
1.31 +
1.32 +class Translator(CommonOutput):
1.33 +
1.34 + "A program translator."
1.35 +
1.36 + def __init__(self, importer, deducer, optimiser, output):
1.37 + self.importer = importer
1.38 + self.deducer = deducer
1.39 + self.optimiser = optimiser
1.40 + self.output = output
1.41 +
1.42 + def to_output(self):
1.43 + output = join(self.output, "src")
1.44 +
1.45 + if not exists(output):
1.46 + makedirs(output)
1.47 +
1.48 + self.check_output()
1.49 +
1.50 + for module in self.importer.modules.values():
1.51 + tm = TranslatedModule(module.name, self.importer, self.deducer, self.optimiser)
1.52 + tm.translate(module.filename, join(output, "%s.c" % module.name))
1.53 +
1.54 +# Classes representing intermediate translation results.
1.55 +
1.56 +class TranslationResult:
1.57 +
1.58 + "An abstract translation result mix-in."
1.59 +
1.60 + pass
1.61 +
1.62 +class Expression(results.Result, TranslationResult):
1.63 +
1.64 + "A general expression."
1.65 +
1.66 + def __init__(self, s):
1.67 + self.s = s
1.68 + def __str__(self):
1.69 + return self.s
1.70 + def __repr__(self):
1.71 + return "Expression(%r)" % self.s
1.72 +
1.73 +class TrResolvedNameRef(results.ResolvedNameRef, TranslationResult):
1.74 +
1.75 + "A reference to a name in the translation."
1.76 +
1.77 + def __str__(self):
1.78 +
1.79 + "Return an output representation of the referenced name."
1.80 +
1.81 + # Use any alias in preference to the origin of the name.
1.82 +
1.83 + name = self.get_name()
1.84 +
1.85 + # Use any static origin in preference to any alias.
1.86 + # For sources, any identified static origin will be constant and thus
1.87 + # usable directly. For targets, no constant should be assigned and thus
1.88 + # the alias (or any plain name) will be used.
1.89 +
1.90 + origin = self.static() and self.get_origin()
1.91 + name = origin and encode_path(origin) or name and encode_path(name) or encode_path(self.name)
1.92 +
1.93 + # Assignments.
1.94 +
1.95 + if self.expr:
1.96 +
1.97 + # Eliminate assignments between constants.
1.98 +
1.99 + if self.static() and isinstance(self.expr, results.ResolvedNameRef) and self.expr.static():
1.100 + return ""
1.101 + else:
1.102 + return "%s = %s" % (name, self.expr)
1.103 +
1.104 + # Expressions.
1.105 +
1.106 + else:
1.107 + return name
1.108 +
1.109 +class TrConstantValueRef(results.ConstantValueRef, TranslationResult):
1.110 +
1.111 + "A constant value reference in the translation."
1.112 +
1.113 + def __str__(self):
1.114 + return "const%d" % self.number
1.115 +
1.116 +class TrLiteralSequenceRef(results.LiteralSequenceRef, TranslationResult):
1.117 +
1.118 + "A reference representing a sequence of values."
1.119 +
1.120 + def __str__(self):
1.121 + return str(self.node)
1.122 +
1.123 +class AttrResult(Expression, TranslationResult):
1.124 +
1.125 + "A translation result for an attribute access."
1.126 +
1.127 + def __init__(self, s, refs):
1.128 + Expression.__init__(self, s)
1.129 + self.refs = refs
1.130 +
1.131 + def get_origin(self):
1.132 + return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
1.133 +
1.134 + def __repr__(self):
1.135 + return "AttrResult(%r, %r)" % (self.s, self.origin)
1.136 +
1.137 +class PredefinedConstantRef(AttrResult):
1.138 +
1.139 + "A predefined constant reference."
1.140 +
1.141 + def __init__(self, value):
1.142 + self.value = value
1.143 + self.only_function_attrs = False
1.144 +
1.145 + def __str__(self):
1.146 + return self.value
1.147 +
1.148 + def __repr__(self):
1.149 + return "PredefinedConstantRef(%r)" % self.value
1.150 +
1.151 +def make_expression(expr):
1.152 +
1.153 + "Make a new expression from the existing 'expr'."
1.154 +
1.155 + if isinstance(expr, results.Result):
1.156 + return expr
1.157 + else:
1.158 + return Expression(str(expr))
1.159 +
1.160 +# The actual translation process itself.
1.161 +
1.162 +class TranslatedModule(CommonModule):
1.163 +
1.164 + "A module translator."
1.165 +
1.166 + def __init__(self, name, importer, deducer, optimiser):
1.167 + CommonModule.__init__(self, name, importer)
1.168 + self.deducer = deducer
1.169 + self.optimiser = optimiser
1.170 +
1.171 + # Output stream.
1.172 +
1.173 + self.out = None
1.174 + self.indent = 0
1.175 + self.tabstop = " "
1.176 +
1.177 + # Recorded namespaces.
1.178 +
1.179 + self.namespaces = []
1.180 + self.in_conditional = False
1.181 +
1.182 + # Attribute access counting.
1.183 +
1.184 + self.attr_accesses = {}
1.185 +
1.186 + def __repr__(self):
1.187 + return "TranslatedModule(%r, %r)" % (self.name, self.importer)
1.188 +
1.189 + def translate(self, filename, output_filename):
1.190 +
1.191 + """
1.192 + Parse the file having the given 'filename', writing the translation to
1.193 + the given 'output_filename'.
1.194 + """
1.195 +
1.196 + self.parse_file(filename)
1.197 +
1.198 + # Collect function namespaces for separate processing.
1.199 +
1.200 + self.record_namespaces(self.astnode)
1.201 +
1.202 + # Reset the lambda naming (in order to obtain the same names again) and
1.203 + # translate the program.
1.204 +
1.205 + self.reset_lambdas()
1.206 +
1.207 + self.out = open(output_filename, "w")
1.208 + try:
1.209 + # Process namespaces, writing the translation.
1.210 +
1.211 + for path, node in self.namespaces:
1.212 + self.process_namespace(path, node)
1.213 +
1.214 + # Process the module namespace including class namespaces.
1.215 +
1.216 + self.process_namespace([], self.astnode)
1.217 +
1.218 + finally:
1.219 + self.out.close()
1.220 +
1.221 + def have_object(self):
1.222 +
1.223 + "Return whether a namespace is a recorded object."
1.224 +
1.225 + return self.importer.objects.get(self.get_namespace_path())
1.226 +
1.227 + def get_builtin(self, name):
1.228 + return self.importer.get_object("__builtins__.%s" % name)
1.229 +
1.230 + def in_method(self, path):
1.231 + class_name, method_name = path.rsplit(".", 1)
1.232 + return self.importer.classes.has_key(class_name) and class_name
1.233 +
1.234 + # Namespace recording.
1.235 +
1.236 + def record_namespaces(self, node):
1.237 +
1.238 + "Process the program structure 'node', recording namespaces."
1.239 +
1.240 + for n in node.getChildNodes():
1.241 + self.record_namespaces_in_node(n)
1.242 +
1.243 + def record_namespaces_in_node(self, node):
1.244 +
1.245 + "Process the program structure 'node', recording namespaces."
1.246 +
1.247 + # Function namespaces within modules, classes and other functions.
1.248 + # Functions appearing within conditional statements are given arbitrary
1.249 + # names.
1.250 +
1.251 + if isinstance(node, compiler.ast.Function):
1.252 + self.record_function_node(node, (self.in_conditional or self.in_function) and self.get_lambda_name() or node.name)
1.253 +
1.254 + elif isinstance(node, compiler.ast.Lambda):
1.255 + self.record_function_node(node, self.get_lambda_name())
1.256 +
1.257 + # Classes are visited, but may be ignored if inside functions.
1.258 +
1.259 + elif isinstance(node, compiler.ast.Class):
1.260 + self.enter_namespace(node.name)
1.261 + if self.have_object():
1.262 + self.record_namespaces(node)
1.263 + self.exit_namespace()
1.264 +
1.265 + # Conditional nodes are tracked so that function definitions may be
1.266 + # handled. Since "for" loops are converted to "while" loops, they are
1.267 + # included here.
1.268 +
1.269 + elif isinstance(node, (compiler.ast.For, compiler.ast.If, compiler.ast.While)):
1.270 + in_conditional = self.in_conditional
1.271 + self.in_conditional = True
1.272 + self.record_namespaces(node)
1.273 + self.in_conditional = in_conditional
1.274 +
1.275 + # All other nodes are processed depth-first.
1.276 +
1.277 + else:
1.278 + self.record_namespaces(node)
1.279 +
1.280 + def record_function_node(self, n, name):
1.281 +
1.282 + """
1.283 + Record the given function, lambda, if expression or list comprehension
1.284 + node 'n' with the given 'name'.
1.285 + """
1.286 +
1.287 + self.in_function = True
1.288 + self.enter_namespace(name)
1.289 +
1.290 + if self.have_object():
1.291 +
1.292 + # Record the namespace path and the node itself.
1.293 +
1.294 + self.namespaces.append((self.namespace_path[:], n))
1.295 + self.record_namespaces_in_node(n.code)
1.296 +
1.297 + self.exit_namespace()
1.298 + self.in_function = False
1.299 +
1.300 + # Constant referencing.
1.301 +
1.302 + def get_literal_instance(self, n, name):
1.303 +
1.304 + """
1.305 + For node 'n', return a reference for the type of the given 'name'.
1.306 + """
1.307 +
1.308 + ref = self.get_builtin(name)
1.309 +
1.310 + if name in ("dict", "list", "tuple"):
1.311 + return self.process_literal_sequence_node(n, name, ref, TrLiteralSequenceRef)
1.312 + else:
1.313 + path = self.get_namespace_path()
1.314 + local_number = self.importer.all_constants[path][n.value]
1.315 + constant_name = "$c%d" % local_number
1.316 + objpath = self.get_object_path(constant_name)
1.317 + number = self.optimiser.constant_numbers[objpath]
1.318 + return TrConstantValueRef(constant_name, ref.instance_of(), n.value, number)
1.319 +
1.320 + # Namespace translation.
1.321 +
1.322 + def process_namespace(self, path, node):
1.323 +
1.324 + """
1.325 + Process the namespace for the given 'path' defined by the given 'node'.
1.326 + """
1.327 +
1.328 + self.namespace_path = path
1.329 +
1.330 + if isinstance(node, (compiler.ast.Function, compiler.ast.Lambda)):
1.331 + self.in_function = True
1.332 + self.process_function_body_node(node)
1.333 + else:
1.334 + self.in_function = False
1.335 + self.invocation_depth = 0
1.336 + self.invocation_argument_depth = 0
1.337 + self.invocation_kw_argument_depth = 0
1.338 + self.start_module()
1.339 + self.process_structure(node)
1.340 + self.end_module()
1.341 +
1.342 + def process_structure(self, node):
1.343 +
1.344 + "Process the given 'node' or result."
1.345 +
1.346 + if isinstance(node, results.Result):
1.347 + return node
1.348 + else:
1.349 + return CommonModule.process_structure(self, node)
1.350 +
1.351 + def process_structure_node(self, n):
1.352 +
1.353 + "Process the individual node 'n'."
1.354 +
1.355 + # Plain statements emit their expressions.
1.356 +
1.357 + if isinstance(n, compiler.ast.Discard):
1.358 + expr = self.process_structure_node(n.expr)
1.359 + self.statement(expr)
1.360 +
1.361 + # Nodes using operator module functions.
1.362 +
1.363 + elif isinstance(n, compiler.ast.Operator):
1.364 + return self.process_operator_node(n)
1.365 +
1.366 + elif isinstance(n, compiler.ast.AugAssign):
1.367 + self.process_augassign_node(n)
1.368 +
1.369 + elif isinstance(n, compiler.ast.Compare):
1.370 + return self.process_compare_node(n)
1.371 +
1.372 + elif isinstance(n, compiler.ast.Slice):
1.373 + return self.process_slice_node(n)
1.374 +
1.375 + elif isinstance(n, compiler.ast.Sliceobj):
1.376 + return self.process_sliceobj_node(n)
1.377 +
1.378 + elif isinstance(n, compiler.ast.Subscript):
1.379 + return self.process_subscript_node(n)
1.380 +
1.381 + # Classes are visited, but may be ignored if inside functions.
1.382 +
1.383 + elif isinstance(n, compiler.ast.Class):
1.384 + self.process_class_node(n)
1.385 +
1.386 + # Functions within namespaces have any dynamic defaults initialised.
1.387 +
1.388 + elif isinstance(n, compiler.ast.Function):
1.389 + self.process_function_node(n)
1.390 +
1.391 + # Lambdas are replaced with references to separately-generated
1.392 + # functions.
1.393 +
1.394 + elif isinstance(n, compiler.ast.Lambda):
1.395 + return self.process_lambda_node(n)
1.396 +
1.397 + # Assignments.
1.398 +
1.399 + elif isinstance(n, compiler.ast.Assign):
1.400 +
1.401 + # Handle each assignment node.
1.402 +
1.403 + for node in n.nodes:
1.404 + self.process_assignment_node(node, n.expr)
1.405 +
1.406 + # Assignments within non-Assign nodes.
1.407 + # NOTE: Cover all possible nodes employing these.
1.408 +
1.409 + elif isinstance(n, compiler.ast.AssName):
1.410 + self.process_assignment_node(n, compiler.ast.Name("$temp"))
1.411 +
1.412 + elif isinstance(n, compiler.ast.AssAttr):
1.413 + self.process_attribute_access(n)
1.414 +
1.415 + # Accesses.
1.416 +
1.417 + elif isinstance(n, compiler.ast.Getattr):
1.418 + return self.process_attribute_access(n)
1.419 +
1.420 + # Names.
1.421 +
1.422 + elif isinstance(n, compiler.ast.Name):
1.423 + return self.process_name_node(n)
1.424 +
1.425 + # Loops and conditionals.
1.426 +
1.427 + elif isinstance(n, compiler.ast.For):
1.428 + self.process_for_node(n)
1.429 +
1.430 + elif isinstance(n, compiler.ast.While):
1.431 + self.process_while_node(n)
1.432 +
1.433 + elif isinstance(n, compiler.ast.If):
1.434 + self.process_if_node(n)
1.435 +
1.436 + elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):
1.437 + return self.process_logical_node(n)
1.438 +
1.439 + elif isinstance(n, compiler.ast.Not):
1.440 + return self.process_not_node(n)
1.441 +
1.442 + # Exception control-flow tracking.
1.443 +
1.444 + elif isinstance(n, compiler.ast.TryExcept):
1.445 + self.process_try_node(n)
1.446 +
1.447 + elif isinstance(n, compiler.ast.TryFinally):
1.448 + self.process_try_finally_node(n)
1.449 +
1.450 + # Control-flow modification statements.
1.451 +
1.452 + elif isinstance(n, compiler.ast.Break):
1.453 + self.writeline("break;")
1.454 +
1.455 + elif isinstance(n, compiler.ast.Continue):
1.456 + self.writeline("continue;")
1.457 +
1.458 + elif isinstance(n, compiler.ast.Return):
1.459 + expr = self.process_structure_node(n.value)
1.460 + if expr:
1.461 + self.writeline("return %s;" % expr)
1.462 + else:
1.463 + self.writeline("return;")
1.464 +
1.465 + # Invocations.
1.466 +
1.467 + elif isinstance(n, compiler.ast.CallFunc):
1.468 + return self.process_invocation_node(n)
1.469 +
1.470 + elif isinstance(n, compiler.ast.Keyword):
1.471 + return self.process_structure_node(n.expr)
1.472 +
1.473 + # Constant usage.
1.474 +
1.475 + elif isinstance(n, compiler.ast.Const):
1.476 + return self.get_literal_instance(n, n.value.__class__.__name__)
1.477 +
1.478 + elif isinstance(n, compiler.ast.Dict):
1.479 + return self.get_literal_instance(n, "dict")
1.480 +
1.481 + elif isinstance(n, compiler.ast.List):
1.482 + return self.get_literal_instance(n, "list")
1.483 +
1.484 + elif isinstance(n, compiler.ast.Tuple):
1.485 + return self.get_literal_instance(n, "tuple")
1.486 +
1.487 + # All other nodes are processed depth-first.
1.488 +
1.489 + else:
1.490 + self.process_structure(n)
1.491 +
1.492 + def process_assignment_node(self, n, expr):
1.493 +
1.494 + "Process the individual node 'n' to be assigned the contents of 'expr'."
1.495 +
1.496 + # Names and attributes are assigned the entire expression.
1.497 +
1.498 + if isinstance(n, compiler.ast.AssName):
1.499 + name_ref = self.process_name_node(n, self.process_structure_node(expr))
1.500 + self.statement(name_ref)
1.501 +
1.502 + elif isinstance(n, compiler.ast.AssAttr):
1.503 + self.statement(self.process_attribute_access(n, self.process_structure_node(expr)))
1.504 +
1.505 + # Lists and tuples are matched against the expression and their
1.506 + # items assigned to expression items.
1.507 +
1.508 + elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)):
1.509 + self.process_assignment_node_items(n, expr)
1.510 +
1.511 + # Slices and subscripts are permitted within assignment nodes.
1.512 +
1.513 + elif isinstance(n, compiler.ast.Slice):
1.514 + self.statement(self.process_slice_node(n, expr))
1.515 +
1.516 + elif isinstance(n, compiler.ast.Subscript):
1.517 + self.statement(self.process_subscript_node(n, expr))
1.518 +
1.519 + def process_attribute_access(self, n, expr=None):
1.520 +
1.521 + """
1.522 + Process the given attribute access node 'n'.
1.523 +
1.524 + Where a name is provided, a single access should be recorded
1.525 + involving potentially many attributes, thus providing a path to an
1.526 + object. The remaining attributes are then accessed dynamically.
1.527 + The remaining accesses could be deduced and computed, but they would
1.528 + also need to be tested.
1.529 +
1.530 + Where no name is provided, potentially many accesses should be
1.531 + recorded, one per attribute name. These could be used to provide
1.532 + computed accesses, but the accessors would need to be tested in each
1.533 + case.
1.534 + """
1.535 +
1.536 + # Obtain any completed chain and return the reference to it.
1.537 +
1.538 + attr_expr = self.process_attribute_chain(n)
1.539 + if self.have_access_expression(n):
1.540 + return attr_expr
1.541 +
1.542 + # Where the start of the chain of attributes has been reached, process
1.543 + # the complete access.
1.544 +
1.545 + name_ref = attr_expr and attr_expr.is_name() and attr_expr
1.546 + name = name_ref and name_ref.name or None
1.547 +
1.548 + location = self.get_access_location(name)
1.549 + refs = self.get_referenced_attributes(location)
1.550 +
1.551 + # Generate access instructions.
1.552 +
1.553 + subs = {
1.554 + "<expr>" : str(attr_expr),
1.555 + "<assexpr>" : str(expr),
1.556 + "<context>" : "__tmp_context",
1.557 + "<accessor>" : "__tmp_value",
1.558 + }
1.559 +
1.560 + output = []
1.561 +
1.562 + for instruction in self.optimiser.access_instructions[location]:
1.563 + output.append(encode_access_instruction(instruction, subs))
1.564 +
1.565 + out = "(\n%s\n)" % ",\n".join(output)
1.566 +
1.567 + del self.attrs[0]
1.568 + return AttrResult(out, refs)
1.569 +
1.570 + def get_referenced_attributes(self, location):
1.571 +
1.572 + """
1.573 + Convert 'location' to the form used by the deducer and retrieve any
1.574 + identified attribute.
1.575 + """
1.576 +
1.577 + access_location = self.deducer.const_accesses.get(location)
1.578 + refs = []
1.579 + for attrtype, objpath, attr in self.deducer.referenced_attrs[access_location or location]:
1.580 + refs.append(attr)
1.581 + return refs
1.582 +
1.583 + def get_access_location(self, name):
1.584 +
1.585 + """
1.586 + Using the current namespace and the given 'name', return the access
1.587 + location.
1.588 + """
1.589 +
1.590 + path = self.get_path_for_access()
1.591 +
1.592 + # Get the location used by the deducer and optimiser and find any
1.593 + # recorded access.
1.594 +
1.595 + attrnames = ".".join(self.attrs)
1.596 + access_number = self.get_access_number(path, name, attrnames)
1.597 + self.update_access_number(path, name, attrnames)
1.598 + return (path, name, attrnames, access_number)
1.599 +
1.600 + def get_access_number(self, path, name, attrnames):
1.601 + access = name, attrnames
1.602 + if self.attr_accesses.has_key(path) and self.attr_accesses[path].has_key(access):
1.603 + return self.attr_accesses[path][access]
1.604 + else:
1.605 + return 0
1.606 +
1.607 + def update_access_number(self, path, name, attrnames):
1.608 + access = name, attrnames
1.609 + if name:
1.610 + init_item(self.attr_accesses, path, dict)
1.611 + init_item(self.attr_accesses[path], access, lambda: 1)
1.612 +
1.613 + def process_class_node(self, n):
1.614 +
1.615 + "Process the given class node 'n'."
1.616 +
1.617 + self.enter_namespace(n.name)
1.618 +
1.619 + if self.have_object():
1.620 + class_name = self.get_namespace_path()
1.621 + self.write_comment("Class: %s" % class_name)
1.622 +
1.623 + self.process_structure(n)
1.624 +
1.625 + self.exit_namespace()
1.626 +
1.627 + def process_function_body_node(self, n):
1.628 +
1.629 + """
1.630 + Process the given function, lambda, if expression or list comprehension
1.631 + node 'n', generating the body.
1.632 + """
1.633 +
1.634 + function_name = self.get_namespace_path()
1.635 + self.start_function(function_name)
1.636 +
1.637 + # Process the function body.
1.638 +
1.639 + self.invocation_depth = 0
1.640 + self.invocation_argument_depth = 0
1.641 + self.invocation_kw_argument_depth = 0
1.642 +
1.643 + in_conditional = self.in_conditional
1.644 + self.in_conditional = False
1.645 +
1.646 + expr = self.process_structure_node(n.code)
1.647 + if expr:
1.648 + self.writeline("return %s;" % expr)
1.649 +
1.650 + self.in_conditional = in_conditional
1.651 +
1.652 + self.end_function()
1.653 +
1.654 + def process_function_node(self, n):
1.655 +
1.656 + """
1.657 + Process the given function, lambda, if expression or list comprehension
1.658 + node 'n', generating any initialisation statements.
1.659 + """
1.660 +
1.661 + # Where a function is declared conditionally, use a separate name for
1.662 + # the definition, and assign the definition to the stated name.
1.663 +
1.664 + if self.in_conditional or self.in_function:
1.665 + original_name = n.name
1.666 + name = self.get_lambda_name()
1.667 + else:
1.668 + original_name = None
1.669 + name = n.name
1.670 +
1.671 + # Obtain details of the defaults.
1.672 +
1.673 + defaults = self.process_function_defaults(n, name, self.get_object_path(name))
1.674 + if defaults:
1.675 + for default in defaults:
1.676 + self.writeline("%s;" % default)
1.677 +
1.678 + # Where a function is set conditionally, assign the name.
1.679 +
1.680 + if original_name:
1.681 + self.process_assignment_for_function(original_name, name)
1.682 +
1.683 + def process_function_defaults(self, n, name, instance_name):
1.684 +
1.685 + """
1.686 + Process the given function or lambda node 'n', initialising defaults
1.687 + that are dynamically set. The given 'name' indicates the name of the
1.688 + function. The given 'instance_name' indicates the name of any separate
1.689 + instance of the function created to hold the defaults.
1.690 +
1.691 + Return a list of operations setting defaults on a function instance.
1.692 + """
1.693 +
1.694 + function_name = self.get_object_path(name)
1.695 + function_defaults = self.importer.function_defaults.get(function_name)
1.696 + if not function_defaults:
1.697 + return None
1.698 +
1.699 + # Determine whether any unidentified defaults are involved.
1.700 +
1.701 + need_defaults = [argname for argname, default in function_defaults if default.has_kind("<var>")]
1.702 + if not need_defaults:
1.703 + return None
1.704 +
1.705 + # Where defaults are involved but cannot be identified, obtain a new
1.706 + # instance of the lambda and populate the defaults.
1.707 +
1.708 + defaults = []
1.709 +
1.710 + # Join the original defaults with the inspected defaults.
1.711 +
1.712 + original_defaults = [(argname, default) for (argname, default) in compiler.ast.get_defaults(n) if default]
1.713 +
1.714 + for i, (original, inspected) in enumerate(map(None, original_defaults, function_defaults)):
1.715 +
1.716 + # Obtain any reference for the default.
1.717 +
1.718 + if original:
1.719 + argname, default = original
1.720 + name_ref = self.process_structure_node(default)
1.721 + elif inspected:
1.722 + argname, default = inspected
1.723 + name_ref = TrResolvedNameRef(argname, default)
1.724 + else:
1.725 + continue
1.726 +
1.727 + if name_ref:
1.728 + defaults.append("__SETDEFAULT(%s, %s, %s)" % (encode_path(instance_name), i, name_ref))
1.729 +
1.730 + return defaults
1.731 +
1.732 + def process_if_node(self, n):
1.733 +
1.734 + """
1.735 + Process the given "if" node 'n'.
1.736 + """
1.737 +
1.738 + first = True
1.739 + for test, body in n.tests:
1.740 + test_ref = self.process_structure_node(test)
1.741 + self.start_if(first, test_ref)
1.742 +
1.743 + in_conditional = self.in_conditional
1.744 + self.in_conditional = True
1.745 + self.process_structure_node(body)
1.746 + self.in_conditional = in_conditional
1.747 +
1.748 + self.end_if()
1.749 + first = False
1.750 +
1.751 + if n.else_:
1.752 + self.start_else()
1.753 + self.process_structure_node(n.else_)
1.754 + self.end_else()
1.755 +
1.756 + def process_invocation_node(self, n):
1.757 +
1.758 + "Process the given invocation node 'n'."
1.759 +
1.760 + expr = self.process_structure_node(n.node)
1.761 + objpath = expr.get_origin()
1.762 +
1.763 + # Obtain details of the callable.
1.764 +
1.765 + if objpath:
1.766 + parameters = self.importer.function_parameters.get(objpath)
1.767 + else:
1.768 + parameters = None
1.769 +
1.770 + stages = []
1.771 +
1.772 + # Arguments are presented in a temporary frame array at the current
1.773 + # position with any context always being the first argument (although it
1.774 + # may be omitted for invocations where it would be unused).
1.775 +
1.776 + stages.append("__tmp_target = %s" % expr)
1.777 + stages.append("__tmp_args[...] = __tmp_target.context")
1.778 +
1.779 + # Keyword arguments are positioned within the frame.
1.780 +
1.781 + # Defaults are added to the frame where arguments are missing.
1.782 +
1.783 + # The callable member of the callable is then obtained.
1.784 +
1.785 + if self.always_callable:
1.786 + get_fn = "__load_via_object(__tmp_target, %s).fn" % \
1.787 + encode_symbol("pos", "__fn__")
1.788 + else:
1.789 + get_fn = "__check_and_load_via_object(__tmp_target, %s, %s).fn" % (
1.790 + encode_symbol("pos", "__fn__"), encode_symbol("code", "__fn__"))
1.791 +
1.792 + stages.append(get_fn)
1.793 +
1.794 + output = "(\n%s\n)(__tmp_frame)" % ",\n".join(stages)
1.795 +
1.796 + return make_expression("".join(output))
1.797 +
1.798 + def always_callable(self, refs):
1.799 +
1.800 + "Determine whether all 'refs' are callable."
1.801 +
1.802 + for ref in refs:
1.803 + if not ref.static():
1.804 + return False
1.805 + else:
1.806 + origin = ref.final()
1.807 + if not self.importer.get_attribute(origin, "__fn__"):
1.808 + return False
1.809 + return True
1.810 +
1.811 + def need_default_arguments(self, objpath, nargs):
1.812 +
1.813 + """
1.814 + Return whether any default arguments are needed when invoking the object
1.815 + given by 'objpath'.
1.816 + """
1.817 +
1.818 + parameters = self.importer.function_parameters.get(objpath)
1.819 + return nargs < len(parameters)
1.820 +
1.821 + def process_lambda_node(self, n):
1.822 +
1.823 + "Process the given lambda node 'n'."
1.824 +
1.825 + name = self.get_lambda_name()
1.826 + function_name = self.get_object_path(name)
1.827 +
1.828 + defaults = self.process_function_defaults(n, name, "__tmp")
1.829 + if not defaults:
1.830 + return make_expression(encode_path(function_name))
1.831 + else:
1.832 + return make_expression("(__COPY(%s, __tmp), %s)" % (encode_path(function_name), ", ".join(defaults)))
1.833 +
1.834 + def process_logical_node(self, n):
1.835 +
1.836 + "Process the given operator node 'n'."
1.837 +
1.838 + if isinstance(n, compiler.ast.And):
1.839 + op = " && "
1.840 + else:
1.841 + op = " || "
1.842 +
1.843 + # NOTE: This needs to evaluate whether the operands are true or false
1.844 + # NOTE: according to Python rules.
1.845 +
1.846 + results = [("(%s)" % self.process_structure_node(node)) for node in n.nodes]
1.847 + return make_expression("(%s)" % op.join(results))
1.848 +
1.849 + def process_name_node(self, n, expr=None):
1.850 +
1.851 + "Process the given name node 'n' with the optional assignment 'expr'."
1.852 +
1.853 + # Determine whether the name refers to a static external entity.
1.854 +
1.855 + if n.name in predefined_constants:
1.856 + return PredefinedConstantRef(n.name)
1.857 +
1.858 + # Convert literal references.
1.859 +
1.860 + elif n.name.startswith("$L"):
1.861 + literal_name = n.name[len("$L"):]
1.862 + ref = self.importer.get_object("__builtins__.%s" % literal_name)
1.863 + return TrResolvedNameRef(n.name, ref)
1.864 +
1.865 + # Convert operator function names to references.
1.866 +
1.867 + elif n.name.startswith("$op"):
1.868 + opname = n.name[len("$op"):]
1.869 + ref = self.importer.get_object("operator.%s" % opname)
1.870 + return TrResolvedNameRef(n.name, ref)
1.871 +
1.872 + # Get the appropriate name for the name reference, using the same method
1.873 + # as in the inspector.
1.874 +
1.875 + path = self.get_object_path(n.name)
1.876 + ref = self.importer.get_object(path)
1.877 + name = self.get_name_for_tracking(n.name, ref and ref.final())
1.878 +
1.879 + # Get the static identity of the name.
1.880 +
1.881 + ref = self.importer.identify(path)
1.882 +
1.883 + # Obtain any resolved names for non-assignment names.
1.884 +
1.885 + if not expr and not ref and self.in_function:
1.886 + locals = self.importer.function_locals.get(self.get_namespace_path())
1.887 + ref = locals and locals.get(n.name)
1.888 +
1.889 + # Qualified names are used for resolved static references or for
1.890 + # static namespace members. The reference should be configured to return
1.891 + # such names.
1.892 +
1.893 + return TrResolvedNameRef(name, ref, expr=expr)
1.894 +
1.895 + def process_not_node(self, n):
1.896 +
1.897 + "Process the given operator node 'n'."
1.898 +
1.899 + # NOTE: This needs to evaluate whether the operand is true or false
1.900 + # NOTE: according to Python rules.
1.901 +
1.902 + return make_expression("(!(%s))" % n.expr)
1.903 +
1.904 + def process_try_node(self, n):
1.905 +
1.906 + """
1.907 + Process the given "try...except" node 'n'.
1.908 + """
1.909 +
1.910 + # NOTE: Placeholders/macros.
1.911 +
1.912 + self.writeline("TRY")
1.913 + self.writeline("{")
1.914 + self.indent += 1
1.915 + self.process_structure_node(n.body)
1.916 + self.indent -= 1
1.917 + self.writeline("}")
1.918 +
1.919 + for name, var, handler in n.handlers:
1.920 + if name is not None:
1.921 + name_ref = self.process_structure_node(name)
1.922 + self.writeline("EXCEPT(%s)" % name_ref)
1.923 +
1.924 + self.writeline("{")
1.925 + self.indent += 1
1.926 +
1.927 + # Establish the local for the handler.
1.928 + # NOTE: Need to provide the exception value.
1.929 +
1.930 + if var is not None:
1.931 + var_ref = self.process_structure_node(var)
1.932 +
1.933 + if handler is not None:
1.934 + self.process_structure_node(handler)
1.935 +
1.936 + self.indent -= 1
1.937 + self.writeline("}")
1.938 +
1.939 + if n.else_:
1.940 + self.process_structure_node(n.else_)
1.941 +
1.942 + def process_try_finally_node(self, n):
1.943 +
1.944 + """
1.945 + Process the given "try...finally" node 'n'.
1.946 + """
1.947 +
1.948 + # NOTE: Placeholders/macros.
1.949 +
1.950 + self.writeline("TRY")
1.951 + self.writeline("{")
1.952 + self.indent += 1
1.953 + self.process_structure_node(n.body)
1.954 + self.indent -= 1
1.955 + self.writeline("}")
1.956 + self.writeline("FINALLY")
1.957 + self.writeline("{")
1.958 + self.indent += 1
1.959 + self.process_structure_node(n.final)
1.960 + self.indent -= 1
1.961 + self.writeline("}")
1.962 +
1.963 + def process_while_node(self, n):
1.964 +
1.965 + "Process the given while node 'n'."
1.966 +
1.967 + self.writeline("while (1)")
1.968 + self.writeline("{")
1.969 + self.indent += 1
1.970 + test = self.process_structure_node(n.test)
1.971 +
1.972 + # Emit the loop termination condition unless "while <true value>" is
1.973 + # indicated.
1.974 +
1.975 + if not (isinstance(test, PredefinedConstantRef) and test.value):
1.976 +
1.977 + # NOTE: This needs to evaluate whether the operand is true or false
1.978 + # NOTE: according to Python rules.
1.979 +
1.980 + self.writeline("if (!(%s))" % test)
1.981 + self.writeline("{")
1.982 + self.indent += 1
1.983 + if n.else_:
1.984 + self.process_structure_node(n.else_)
1.985 + self.writeline("break;")
1.986 + self.indent -= 1
1.987 + self.writeline("}")
1.988 +
1.989 + in_conditional = self.in_conditional
1.990 + self.in_conditional = True
1.991 + self.process_structure_node(n.body)
1.992 + self.in_conditional = in_conditional
1.993 +
1.994 + self.indent -= 1
1.995 + self.writeline("}")
1.996 +
1.997 + # Output generation.
1.998 +
1.999 + def start_module(self):
1.1000 + print >>self.out, "void __main_%s()" % encode_path(self.name)
1.1001 + print >>self.out, "{"
1.1002 + self.indent += 1
1.1003 + self.emit_invocation_storage(self.name)
1.1004 +
1.1005 + def end_module(self):
1.1006 + self.indent -= 1
1.1007 + self.end_function()
1.1008 +
1.1009 + def start_function(self, name):
1.1010 + print >>self.out, "__attr %s(__attr __args[])" % encode_function_pointer(name)
1.1011 + print >>self.out, "{"
1.1012 + self.indent += 1
1.1013 +
1.1014 + # Obtain local names from parameters.
1.1015 +
1.1016 + parameters = self.importer.function_parameters[name]
1.1017 + names = []
1.1018 + locals = self.importer.function_locals[name].keys()
1.1019 +
1.1020 + for n in locals:
1.1021 +
1.1022 + # Filter out special names and parameters. Note that self is a local
1.1023 + # regardless of whether it originally appeared in the parameters or
1.1024 + # not.
1.1025 +
1.1026 + if n.startswith("$l") or n in parameters or n == "self":
1.1027 + continue
1.1028 + names.append(encode_path(n))
1.1029 +
1.1030 + # Emit required local names.
1.1031 +
1.1032 + if names:
1.1033 + names.sort()
1.1034 + self.writeline("__attr %s;" % ", ".join(names))
1.1035 +
1.1036 + self.emit_invocation_storage(name)
1.1037 +
1.1038 + # Generate any self reference.
1.1039 +
1.1040 + if self.in_method(name):
1.1041 + self.writeline("#define self (__args[0])")
1.1042 +
1.1043 + # Generate aliases for the parameters.
1.1044 +
1.1045 + for i, parameter in enumerate(parameters):
1.1046 + self.writeline("#define %s (__args[%d])" % (encode_path(parameter), i+1))
1.1047 +
1.1048 + def end_function(self):
1.1049 + self.indent -= 1
1.1050 + print >>self.out, "}"
1.1051 +
1.1052 + def start_if(self, first, test_ref):
1.1053 +
1.1054 + # NOTE: This needs to evaluate whether the operand is true or false
1.1055 + # NOTE: according to Python rules.
1.1056 +
1.1057 + self.writeline("%sif (%s)" % (not first and "else " or "", test_ref))
1.1058 + self.writeline("{")
1.1059 + self.indent += 1
1.1060 +
1.1061 + def end_if(self):
1.1062 + self.indent -= 1
1.1063 + self.writeline("}")
1.1064 +
1.1065 + def start_else(self):
1.1066 + self.writeline("else")
1.1067 + self.writeline("{")
1.1068 + self.indent += 1
1.1069 +
1.1070 + def end_else(self):
1.1071 + self.indent -= 1
1.1072 + self.writeline("}")
1.1073 +
1.1074 + def emit_invocation_storage(self, name):
1.1075 +
1.1076 + "Emit invocation temporary storage."
1.1077 +
1.1078 + if self.importer.function_targets.has_key(name):
1.1079 + self.writeline("__attr __tmp_targets[%d];" % self.importer.function_targets[name])
1.1080 +
1.1081 + if self.importer.function_arguments.has_key(name):
1.1082 + self.writeline("__attr __tmp_args[%d];" % self.importer.function_arguments[name])
1.1083 +
1.1084 + def statement(self, expr):
1.1085 + # NOTE: Should never be None.
1.1086 + if not expr:
1.1087 + self.writeline("...;")
1.1088 + s = str(expr)
1.1089 + if s:
1.1090 + self.writeline("%s;" % s)
1.1091 +
1.1092 + def statements(self, results):
1.1093 + for result in results:
1.1094 + self.statement(result)
1.1095 +
1.1096 + def pad(self, extra=0):
1.1097 + return (self.indent + extra) * self.tabstop
1.1098 +
1.1099 + def indenttext(self, s, levels):
1.1100 + return s.replace("\n", "\n%s" % (levels * self.tabstop))
1.1101 +
1.1102 + def writeline(self, s):
1.1103 + print >>self.out, "%s%s" % (self.pad(), self.indenttext(s, self.indent + 1))
1.1104 +
1.1105 + def write_comment(self, s):
1.1106 + self.writeline("/* %s */" % s)
1.1107 +
1.1108 +# vim: tabstop=4 expandtab shiftwidth=4