1.1 --- a/micropython/__init__.py Fri Mar 20 00:32:43 2009 +0100
1.2 +++ b/micropython/__init__.py Sun Apr 05 03:05:59 2009 +0200
1.3 @@ -412,7 +412,7 @@
1.4 # Name records (used to track actual use of names).
1.5 # Include names which may not be explicitly used in programs.
1.6
1.7 - self.names_used = set(["__init__", "__call__"])
1.8 + self.names_used = set(["__init__", "__call__", "__getitem__", "__bool__"])
1.9
1.10 # Status information.
1.11
2.1 --- a/micropython/ast.py Fri Mar 20 00:32:43 2009 +0100
2.2 +++ b/micropython/ast.py Sun Apr 05 03:05:59 2009 +0200
2.3 @@ -23,12 +23,13 @@
2.4 from micropython.common import *
2.5 from micropython.data import *
2.6 from micropython.rsvp import *
2.7 +from micropython.trans import Helper
2.8 import compiler.ast
2.9 from compiler.visitor import ASTVisitor
2.10
2.11 # Program visitors.
2.12
2.13 -class Translation(ASTVisitor):
2.14 +class Translation(ASTVisitor, Helper):
2.15
2.16 "A translated module."
2.17
2.18 @@ -195,265 +196,6 @@
2.19 self.unit.blocks = self.blocks
2.20 return self.blocks
2.21
2.22 - # Allocation-related methods.
2.23 -
2.24 - def make_object(self, cls, n):
2.25 -
2.26 - """
2.27 - Request a new object with the given class 'cls' and with 'n' attributes.
2.28 - """
2.29 -
2.30 - # Load the class in order to locate the instance template.
2.31 -
2.32 - self.new_op(LoadConst(cls))
2.33 -
2.34 - # NOTE: Object headers are one location.
2.35 -
2.36 - self.new_op(MakeObject(n + 1))
2.37 -
2.38 - # Name-related methods.
2.39 -
2.40 - def get_scope(self, name):
2.41 -
2.42 - "Return the scope for the given 'name'."
2.43 -
2.44 - if self.unit.has_key(name):
2.45 - return "local"
2.46 - elif self.module.has_key(name):
2.47 - return "global"
2.48 - else:
2.49 - return "builtins"
2.50 -
2.51 - def load_builtin(self, name, node):
2.52 -
2.53 - "Generate an instruction loading 'name' for the given 'node'."
2.54 -
2.55 - self.new_op(LoadAddress(self.get_builtin(name, node)))
2.56 -
2.57 - def get_builtin_class(self, name, node):
2.58 -
2.59 - "Return the built-in class with the given 'name' for the given 'node'."
2.60 -
2.61 - return self.get_builtin(name, node).get_value()
2.62 -
2.63 - def get_builtin(self, name, node):
2.64 -
2.65 - """
2.66 - Return the built-in module definition for the given 'name', used by the
2.67 - given 'node'.
2.68 - """
2.69 -
2.70 - if self.builtins is not None:
2.71 - try:
2.72 - return self.builtins[name]
2.73 - except KeyError:
2.74 - raise TranslateError(self.module.full_name(), node, "No __builtins__ definition is available for name %r." % name)
2.75 - else:
2.76 - raise TranslateError(self.module.full_name(), node, "No __builtins__ module is available for name %r." % name)
2.77 -
2.78 - # Code feature methods.
2.79 -
2.80 - def new_block(self):
2.81 -
2.82 - "Return a new code block."
2.83 -
2.84 - return Block()
2.85 -
2.86 - def set_block(self, block):
2.87 -
2.88 - "Add the given 'block' to the unit's list of blocks."
2.89 -
2.90 - self.optimiser.reset()
2.91 - self.blocks.append(block)
2.92 -
2.93 - def get_loop_blocks(self):
2.94 - return self.loop_blocks[-1]
2.95 -
2.96 - def add_loop_blocks(self, next_block, exit_block):
2.97 - self.loop_blocks.append((next_block, exit_block))
2.98 -
2.99 - def drop_loop_blocks(self):
2.100 - self.loop_blocks.pop()
2.101 -
2.102 - def get_exception_blocks(self):
2.103 - return self.exception_blocks[-1]
2.104 -
2.105 - def add_exception_blocks(self, handler_block, exit_block):
2.106 - self.exception_blocks.append((handler_block, exit_block))
2.107 -
2.108 - def drop_exception_blocks(self):
2.109 - self.exception_blocks.pop()
2.110 -
2.111 - # Assignment expression values.
2.112 -
2.113 - def record_value(self, immediate=1):
2.114 -
2.115 - """
2.116 - Record the current active value for an assignment. If the optional
2.117 - 'immediate' parameter if set to a false value always allocates new
2.118 - temporary storage to hold the recorded value; otherwise, the
2.119 - value-providing instruction may be replicated in order to provide the
2.120 - active value later on.
2.121 - """
2.122 -
2.123 - if immediate:
2.124 - temp = self.optimiser.optimise_temp_storage()
2.125 - else:
2.126 - temp = self.get_temp()
2.127 - self.expr_temp.append(temp)
2.128 -
2.129 - def discard_value(self):
2.130 -
2.131 - "Discard any temporary storage in use for the current assignment value."
2.132 -
2.133 - self.discard_temp(self.expr_temp.pop())
2.134 -
2.135 - def set_source(self):
2.136 -
2.137 - "Set the source of an assignment using the current assignment value."
2.138 -
2.139 - self.optimiser.set_source(self.expr_temp[-1])
2.140 -
2.141 - # Optimise away constant storage if appropriate.
2.142 -
2.143 - if self.optimiser.optimise_constant_storage():
2.144 - self.remove_op()
2.145 -
2.146 - def is_immediate_user(self, node):
2.147 -
2.148 - """
2.149 - Return whether 'node' is an immediate user of an assignment expression.
2.150 - """
2.151 -
2.152 - return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr))
2.153 -
2.154 - def has_immediate_usage(self, nodes):
2.155 -
2.156 - """
2.157 - Return whether 'nodes' are all immediate users of an assignment expression.
2.158 - """
2.159 -
2.160 - for n in nodes:
2.161 - if not self.is_immediate_user(n):
2.162 - return 0
2.163 - return 1
2.164 -
2.165 - # Temporary storage administration.
2.166 -
2.167 - def get_temp(self):
2.168 -
2.169 - """
2.170 - Add a temporary storage instruction for the current value and return a
2.171 - sequence of access instructions.
2.172 - """
2.173 -
2.174 - position_in_frame = self.reserve_temp()
2.175 - self.new_op(StoreTemp(position_in_frame))
2.176 - return LoadTemp(position_in_frame)
2.177 -
2.178 - def reserve_temp(self, temp_position=None):
2.179 -
2.180 - """
2.181 - Reserve a new temporary storage position, or if the optional
2.182 - 'temp_position' is specified, ensure that this particular position is
2.183 - reserved.
2.184 - """
2.185 -
2.186 - if temp_position is not None:
2.187 - pass
2.188 - elif not self.temp_positions:
2.189 - temp_position = 0
2.190 - else:
2.191 - temp_position = max(self.temp_positions) + 1
2.192 - self.temp_positions.add(temp_position)
2.193 - self.max_temp_position = max(self.max_temp_position, temp_position)
2.194 - return self.unit.all_local_usage + temp_position # position in frame
2.195 -
2.196 - def ensure_temp(self, instruction=None):
2.197 -
2.198 - """
2.199 - Ensure that the 'instruction' is using a reserved temporary storage
2.200 - position.
2.201 - """
2.202 -
2.203 - if isinstance(instruction, LoadTemp):
2.204 - temp_position = instruction.attr - self.unit.all_local_usage
2.205 - self.reserve_temp(temp_position)
2.206 -
2.207 - def discard_temp(self, instruction=None):
2.208 -
2.209 - "Discard any temporary storage position used by 'instruction'."
2.210 -
2.211 - if isinstance(instruction, LoadTemp):
2.212 - temp_position = instruction.attr - self.unit.all_local_usage
2.213 - self.free_temp(temp_position)
2.214 -
2.215 - def free_temp(self, temp_position):
2.216 -
2.217 - "Free the temporary storage position specified by 'temp_position'."
2.218 -
2.219 - if temp_position in self.temp_positions:
2.220 - self.temp_positions.remove(temp_position)
2.221 -
2.222 - def set_frame_usage(self, node, extend):
2.223 -
2.224 - """
2.225 - Ensure that the frame usage for the unit associated with 'node' is set
2.226 - on the 'extend' instruction.
2.227 - """
2.228 -
2.229 - ntemp = self.max_temp_position + 1
2.230 - extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code.
2.231 -
2.232 - # Code writing methods.
2.233 -
2.234 - def new_op(self, op):
2.235 -
2.236 - "Add 'op' to the generated code."
2.237 -
2.238 - # Optimise load operations employed by this instruction.
2.239 -
2.240 - self.optimiser.optimise_load_operations(op)
2.241 - if self.optimiser.optimise_away_no_operations(op):
2.242 - return
2.243 -
2.244 - # Add the operation to the current block.
2.245 -
2.246 - self.blocks[-1].code.append(op)
2.247 - self.optimiser.set_new(op)
2.248 -
2.249 - def remove_op(self):
2.250 -
2.251 - "Remove the last instruction."
2.252 -
2.253 - op = self.blocks[-1].code.pop()
2.254 - self.optimiser.clear_active()
2.255 -
2.256 - def replace_op(self, op):
2.257 -
2.258 - "Replace the last added instruction with 'op'."
2.259 -
2.260 - self.remove_op()
2.261 - self.new_op(op)
2.262 -
2.263 - def replace_active_value(self, op):
2.264 -
2.265 - """
2.266 - Replace the value-providing active instruction with 'op' if appropriate.
2.267 - """
2.268 -
2.269 - self.optimiser.remove_active_value()
2.270 - self.new_op(op)
2.271 -
2.272 - def last_op(self):
2.273 -
2.274 - "Return the last added instruction."
2.275 -
2.276 - try:
2.277 - return self.blocks[-1].code[-1]
2.278 - except IndexError:
2.279 - return None
2.280 -
2.281 # Visitor methods.
2.282
2.283 def default(self, node, *args):
2.284 @@ -462,882 +204,28 @@
2.285 def dispatch(self, node, *args):
2.286 return ASTVisitor.dispatch(self, node, *args)
2.287
2.288 - # Internal helper methods.
2.289 -
2.290 - def _visitAttr(self, node, classes):
2.291 -
2.292 - """
2.293 - Visit the attribute-related 'node', generating instructions based on the
2.294 - given 'classes'.
2.295 - """
2.296 -
2.297 - self.dispatch(node.expr)
2.298 - self._generateAttr(node, node.attrname, classes)
2.299 -
2.300 - def _generateAttr(self, node, attrname, classes):
2.301 -
2.302 - """
2.303 - Generate code for the access to 'attrname' using the given 'classes'.
2.304 - """
2.305 -
2.306 - AddressInstruction, AddressContextInstruction, AddressContextCondInstruction, \
2.307 - AttrInstruction, AttrIndexInstruction, AttrIndexContextInstruction, \
2.308 - AttrIndexContextCondInstruction = classes
2.309 -
2.310 - # Where the last operation (defining the attribute owner) yields a
2.311 - # constant...
2.312 -
2.313 - target_name = self.optimiser.optimise_constant_accessor()
2.314 -
2.315 - # Only try and discover the position if the target can be resolved.
2.316 - # Since instances cannot be constants, this involves classes and
2.317 - # modules.
2.318 -
2.319 - if target_name is not None:
2.320 -
2.321 - # Access the object table to get the attribute position.
2.322 -
2.323 - try:
2.324 - table_entry = self.objtable.table[target_name]
2.325 - except KeyError:
2.326 - raise TranslateError(self.module.full_name(), node,
2.327 - "No object entry exists for target %r." % target_name)
2.328 -
2.329 - try:
2.330 - pos = table_entry[attrname]
2.331 - except KeyError:
2.332 - raise TranslateError(self.module.full_name(), node,
2.333 - "No attribute entry exists for name %r in target %r." % (attrname, target_name))
2.334 -
2.335 - # Produce a suitable instruction.
2.336 -
2.337 - if AddressInstruction is not None:
2.338 - self.replace_active_value(AddressInstruction(pos))
2.339 - else:
2.340 - raise TranslateError(self.module.full_name(), node,
2.341 - "Storing of class or module attribute %r via an object is not permitted." % attrname)
2.342 -
2.343 - return
2.344 -
2.345 - # Where the last operation involves the special 'self' name, check to
2.346 - # see if the attribute is acceptably positioned and produce a direct
2.347 - # access to the attribute.
2.348 -
2.349 - # This is the only reliable way of detecting instance accesses at
2.350 - # compile-time since in general, objects could be classes or modules,
2.351 - # but 'self' should only refer to instances.
2.352 -
2.353 - elif self.optimiser.optimise_self_access(self.unit, attrname):
2.354 -
2.355 - # Either generate an instruction operating on an instance attribute.
2.356 -
2.357 - try:
2.358 - attr = self.unit.parent.instance_attributes()[attrname]
2.359 - self.new_op(AttrInstruction(attr))
2.360 - return
2.361 -
2.362 - # Or generate an instruction operating on a class attribute.
2.363 - # NOTE: Any simple instruction providing self is not removed.
2.364 -
2.365 - except KeyError:
2.366 -
2.367 - try:
2.368 - attr = self.unit.parent.all_attributes()[attrname]
2.369 -
2.370 - # Switch the context if the class attribute is compatible with
2.371 - # the instance.
2.372 -
2.373 - if attr.defined_within_hierarchy():
2.374 -
2.375 - # Only permit loading (not storing) of class attributes via self.
2.376 -
2.377 - if AddressContextInstruction is not None:
2.378 - self.new_op(AddressContextInstruction(attr))
2.379 - else:
2.380 - raise TranslateError(self.module.full_name(), node,
2.381 - "Storing of class attribute %r via self not permitted." % attrname)
2.382 -
2.383 - # Preserve the context if the class attribute comes from an
2.384 - # incompatible class.
2.385 -
2.386 - elif attr.defined_outside_hierarchy():
2.387 -
2.388 - # Only permit loading (not storing) of class attributes via self.
2.389 -
2.390 - if AddressInstruction is not None:
2.391 - self.new_op(AddressInstruction(attr))
2.392 - else:
2.393 - raise TranslateError(self.module.full_name(), node,
2.394 - "Storing of class attribute %r via self not permitted." % attrname)
2.395 -
2.396 - # Otherwise, test for a suitable context at run-time.
2.397 -
2.398 - else:
2.399 -
2.400 - # Only permit loading (not storing) of class attributes via self.
2.401 -
2.402 - if AddressContextCondInstruction is not None:
2.403 - self.new_op(AddressContextCondInstruction(attr))
2.404 - else:
2.405 - raise TranslateError(self.module.full_name(), node,
2.406 - "Storing of class attribute %r via self not permitted." % attrname)
2.407 -
2.408 - return
2.409 -
2.410 - # Or delegate the attribute access to a general instruction
2.411 - # since the kind of attribute cannot be deduced.
2.412 -
2.413 - except KeyError:
2.414 - pass
2.415 -
2.416 - # Otherwise, perform a normal operation.
2.417 -
2.418 - try:
2.419 - index = self.objtable.get_index(attrname)
2.420 -
2.421 - except self.objtable.TableError:
2.422 - raise TranslateError(self.module.full_name(), node,
2.423 - "No attribute entry exists for name %r." % attrname)
2.424 -
2.425 - # NOTE: Test for class vs. instance attributes, generating
2.426 - # NOTE: context-related instructions.
2.427 -
2.428 - if AttrIndexContextCondInstruction is not None:
2.429 - self.new_op(AttrIndexContextCondInstruction(index))
2.430 -
2.431 - # Store instructions do not need to consider context modifications.
2.432 -
2.433 - else:
2.434 - self.new_op(AttrIndexInstruction(index))
2.435 -
2.436 - # Invocations involve the following:
2.437 - #
2.438 - # 1. Reservation of a frame for the arguments
2.439 - # 2. Identification of the target which is then held in temporary storage
2.440 - # 3. Optional inclusion of a context (important for methods)
2.441 - # 4. Preparation of the argument frame
2.442 - # 5. Invocation of the target
2.443 - # 6. Discarding of the frame
2.444 - #
2.445 - # In order to support nested invocations - such as a(b(c)) - use of the
2.446 - # temporary storage is essential.
2.447 -
2.448 - def _startCallFunc(self):
2.449 -
2.450 - "Record the location of the invocation."
2.451 -
2.452 - op = MakeFrame()
2.453 - self.new_op(op) # records the start of the frame
2.454 - self.frame_makers.append(op)
2.455 -
2.456 - def _generateCallFunc(self, args, node):
2.457 -
2.458 - """
2.459 - Support a generic function invocation using the given 'args', occurring
2.460 - on the given 'node', where the expression providing the invocation
2.461 - target has just been generated.
2.462 -
2.463 - In other situations, the invocation is much simpler and does not need to
2.464 - handle the full flexibility of a typical Python invocation. Internal
2.465 - invocations, such as those employed by operators and certain
2.466 - control-flow mechanisms, use predetermined arguments and arguably do not
2.467 - need to support the same things as the more general invocations.
2.468 - """
2.469 -
2.470 - target, context, temp = self._generateCallFuncContext()
2.471 - self._generateCallFuncArgs(target, context, temp, args, node)
2.472 - return temp, target
2.473 -
2.474 - def _generateCallFuncContext(self):
2.475 -
2.476 - """
2.477 - Produce code which loads and checks the context of the current
2.478 - invocation, the instructions for whose target have already been
2.479 - produced, returning a list of instructions which reference the
2.480 - invocation target.
2.481 - """
2.482 -
2.483 - t = self.optimiser.optimise_known_target()
2.484 - if t:
2.485 - target, context = t
2.486 - if isinstance(target, Instance): # lambda object
2.487 - target, context = None, None
2.488 - else:
2.489 - target, context = None, None
2.490 -
2.491 - # Store the target in temporary storage for subsequent referencing.
2.492 - # NOTE: This may not be appropriate for class invocations
2.493 - # NOTE: (instantiation).
2.494 -
2.495 - temp = self.optimiser.optimise_temp_storage()
2.496 -
2.497 - # Where a target or context are not known or where an instance is known
2.498 - # to be the context, load the context.
2.499 -
2.500 - if target is None or isinstance(context, Instance):
2.501 - self.new_op(temp)
2.502 - self.new_op(LoadContext())
2.503 - self.new_op(StoreFrame(0))
2.504 -
2.505 - # For known instantiations, provide a new object as the first argument
2.506 - # to the __init__ method.
2.507 -
2.508 - elif isinstance(target, Class):
2.509 - self.make_object(target, len(target.instance_attributes()))
2.510 - self.new_op(StoreFrame(0))
2.511 -
2.512 - # Otherwise omit the context.
2.513 -
2.514 - else:
2.515 - pass # NOTE: Class methods should be supported.
2.516 -
2.517 - return target, context, temp
2.518 -
2.519 - def _generateCallFuncArgs(self, target, context, temp, args, node):
2.520 -
2.521 - """
2.522 - Given invocation 'target' and 'context' information, the 'temp'
2.523 - reference to the target, a list of nodes representing the 'args'
2.524 - (arguments), generate instructions which load the arguments for the
2.525 - invocation defined by the given 'node'.
2.526 - """
2.527 -
2.528 - # Evaluate the arguments.
2.529 -
2.530 - employed_positions = set()
2.531 - employed_keywords = set()
2.532 - extra_keywords = []
2.533 -
2.534 - # Find keyword arguments in advance in order to help resolve targets.
2.535 -
2.536 - for arg in args:
2.537 - if isinstance(arg, compiler.ast.Keyword):
2.538 - employed_keywords.add(arg.name)
2.539 -
2.540 - possible_targets = self.paramtable.all_possible_objects(employed_keywords)
2.541 -
2.542 - # Note the presence of the context in the frame where appropriate.
2.543 -
2.544 - if target is None or isinstance(context, Instance):
2.545 - ncontext = 1
2.546 - expect_context = 0
2.547 -
2.548 - # Handle calls to classes.
2.549 -
2.550 - elif isinstance(target, Class):
2.551 - ncontext = 1
2.552 - expect_context = 0
2.553 - target = target.get_init_method()
2.554 -
2.555 - # Method calls via classes.
2.556 -
2.557 - elif isinstance(context, Class):
2.558 - ncontext = 0
2.559 - expect_context = 1
2.560 -
2.561 - # Function calls.
2.562 -
2.563 - else:
2.564 - ncontext = 0
2.565 - expect_context = 0
2.566 -
2.567 - first = 1
2.568 - frame_pos = ncontext
2.569 - max_keyword_pos = -1
2.570 -
2.571 - for arg in args:
2.572 -
2.573 - # Handle positional and keyword arguments separately.
2.574 -
2.575 - if isinstance(arg, compiler.ast.Keyword):
2.576 -
2.577 - # Optimise where the target is known now.
2.578 -
2.579 - if target is not None:
2.580 -
2.581 - # Find the parameter table entry for the target.
2.582 -
2.583 - target_name = target.full_name()
2.584 -
2.585 - # Look for a callable with the precise target name.
2.586 -
2.587 - table_entry = self.paramtable.table[target_name]
2.588 -
2.589 - # Look the name up in the parameter table entry.
2.590 -
2.591 - try:
2.592 - pos = table_entry[arg.name]
2.593 -
2.594 - # Where no position is found, this could be an extra keyword
2.595 - # argument.
2.596 -
2.597 - except KeyError:
2.598 - extra_keywords.append(arg)
2.599 - continue
2.600 -
2.601 - # Test for illegal conditions.
2.602 -
2.603 - if pos in employed_positions:
2.604 - raise TranslateError(self.module.full_name(), node,
2.605 - "Keyword argument %r overwrites parameter %r." % (arg.name, pos))
2.606 -
2.607 - employed_positions.add(pos)
2.608 -
2.609 - # Generate code for the keyword and the positioning
2.610 - # operation.
2.611 -
2.612 - self.dispatch(arg.expr)
2.613 - self.new_op(StoreFrame(pos))
2.614 -
2.615 - # Otherwise, generate the code needed to obtain the details of
2.616 - # the parameter location.
2.617 -
2.618 - else:
2.619 -
2.620 - # Combine the target details with the name to get the location.
2.621 - # See the access method on the List class.
2.622 -
2.623 - try:
2.624 - paramindex = self.paramtable.get_index(arg.name)
2.625 -
2.626 - # Where no position is found, this could be an extra keyword
2.627 - # argument.
2.628 -
2.629 - except self.paramtable.TableError:
2.630 - extra_keywords.append(arg)
2.631 - continue
2.632 -
2.633 - # Generate code for the keyword and the positioning
2.634 - # operation. Get the value as the source of the assignment.
2.635 -
2.636 - self.dispatch(arg.expr)
2.637 - self.record_value()
2.638 -
2.639 - # Store the source value using the callable's parameter
2.640 - # table information.
2.641 -
2.642 - self.new_op(temp)
2.643 - self.new_op(StoreFrameIndex(paramindex))
2.644 -
2.645 - self.set_source()
2.646 - self.discard_value()
2.647 -
2.648 - # Record the highest possible frame position for this argument.
2.649 -
2.650 - max_keyword_pos = max(max_keyword_pos, max(self.paramtable.all_attribute_positions(arg.name)))
2.651 -
2.652 - else:
2.653 - self.dispatch(arg)
2.654 - self.new_op(StoreFrame(frame_pos))
2.655 -
2.656 - employed_positions.add(frame_pos)
2.657 -
2.658 - # Check to see if the first argument is appropriate (compatible with
2.659 - # the target where methods are being invoked via classes).
2.660 -
2.661 - if first and expect_context:
2.662 -
2.663 - # Drop any test if the target and the context are known.
2.664 -
2.665 - if not self.optimiser.have_correct_self_for_target(context, self.unit):
2.666 -
2.667 - continue_block = self.new_block()
2.668 -
2.669 - self.new_op(CheckSelf())
2.670 - self.optimiser.set_source(temp)
2.671 - self.new_op(JumpIfTrue(continue_block))
2.672 -
2.673 - # Where the context is inappropriate, drop the incomplete frame and
2.674 - # raise an exception.
2.675 -
2.676 - self.new_op(DropFrame())
2.677 - self.new_op(LoadResult())
2.678 -
2.679 - self.load_builtin("TypeError", node)
2.680 - self.new_op(StoreException())
2.681 - self.new_op(RaiseException())
2.682 -
2.683 - self.set_block(continue_block)
2.684 -
2.685 - first = 0
2.686 - frame_pos += 1
2.687 -
2.688 - # NOTE: Extra keywords are not supported.
2.689 - # NOTE: Somehow, the above needs to be combined with * arguments.
2.690 -
2.691 - if extra_keywords:
2.692 - print "Warning: extra keyword argument(s) %s not handled." % ", ".join([arg.name for arg in extra_keywords])
2.693 -
2.694 - # Either test for a complete set of arguments.
2.695 -
2.696 - if target is not None:
2.697 -
2.698 - # Make sure that enough arguments have been given.
2.699 -
2.700 - nargs_max = len(target.positional_names)
2.701 - ndefaults = len(target.defaults)
2.702 - nargs_min = nargs_max - ndefaults
2.703 -
2.704 - for i in range(ncontext, nargs_min):
2.705 - if i not in employed_positions:
2.706 - raise TranslateError(self.module.full_name(), node,
2.707 - "Argument %r not supplied for %r: need at least %d argument(s)." % (i+1, target.name, nargs_min))
2.708 -
2.709 - nargs = frame_pos
2.710 -
2.711 - if nargs > nargs_max and not target.has_star and not target.has_dstar:
2.712 - raise TranslateError(self.module.full_name(), node,
2.713 - "Too many arguments for %r: need at most %d argument(s)." % (target.name, nargs_max))
2.714 -
2.715 - # Where defaults are involved, put them into the frame.
2.716 -
2.717 - self._generateCallFuncDefaultArgs(target, temp, nargs_min, nargs_max, employed_positions)
2.718 -
2.719 - # Set the frame size.
2.720 -
2.721 - self._endCallFuncArgs(nargs_max)
2.722 -
2.723 - # Or generate instructions to do this at run-time.
2.724 -
2.725 - else:
2.726 - max_pos = max(max(employed_positions or [-1]), max_keyword_pos, frame_pos - 1)
2.727 -
2.728 - # Only check non-empty frames (using the callable's details).
2.729 -
2.730 - if employed_positions or max_pos >= 0:
2.731 - self.new_op(temp)
2.732 - self.new_op(CheckFrame(max_pos + 1))
2.733 -
2.734 - # Set the frame size.
2.735 -
2.736 - self._endCallFuncArgs(max_pos + 1)
2.737 -
2.738 - def _generateCallFuncDefaultArgs(self, target, temp, nargs_min, nargs_max, employed_positions):
2.739 -
2.740 - """
2.741 - For the given 'target' and 'temp' reference to the target, generate
2.742 - default arguments for those positions in the range 'nargs_min'...
2.743 - 'nargs_max' which are not present in the 'employed_positions'
2.744 - collection.
2.745 - """
2.746 -
2.747 - # Where a lambda is involved, construct a dynamic object to hold the
2.748 - # defaults.
2.749 -
2.750 - dynamic = target.name is None
2.751 -
2.752 - # Here, we use negative index values to visit the right hand end of
2.753 - # the defaults list.
2.754 -
2.755 - for pos in range(nargs_min, nargs_max):
2.756 - if pos not in employed_positions:
2.757 - if dynamic:
2.758 - self.new_op(temp)
2.759 - self.new_op(LoadAttr(target.default_attrs[pos - nargs_min]))
2.760 - else:
2.761 - self.new_op(LoadAddress(target.default_attrs[pos - nargs_min]))
2.762 - self.new_op(StoreFrame(pos))
2.763 -
2.764 - def _doCallFunc(self, instruction, target=None):
2.765 -
2.766 - "Make the invocation."
2.767 -
2.768 - if isinstance(target, Class):
2.769 - self.new_op(LoadConst(target.get_init_method()))
2.770 - else:
2.771 - self.new_op(instruction)
2.772 - self.new_op(LoadCallable())
2.773 - self.new_op(JumpWithFrame())
2.774 -
2.775 - def _endCallFuncArgs(self, nargs):
2.776 -
2.777 - "Set the frame size."
2.778 -
2.779 - self.frame_makers[-1].attr = nargs
2.780 - self.frame_makers.pop()
2.781 -
2.782 - def _endCallFunc(self, instruction=None, target=None, load_result=1):
2.783 -
2.784 - "Finish the invocation and tidy up afterwards."
2.785 -
2.786 - if isinstance(target, Class):
2.787 - self.new_op(LoadName(target.get_init_method().all_locals()["self"])) # load the context in the invocation frame
2.788 - self.new_op(StoreResult())
2.789 - self.new_op(DropFrame())
2.790 - if load_result:
2.791 - self.new_op(LoadResult())
2.792 -
2.793 - # Discard any temporary storage instructions.
2.794 -
2.795 - if instruction is not None:
2.796 - self.discard_temp(instruction)
2.797 -
2.798 - def _generateFunctionDefaults(self, function):
2.799 -
2.800 - """
2.801 - Generate the default initialisation code for 'function', returning
2.802 - a temporary storage reference if a dynamic object was created for the
2.803 - function.
2.804 - """
2.805 -
2.806 - attr_to_default = zip(function.default_attrs, function.defaults)
2.807 - if not attr_to_default:
2.808 - return None
2.809 -
2.810 - # Where a lambda is involved, construct a dynamic object to hold the
2.811 - # defaults.
2.812 -
2.813 - dynamic = function.name is None
2.814 -
2.815 - if dynamic:
2.816 - self.make_object(self.get_builtin_class("function", function), len(attr_to_default))
2.817 - temp = self.get_temp()
2.818 -
2.819 - for attr, default in attr_to_default:
2.820 - self.dispatch(default)
2.821 -
2.822 - self.record_value()
2.823 - if dynamic:
2.824 - self.new_op(temp)
2.825 - self.new_op(StoreAttr(attr))
2.826 - else:
2.827 - self.new_op(StoreAddress(attr))
2.828 - self.set_source()
2.829 - self.discard_value()
2.830 -
2.831 - if dynamic:
2.832 - return temp
2.833 - else:
2.834 - return None
2.835 -
2.836 - def _visitName(self, node, classes):
2.837 -
2.838 - """
2.839 - Visit the name-related 'node', generating instructions based on the
2.840 - given 'classes'.
2.841 - """
2.842 -
2.843 - name = node.name
2.844 - scope = self.get_scope(name)
2.845 - #print self.module.name, node.lineno, name, scope
2.846 - self._generateName(name, scope, classes, node)
2.847 -
2.848 - def _generateName(self, name, scope, classes, node):
2.849 -
2.850 - """
2.851 - Generate code for the access to 'name' in 'scope' using the given
2.852 - 'classes', and using the given 'node' as the source of the access.
2.853 - """
2.854 -
2.855 - NameInstruction, AddressInstruction = classes
2.856 -
2.857 - if scope == "local":
2.858 - unit = self.unit
2.859 - if isinstance(unit, Function):
2.860 - self.new_op(NameInstruction(unit.all_locals()[name]))
2.861 - elif isinstance(unit, Class):
2.862 - self.new_op(AddressInstruction(unit.all_class_attributes()[name]))
2.863 - elif isinstance(unit, Module):
2.864 - self.new_op(AddressInstruction(unit.module_attributes()[name]))
2.865 - else:
2.866 - raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r." % (unit, name))
2.867 -
2.868 - elif scope == "global":
2.869 - globals = self.module.module_attributes()
2.870 - if globals.has_key(name):
2.871 - self.new_op(AddressInstruction(globals[name]))
2.872 - else:
2.873 - raise TranslateError(self.module.full_name(), node, "Module %r has no attribute %r." % (self.module, name))
2.874 -
2.875 - else:
2.876 - self.new_op(AddressInstruction(self.get_builtin(name, node)))
2.877 -
2.878 - def _visitUnary(self, node):
2.879 -
2.880 - """
2.881 - _t = node.expr
2.882 - try:
2.883 - _result = _t.__pos__()
2.884 - except AttributeError:
2.885 - raise TypeError
2.886 - """
2.887 -
2.888 - method = unary_methods[node.__class__.__name__]
2.889 -
2.890 - type_error_block = self.new_block()
2.891 - end_block = self.new_block()
2.892 -
2.893 - # Evaluate and store the operand in temporary storage.
2.894 -
2.895 - self.dispatch(node.expr)
2.896 - temp = self.optimiser.optimise_temp_storage()
2.897 -
2.898 - self.new_op(temp)
2.899 -
2.900 - # Get the method on temp.
2.901 -
2.902 - self._generateAttr(node, method, self.attribute_load_instructions)
2.903 - temp_method = self.optimiser.optimise_temp_storage()
2.904 -
2.905 - self._handleAttributeError(node, type_error_block)
2.906 -
2.907 - # Add arguments.
2.908 - # NOTE: No support for defaults.
2.909 -
2.910 - self._startCallFunc()
2.911 - self.new_op(temp) # Explicit context as first argument.
2.912 - self.new_op(StoreFrame(0))
2.913 - self._endCallFuncArgs(1)
2.914 - self._doCallFunc(temp_method)
2.915 - self._endCallFunc(temp_method)
2.916 - self.new_op(Jump(end_block))
2.917 -
2.918 - # Store the result.
2.919 -
2.920 - temp_out = self.get_temp()
2.921 -
2.922 - # Raise a TypeError.
2.923 -
2.924 - self.set_block(type_error_block)
2.925 - self.load_builtin("TypeError", node)
2.926 - self.new_op(StoreException())
2.927 - self.new_op(RaiseException())
2.928 -
2.929 - self.set_block(end_block)
2.930 -
2.931 - # Produce the result.
2.932 -
2.933 - self.new_op(temp_out)
2.934 -
2.935 - # Compilation duties...
2.936 -
2.937 - self.discard_temp(temp)
2.938 - self.discard_temp(temp_out)
2.939 -
2.940 - def _visitBinary(self, node):
2.941 -
2.942 - """
2.943 - _t1 = node.left
2.944 - _t2 = node.right
2.945 - try:
2.946 - _result = _t1.__add__(_t2)
2.947 - if _result is NotImplemented:
2.948 - raise AttributeError
2.949 - except AttributeError:
2.950 - try:
2.951 - _result = _t2.__radd__(_t1)
2.952 - if _result is NotImplemented:
2.953 - raise AttributeError
2.954 - except AttributeError:
2.955 - raise TypeError
2.956 - """
2.957 -
2.958 - left_method, right_method = binary_methods[node.__class__.__name__]
2.959 -
2.960 - # Evaluate and store the left operand in temporary storage.
2.961 -
2.962 - self.dispatch(node.left)
2.963 - temp1 = self.optimiser.optimise_temp_storage()
2.964 -
2.965 - # Evaluate and store the right operand in temporary storage.
2.966 -
2.967 - self.dispatch(node.right)
2.968 - temp2 = self.optimiser.optimise_temp_storage()
2.969 -
2.970 - temp_out = self._generateBinary(node, temp1, temp2, left_method, right_method)
2.971 -
2.972 - # Produce the result.
2.973 -
2.974 - self.new_op(temp_out)
2.975 -
2.976 - # Compilation duties...
2.977 -
2.978 - self.discard_temp(temp1)
2.979 - self.discard_temp(temp2)
2.980 - self.discard_temp(temp_out)
2.981 -
2.982 - def _generateBinary(self, node, temp1, temp2, left_method, right_method):
2.983 -
2.984 - """
2.985 - For the given 'node', generate the binary operator pattern for the
2.986 - operands 'temp1' and 'temp2', employing 'left_method' and 'right_method'
2.987 - as defined for binary operators, but also used in comparisons (for which
2.988 - this method is provided).
2.989 -
2.990 - A temporary storage reference is returned from this method.
2.991 - """
2.992 -
2.993 - right_block = self.new_block()
2.994 - type_error_block = self.new_block()
2.995 - end_block = self.new_block()
2.996 -
2.997 - # Left method.
2.998 -
2.999 - temp_out = self._generateOpMethod(node, temp1, temp2, left_method, right_block, end_block)
2.1000 - self.discard_temp(temp_out) # NOTE: Will re-use the same storage.
2.1001 -
2.1002 - # Right method.
2.1003 -
2.1004 - self.set_block(right_block)
2.1005 - temp_out = self._generateOpMethod(node, temp2, temp1, right_method, type_error_block, end_block)
2.1006 -
2.1007 - # Raise a TypeError.
2.1008 -
2.1009 - self.set_block(type_error_block)
2.1010 - self.load_builtin("TypeError", node)
2.1011 - self.new_op(StoreException())
2.1012 - self.new_op(RaiseException())
2.1013 -
2.1014 - self.set_block(end_block)
2.1015 - return temp_out
2.1016 -
2.1017 - def _generateOpMethod(self, node, temp1, temp2, method_name, next_method_block, end_block):
2.1018 -
2.1019 - """
2.1020 - For the given 'node', generate the operator method invocation using the
2.1021 - operands 'temp1' and 'temp2', employing the given 'method_name', and
2.1022 - jumping appropriately to 'next_method_block' where a NotImplemented
2.1023 - result is returned, or to 'end_block' if the method call was successful.
2.1024 -
2.1025 - A temporary storage reference is returned from this method.
2.1026 - """
2.1027 -
2.1028 - end_attempt_block = self.new_block()
2.1029 -
2.1030 - self.new_op(temp1)
2.1031 -
2.1032 - # Get method on temp1.
2.1033 -
2.1034 - self._generateAttr(node, method_name, self.attribute_load_instructions)
2.1035 - temp_method = self.optimiser.optimise_temp_storage()
2.1036 -
2.1037 - self._handleAttributeError(node, end_attempt_block)
2.1038 -
2.1039 - # Add arguments.
2.1040 - # NOTE: No support for defaults.
2.1041 -
2.1042 - self._startCallFunc()
2.1043 - self.new_op(temp1)
2.1044 - self.new_op(StoreFrame(0))
2.1045 - self.new_op(temp2)
2.1046 - self.new_op(StoreFrame(1))
2.1047 - self._endCallFuncArgs(2)
2.1048 - self._doCallFunc(temp_method)
2.1049 - self._endCallFunc(temp_method)
2.1050 -
2.1051 - # Store the result.
2.1052 -
2.1053 - temp_out = self.get_temp()
2.1054 -
2.1055 - # Test for NotImplemented.
2.1056 - # Don't actually raise an exception.
2.1057 -
2.1058 - self.new_op(TestIdentityAddress(self.importer.get_predefined_constant("NotImplemented")))
2.1059 - self.new_op(JumpIfTrue(next_method_block))
2.1060 - self.new_op(Jump(end_block))
2.1061 -
2.1062 - # End method attempt.
2.1063 -
2.1064 - self.set_block(end_attempt_block)
2.1065 - return temp_out
2.1066 -
2.1067 - def _handleAttributeError(self, node, end_call_block):
2.1068 -
2.1069 - """
2.1070 - Add exception handling to the method acquisition instructions where the
2.1071 - attribute access cannot be resolved at compile-time.
2.1072 - """
2.1073 -
2.1074 - if not self.optimiser.optimise_known_target():
2.1075 - self.load_builtin("AttributeError", node)
2.1076 - self.new_op(CheckException())
2.1077 - self.new_op(JumpIfTrue(end_call_block))
2.1078 -
2.1079 - def _generateSequence(self, sequence_type, node):
2.1080 -
2.1081 - "Make a sequence of 'sequence_type' for the given program 'node'."
2.1082 -
2.1083 - self.make_object(self.get_builtin_class(sequence_type, node), len(node.nodes))
2.1084 - temp = self.get_temp()
2.1085 -
2.1086 - for i, n in enumerate(node.nodes):
2.1087 - self.dispatch(n)
2.1088 - self.record_value()
2.1089 - self.new_op(temp)
2.1090 - self.new_op(StoreAttr(Attr(i, None, None)))
2.1091 - self.set_source()
2.1092 - self.discard_value()
2.1093 -
2.1094 - self.new_op(temp)
2.1095 - self.discard_temp(temp)
2.1096 -
2.1097 - def _generateTestBoolean(self, node, temp):
2.1098 -
2.1099 - """
2.1100 - Generate a test of the boolean status of the current value for the given
2.1101 - program 'node'.
2.1102 - """
2.1103 -
2.1104 - # Get method on temp.
2.1105 - # NOTE: Using __bool__ instead of __nonzero__.
2.1106 -
2.1107 - self._generateAttr(node, "__bool__", self.attribute_load_instructions)
2.1108 - temp_method = self.optimiser.optimise_temp_storage()
2.1109 -
2.1110 - self._startCallFunc()
2.1111 - self.new_op(temp)
2.1112 - self.new_op(StoreFrame(0))
2.1113 - self._endCallFuncArgs(1)
2.1114 - self._doCallFunc(temp_method)
2.1115 - self._endCallFunc(temp_method)
2.1116 -
2.1117 - self.discard_temp(temp_method)
2.1118 -
2.1119 - # Convert result to boolean (a StoreBoolean operation).
2.1120 -
2.1121 - self.new_op(TestIdentityAddress(self.get_builtin("True", node)))
2.1122 -
2.1123 - def _generateLoadBoolean(self, node):
2.1124 -
2.1125 - """
2.1126 - Generate instructions to load the appropriate value given the current
2.1127 - boolean status.
2.1128 - """
2.1129 -
2.1130 - true_block = self.new_block()
2.1131 - end_block = self.new_block()
2.1132 -
2.1133 - self.new_op(JumpIfTrue(true_block))
2.1134 - self.load_builtin("False", node)
2.1135 - self.new_op(Jump(end_block))
2.1136 -
2.1137 - self.set_block(true_block)
2.1138 - self.load_builtin("True", node)
2.1139 -
2.1140 - self.set_block(end_block)
2.1141 -
2.1142 # Concrete visitor methods.
2.1143
2.1144 # Binary operators.
2.1145
2.1146 - visitAdd = _visitBinary
2.1147 - visitBitand = _visitBinary
2.1148 - visitBitor = _visitBinary
2.1149 - visitBitxor = _visitBinary
2.1150 - visitDiv = _visitBinary
2.1151 - visitFloorDiv = _visitBinary
2.1152 - visitLeftShift = _visitBinary
2.1153 - visitMod = _visitBinary
2.1154 - visitMul = _visitBinary
2.1155 - visitPower = _visitBinary
2.1156 - visitRightShift = _visitBinary
2.1157 - visitSub = _visitBinary
2.1158 + visitAdd = Helper._visitBinary
2.1159 + visitBitand = Helper._visitBinary
2.1160 + visitBitor = Helper._visitBinary
2.1161 + visitBitxor = Helper._visitBinary
2.1162 + visitDiv = Helper._visitBinary
2.1163 + visitFloorDiv = Helper._visitBinary
2.1164 + visitLeftShift = Helper._visitBinary
2.1165 + visitMod = Helper._visitBinary
2.1166 + visitMul = Helper._visitBinary
2.1167 + visitPower = Helper._visitBinary
2.1168 + visitRightShift = Helper._visitBinary
2.1169 + visitSub = Helper._visitBinary
2.1170
2.1171 # Unary operators.
2.1172
2.1173 - visitInvert = _visitUnary
2.1174 - visitUnaryAdd = _visitUnary
2.1175 - visitUnarySub = _visitUnary
2.1176 + visitInvert = Helper._visitUnary
2.1177 + visitUnaryAdd = Helper._visitUnary
2.1178 + visitUnarySub = Helper._visitUnary
2.1179
2.1180 # Logical operators.
2.1181
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/micropython/trans.py Sun Apr 05 03:05:59 2009 +0200
3.3 @@ -0,0 +1,1148 @@
3.4 +#!/usr/bin/env python
3.5 +
3.6 +"""
3.7 +Translate the AST of a Python program into a more interpretable representation.
3.8 +
3.9 +Copyright (C) 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk>
3.10 +
3.11 +This program is free software; you can redistribute it and/or modify it under
3.12 +the terms of the GNU General Public License as published by the Free Software
3.13 +Foundation; either version 3 of the License, or (at your option) any later
3.14 +version.
3.15 +
3.16 +This program is distributed in the hope that it will be useful, but WITHOUT
3.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
3.19 +details.
3.20 +
3.21 +You should have received a copy of the GNU General Public License along with
3.22 +this program. If not, see <http://www.gnu.org/licenses/>.
3.23 +"""
3.24 +
3.25 +from micropython.common import *
3.26 +from micropython.data import *
3.27 +from micropython.rsvp import *
3.28 +import compiler.ast
3.29 +
3.30 +class Helper:
3.31 +
3.32 + "Internal helper methods for AST visitors."
3.33 +
3.34 + # Allocation-related methods.
3.35 +
3.36 + def make_object(self, cls, n):
3.37 +
3.38 + """
3.39 + Request a new object with the given class 'cls' and with 'n' attributes.
3.40 + """
3.41 +
3.42 + # Load the class in order to locate the instance template.
3.43 +
3.44 + self.new_op(LoadConst(cls))
3.45 +
3.46 + # NOTE: Object headers are one location.
3.47 +
3.48 + self.new_op(MakeObject(n + 1))
3.49 +
3.50 + # Name-related methods.
3.51 +
3.52 + def get_scope(self, name):
3.53 +
3.54 + "Return the scope for the given 'name'."
3.55 +
3.56 + if self.unit.has_key(name):
3.57 + return "local"
3.58 + elif self.module.has_key(name):
3.59 + return "global"
3.60 + else:
3.61 + return "builtins"
3.62 +
3.63 + def load_builtin(self, name, node):
3.64 +
3.65 + "Generate an instruction loading 'name' for the given 'node'."
3.66 +
3.67 + self.new_op(LoadAddress(self.get_builtin(name, node)))
3.68 +
3.69 + def get_builtin_class(self, name, node):
3.70 +
3.71 + "Return the built-in class with the given 'name' for the given 'node'."
3.72 +
3.73 + return self.get_builtin(name, node).get_value()
3.74 +
3.75 + def get_builtin(self, name, node):
3.76 +
3.77 + """
3.78 + Return the built-in module definition for the given 'name', used by the
3.79 + given 'node'.
3.80 + """
3.81 +
3.82 + if self.builtins is not None:
3.83 + try:
3.84 + return self.builtins[name]
3.85 + except KeyError:
3.86 + raise TranslateError(self.module.full_name(), node, "No __builtins__ definition is available for name %r." % name)
3.87 + else:
3.88 + raise TranslateError(self.module.full_name(), node, "No __builtins__ module is available for name %r." % name)
3.89 +
3.90 + # Code feature methods.
3.91 +
3.92 + def new_block(self):
3.93 +
3.94 + "Return a new code block."
3.95 +
3.96 + return Block()
3.97 +
3.98 + def set_block(self, block):
3.99 +
3.100 + "Add the given 'block' to the unit's list of blocks."
3.101 +
3.102 + self.optimiser.reset()
3.103 + self.blocks.append(block)
3.104 +
3.105 + def get_loop_blocks(self):
3.106 + return self.loop_blocks[-1]
3.107 +
3.108 + def add_loop_blocks(self, next_block, exit_block):
3.109 + self.loop_blocks.append((next_block, exit_block))
3.110 +
3.111 + def drop_loop_blocks(self):
3.112 + self.loop_blocks.pop()
3.113 +
3.114 + def get_exception_blocks(self):
3.115 + return self.exception_blocks[-1]
3.116 +
3.117 + def add_exception_blocks(self, handler_block, exit_block):
3.118 + self.exception_blocks.append((handler_block, exit_block))
3.119 +
3.120 + def drop_exception_blocks(self):
3.121 + self.exception_blocks.pop()
3.122 +
3.123 + # Assignment expression values.
3.124 +
3.125 + def record_value(self, immediate=1):
3.126 +
3.127 + """
3.128 + Record the current active value for an assignment. If the optional
3.129 + 'immediate' parameter if set to a false value always allocates new
3.130 + temporary storage to hold the recorded value; otherwise, the
3.131 + value-providing instruction may be replicated in order to provide the
3.132 + active value later on.
3.133 + """
3.134 +
3.135 + if immediate:
3.136 + temp = self.optimiser.optimise_temp_storage()
3.137 + else:
3.138 + temp = self.get_temp()
3.139 + self.expr_temp.append(temp)
3.140 +
3.141 + def discard_value(self):
3.142 +
3.143 + "Discard any temporary storage in use for the current assignment value."
3.144 +
3.145 + self.discard_temp(self.expr_temp.pop())
3.146 +
3.147 + def set_source(self):
3.148 +
3.149 + "Set the source of an assignment using the current assignment value."
3.150 +
3.151 + self.optimiser.set_source(self.expr_temp[-1])
3.152 +
3.153 + # Optimise away constant storage if appropriate.
3.154 +
3.155 + if self.optimiser.optimise_constant_storage():
3.156 + self.remove_op()
3.157 +
3.158 + def is_immediate_user(self, node):
3.159 +
3.160 + """
3.161 + Return whether 'node' is an immediate user of an assignment expression.
3.162 + """
3.163 +
3.164 + return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr))
3.165 +
3.166 + def has_immediate_usage(self, nodes):
3.167 +
3.168 + """
3.169 + Return whether 'nodes' are all immediate users of an assignment expression.
3.170 + """
3.171 +
3.172 + for n in nodes:
3.173 + if not self.is_immediate_user(n):
3.174 + return 0
3.175 + return 1
3.176 +
3.177 + # Temporary storage administration.
3.178 +
3.179 + def get_temp(self):
3.180 +
3.181 + """
3.182 + Add a temporary storage instruction for the current value and return a
3.183 + sequence of access instructions.
3.184 + """
3.185 +
3.186 + position_in_frame = self.reserve_temp()
3.187 + self.new_op(StoreTemp(position_in_frame))
3.188 + return LoadTemp(position_in_frame)
3.189 +
3.190 + def reserve_temp(self, temp_position=None):
3.191 +
3.192 + """
3.193 + Reserve a new temporary storage position, or if the optional
3.194 + 'temp_position' is specified, ensure that this particular position is
3.195 + reserved.
3.196 + """
3.197 +
3.198 + if temp_position is not None:
3.199 + pass
3.200 + elif not self.temp_positions:
3.201 + temp_position = 0
3.202 + else:
3.203 + temp_position = max(self.temp_positions) + 1
3.204 + self.temp_positions.add(temp_position)
3.205 + self.max_temp_position = max(self.max_temp_position, temp_position)
3.206 + return self.unit.all_local_usage + temp_position # position in frame
3.207 +
3.208 + def ensure_temp(self, instruction=None):
3.209 +
3.210 + """
3.211 + Ensure that the 'instruction' is using a reserved temporary storage
3.212 + position.
3.213 + """
3.214 +
3.215 + if isinstance(instruction, LoadTemp):
3.216 + temp_position = instruction.attr - self.unit.all_local_usage
3.217 + self.reserve_temp(temp_position)
3.218 +
3.219 + def discard_temp(self, instruction=None):
3.220 +
3.221 + "Discard any temporary storage position used by 'instruction'."
3.222 +
3.223 + if isinstance(instruction, LoadTemp):
3.224 + temp_position = instruction.attr - self.unit.all_local_usage
3.225 + self.free_temp(temp_position)
3.226 +
3.227 + def free_temp(self, temp_position):
3.228 +
3.229 + "Free the temporary storage position specified by 'temp_position'."
3.230 +
3.231 + if temp_position in self.temp_positions:
3.232 + self.temp_positions.remove(temp_position)
3.233 +
3.234 + def set_frame_usage(self, node, extend):
3.235 +
3.236 + """
3.237 + Ensure that the frame usage for the unit associated with 'node' is set
3.238 + on the 'extend' instruction.
3.239 + """
3.240 +
3.241 + ntemp = self.max_temp_position + 1
3.242 + extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code.
3.243 +
3.244 + # Code writing methods.
3.245 +
3.246 + def new_op(self, op):
3.247 +
3.248 + "Add 'op' to the generated code."
3.249 +
3.250 + # Optimise load operations employed by this instruction.
3.251 +
3.252 + self.optimiser.optimise_load_operations(op)
3.253 + if self.optimiser.optimise_away_no_operations(op):
3.254 + return
3.255 +
3.256 + # Add the operation to the current block.
3.257 +
3.258 + self.blocks[-1].code.append(op)
3.259 + self.optimiser.set_new(op)
3.260 +
3.261 + def remove_op(self):
3.262 +
3.263 + "Remove the last instruction."
3.264 +
3.265 + op = self.blocks[-1].code.pop()
3.266 + self.optimiser.clear_active()
3.267 +
3.268 + def replace_op(self, op):
3.269 +
3.270 + "Replace the last added instruction with 'op'."
3.271 +
3.272 + self.remove_op()
3.273 + self.new_op(op)
3.274 +
3.275 + def replace_active_value(self, op):
3.276 +
3.277 + """
3.278 + Replace the value-providing active instruction with 'op' if appropriate.
3.279 + """
3.280 +
3.281 + self.optimiser.remove_active_value()
3.282 + self.new_op(op)
3.283 +
3.284 + def last_op(self):
3.285 +
3.286 + "Return the last added instruction."
3.287 +
3.288 + try:
3.289 + return self.blocks[-1].code[-1]
3.290 + except IndexError:
3.291 + return None
3.292 +
3.293 + # Common methods.
3.294 +
3.295 + def _visitAttr(self, node, classes):
3.296 +
3.297 + """
3.298 + Visit the attribute-related 'node', generating instructions based on the
3.299 + given 'classes'.
3.300 + """
3.301 +
3.302 + self.dispatch(node.expr)
3.303 + self._generateAttr(node, node.attrname, classes)
3.304 +
3.305 + def _generateAttr(self, node, attrname, classes):
3.306 +
3.307 + """
3.308 + Generate code for the access to 'attrname' using the given 'classes'.
3.309 + """
3.310 +
3.311 + AddressInstruction, AddressContextInstruction, AddressContextCondInstruction, \
3.312 + AttrInstruction, AttrIndexInstruction, AttrIndexContextInstruction, \
3.313 + AttrIndexContextCondInstruction = classes
3.314 +
3.315 + # Where the last operation (defining the attribute owner) yields a
3.316 + # constant...
3.317 +
3.318 + target_name = self.optimiser.optimise_constant_accessor()
3.319 +
3.320 + # Only try and discover the position if the target can be resolved.
3.321 + # Since instances cannot be constants, this involves classes and
3.322 + # modules.
3.323 +
3.324 + if target_name is not None:
3.325 +
3.326 + # Access the object table to get the attribute position.
3.327 +
3.328 + try:
3.329 + table_entry = self.objtable.table[target_name]
3.330 + except KeyError:
3.331 + raise TranslateError(self.module.full_name(), node,
3.332 + "No object entry exists for target %r." % target_name)
3.333 +
3.334 + try:
3.335 + pos = table_entry[attrname]
3.336 + except KeyError:
3.337 + raise TranslateError(self.module.full_name(), node,
3.338 + "No attribute entry exists for name %r in target %r." % (attrname, target_name))
3.339 +
3.340 + # Produce a suitable instruction.
3.341 +
3.342 + if AddressInstruction is not None:
3.343 + self.replace_active_value(AddressInstruction(pos))
3.344 + else:
3.345 + raise TranslateError(self.module.full_name(), node,
3.346 + "Storing of class or module attribute %r via an object is not permitted." % attrname)
3.347 +
3.348 + return
3.349 +
3.350 + # Where the last operation involves the special 'self' name, check to
3.351 + # see if the attribute is acceptably positioned and produce a direct
3.352 + # access to the attribute.
3.353 +
3.354 + # This is the only reliable way of detecting instance accesses at
3.355 + # compile-time since in general, objects could be classes or modules,
3.356 + # but 'self' should only refer to instances.
3.357 +
3.358 + elif self.optimiser.optimise_self_access(self.unit, attrname):
3.359 +
3.360 + # Either generate an instruction operating on an instance attribute.
3.361 +
3.362 + try:
3.363 + attr = self.unit.parent.instance_attributes()[attrname]
3.364 + self.new_op(AttrInstruction(attr))
3.365 + return
3.366 +
3.367 + # Or generate an instruction operating on a class attribute.
3.368 + # NOTE: Any simple instruction providing self is not removed.
3.369 +
3.370 + except KeyError:
3.371 +
3.372 + try:
3.373 + attr = self.unit.parent.all_attributes()[attrname]
3.374 +
3.375 + # Switch the context if the class attribute is compatible with
3.376 + # the instance.
3.377 +
3.378 + if attr.defined_within_hierarchy():
3.379 +
3.380 + # Only permit loading (not storing) of class attributes via self.
3.381 +
3.382 + if AddressContextInstruction is not None:
3.383 + self.new_op(AddressContextInstruction(attr))
3.384 + else:
3.385 + raise TranslateError(self.module.full_name(), node,
3.386 + "Storing of class attribute %r via self not permitted." % attrname)
3.387 +
3.388 + # Preserve the context if the class attribute comes from an
3.389 + # incompatible class.
3.390 +
3.391 + elif attr.defined_outside_hierarchy():
3.392 +
3.393 + # Only permit loading (not storing) of class attributes via self.
3.394 +
3.395 + if AddressInstruction is not None:
3.396 + self.new_op(AddressInstruction(attr))
3.397 + else:
3.398 + raise TranslateError(self.module.full_name(), node,
3.399 + "Storing of class attribute %r via self not permitted." % attrname)
3.400 +
3.401 + # Otherwise, test for a suitable context at run-time.
3.402 +
3.403 + else:
3.404 +
3.405 + # Only permit loading (not storing) of class attributes via self.
3.406 +
3.407 + if AddressContextCondInstruction is not None:
3.408 + self.new_op(AddressContextCondInstruction(attr))
3.409 + else:
3.410 + raise TranslateError(self.module.full_name(), node,
3.411 + "Storing of class attribute %r via self not permitted." % attrname)
3.412 +
3.413 + return
3.414 +
3.415 + # Or delegate the attribute access to a general instruction
3.416 + # since the kind of attribute cannot be deduced.
3.417 +
3.418 + except KeyError:
3.419 + pass
3.420 +
3.421 + # Otherwise, perform a normal operation.
3.422 +
3.423 + try:
3.424 + index = self.objtable.get_index(attrname)
3.425 +
3.426 + except self.objtable.TableError:
3.427 +
3.428 + # If this error arises on generated code, check the names_used
3.429 + # attribute on the Importer.
3.430 +
3.431 + raise TranslateError(self.module.full_name(), node,
3.432 + "No attribute entry exists for name %r." % attrname)
3.433 +
3.434 + # NOTE: Test for class vs. instance attributes, generating
3.435 + # NOTE: context-related instructions.
3.436 +
3.437 + if AttrIndexContextCondInstruction is not None:
3.438 + self.new_op(AttrIndexContextCondInstruction(index))
3.439 +
3.440 + # Store instructions do not need to consider context modifications.
3.441 +
3.442 + else:
3.443 + self.new_op(AttrIndexInstruction(index))
3.444 +
3.445 + # Invocations involve the following:
3.446 + #
3.447 + # 1. Reservation of a frame for the arguments
3.448 + # 2. Identification of the target which is then held in temporary storage
3.449 + # 3. Optional inclusion of a context (important for methods)
3.450 + # 4. Preparation of the argument frame
3.451 + # 5. Invocation of the target
3.452 + # 6. Discarding of the frame
3.453 + #
3.454 + # In order to support nested invocations - such as a(b(c)) - use of the
3.455 + # temporary storage is essential.
3.456 +
3.457 + def _startCallFunc(self):
3.458 +
3.459 + "Record the location of the invocation."
3.460 +
3.461 + op = MakeFrame()
3.462 + self.new_op(op) # records the start of the frame
3.463 + self.frame_makers.append(op)
3.464 +
3.465 + def _generateCallFunc(self, args, node):
3.466 +
3.467 + """
3.468 + Support a generic function invocation using the given 'args', occurring
3.469 + on the given 'node', where the expression providing the invocation
3.470 + target has just been generated.
3.471 +
3.472 + In other situations, the invocation is much simpler and does not need to
3.473 + handle the full flexibility of a typical Python invocation. Internal
3.474 + invocations, such as those employed by operators and certain
3.475 + control-flow mechanisms, use predetermined arguments and arguably do not
3.476 + need to support the same things as the more general invocations.
3.477 + """
3.478 +
3.479 + target, context, temp = self._generateCallFuncContext()
3.480 + self._generateCallFuncArgs(target, context, temp, args, node)
3.481 + return temp, target
3.482 +
3.483 + def _generateCallFuncContext(self):
3.484 +
3.485 + """
3.486 + Produce code which loads and checks the context of the current
3.487 + invocation, the instructions for whose target have already been
3.488 + produced, returning a list of instructions which reference the
3.489 + invocation target.
3.490 + """
3.491 +
3.492 + t = self.optimiser.optimise_known_target()
3.493 + if t:
3.494 + target, context = t
3.495 + if isinstance(target, Instance): # lambda object
3.496 + target, context = None, None
3.497 + else:
3.498 + target, context = None, None
3.499 +
3.500 + # Store the target in temporary storage for subsequent referencing.
3.501 + # NOTE: This may not be appropriate for class invocations
3.502 + # NOTE: (instantiation).
3.503 +
3.504 + temp = self.optimiser.optimise_temp_storage()
3.505 +
3.506 + # Where a target or context are not known or where an instance is known
3.507 + # to be the context, load the context.
3.508 +
3.509 + if target is None or isinstance(context, Instance):
3.510 + self.new_op(temp)
3.511 + self.new_op(LoadContext())
3.512 + self.new_op(StoreFrame(0))
3.513 +
3.514 + # For known instantiations, provide a new object as the first argument
3.515 + # to the __init__ method.
3.516 +
3.517 + elif isinstance(target, Class):
3.518 + self.make_object(target, len(target.instance_attributes()))
3.519 + self.new_op(StoreFrame(0))
3.520 +
3.521 + # Otherwise omit the context.
3.522 +
3.523 + else:
3.524 + pass # NOTE: Class methods should be supported.
3.525 +
3.526 + return target, context, temp
3.527 +
3.528 + def _generateCallFuncArgs(self, target, context, temp, args, node):
3.529 +
3.530 + """
3.531 + Given invocation 'target' and 'context' information, the 'temp'
3.532 + reference to the target, a list of nodes representing the 'args'
3.533 + (arguments), generate instructions which load the arguments for the
3.534 + invocation defined by the given 'node'.
3.535 + """
3.536 +
3.537 + # Evaluate the arguments.
3.538 +
3.539 + employed_positions = set()
3.540 + employed_keywords = set()
3.541 + extra_keywords = []
3.542 +
3.543 + # Find keyword arguments in advance in order to help resolve targets.
3.544 +
3.545 + for arg in args:
3.546 + if isinstance(arg, compiler.ast.Keyword):
3.547 + employed_keywords.add(arg.name)
3.548 +
3.549 + possible_targets = self.paramtable.all_possible_objects(employed_keywords)
3.550 +
3.551 + # Note the presence of the context in the frame where appropriate.
3.552 +
3.553 + if target is None or isinstance(context, Instance):
3.554 + ncontext = 1
3.555 + expect_context = 0
3.556 +
3.557 + # Handle calls to classes.
3.558 +
3.559 + elif isinstance(target, Class):
3.560 + ncontext = 1
3.561 + expect_context = 0
3.562 + target = target.get_init_method()
3.563 +
3.564 + # Method calls via classes.
3.565 +
3.566 + elif isinstance(context, Class):
3.567 + ncontext = 0
3.568 + expect_context = 1
3.569 +
3.570 + # Function calls.
3.571 +
3.572 + else:
3.573 + ncontext = 0
3.574 + expect_context = 0
3.575 +
3.576 + first = 1
3.577 + frame_pos = ncontext
3.578 + max_keyword_pos = -1
3.579 +
3.580 + for arg in args:
3.581 +
3.582 + # Handle positional and keyword arguments separately.
3.583 +
3.584 + if isinstance(arg, compiler.ast.Keyword):
3.585 +
3.586 + # Optimise where the target is known now.
3.587 +
3.588 + if target is not None:
3.589 +
3.590 + # Find the parameter table entry for the target.
3.591 +
3.592 + target_name = target.full_name()
3.593 +
3.594 + # Look for a callable with the precise target name.
3.595 +
3.596 + table_entry = self.paramtable.table[target_name]
3.597 +
3.598 + # Look the name up in the parameter table entry.
3.599 +
3.600 + try:
3.601 + pos = table_entry[arg.name]
3.602 +
3.603 + # Where no position is found, this could be an extra keyword
3.604 + # argument.
3.605 +
3.606 + except KeyError:
3.607 + extra_keywords.append(arg)
3.608 + continue
3.609 +
3.610 + # Test for illegal conditions.
3.611 +
3.612 + if pos in employed_positions:
3.613 + raise TranslateError(self.module.full_name(), node,
3.614 + "Keyword argument %r overwrites parameter %r." % (arg.name, pos))
3.615 +
3.616 + employed_positions.add(pos)
3.617 +
3.618 + # Generate code for the keyword and the positioning
3.619 + # operation.
3.620 +
3.621 + self.dispatch(arg.expr)
3.622 + self.new_op(StoreFrame(pos))
3.623 +
3.624 + # Otherwise, generate the code needed to obtain the details of
3.625 + # the parameter location.
3.626 +
3.627 + else:
3.628 +
3.629 + # Combine the target details with the name to get the location.
3.630 + # See the access method on the List class.
3.631 +
3.632 + try:
3.633 + paramindex = self.paramtable.get_index(arg.name)
3.634 +
3.635 + # Where no position is found, this could be an extra keyword
3.636 + # argument.
3.637 +
3.638 + except self.paramtable.TableError:
3.639 + extra_keywords.append(arg)
3.640 + continue
3.641 +
3.642 + # Generate code for the keyword and the positioning
3.643 + # operation. Get the value as the source of the assignment.
3.644 +
3.645 + self.dispatch(arg.expr)
3.646 + self.record_value()
3.647 +
3.648 + # Store the source value using the callable's parameter
3.649 + # table information.
3.650 +
3.651 + self.new_op(temp)
3.652 + self.new_op(StoreFrameIndex(paramindex))
3.653 +
3.654 + self.set_source()
3.655 + self.discard_value()
3.656 +
3.657 + # Record the highest possible frame position for this argument.
3.658 +
3.659 + max_keyword_pos = max(max_keyword_pos, max(self.paramtable.all_attribute_positions(arg.name)))
3.660 +
3.661 + else:
3.662 + self.dispatch(arg)
3.663 + self.new_op(StoreFrame(frame_pos))
3.664 +
3.665 + employed_positions.add(frame_pos)
3.666 +
3.667 + # Check to see if the first argument is appropriate (compatible with
3.668 + # the target where methods are being invoked via classes).
3.669 +
3.670 + if first and expect_context:
3.671 +
3.672 + # Drop any test if the target and the context are known.
3.673 +
3.674 + if not self.optimiser.have_correct_self_for_target(context, self.unit):
3.675 +
3.676 + continue_block = self.new_block()
3.677 +
3.678 + self.new_op(CheckSelf())
3.679 + self.optimiser.set_source(temp)
3.680 + self.new_op(JumpIfTrue(continue_block))
3.681 +
3.682 + # Where the context is inappropriate, drop the incomplete frame and
3.683 + # raise an exception.
3.684 +
3.685 + self.new_op(DropFrame())
3.686 + self.new_op(LoadResult())
3.687 +
3.688 + self.load_builtin("TypeError", node)
3.689 + self.new_op(StoreException())
3.690 + self.new_op(RaiseException())
3.691 +
3.692 + self.set_block(continue_block)
3.693 +
3.694 + first = 0
3.695 + frame_pos += 1
3.696 +
3.697 + # NOTE: Extra keywords are not supported.
3.698 + # NOTE: Somehow, the above needs to be combined with * arguments.
3.699 +
3.700 + if extra_keywords:
3.701 + print "Warning: extra keyword argument(s) %s not handled." % ", ".join([arg.name for arg in extra_keywords])
3.702 +
3.703 + # Either test for a complete set of arguments.
3.704 +
3.705 + if target is not None:
3.706 +
3.707 + # Make sure that enough arguments have been given.
3.708 +
3.709 + nargs_max = len(target.positional_names)
3.710 + ndefaults = len(target.defaults)
3.711 + nargs_min = nargs_max - ndefaults
3.712 +
3.713 + for i in range(ncontext, nargs_min):
3.714 + if i not in employed_positions:
3.715 + raise TranslateError(self.module.full_name(), node,
3.716 + "Argument %r not supplied for %r: need at least %d argument(s)." % (i+1, target.name, nargs_min))
3.717 +
3.718 + nargs = frame_pos
3.719 +
3.720 + if nargs > nargs_max and not target.has_star and not target.has_dstar:
3.721 + raise TranslateError(self.module.full_name(), node,
3.722 + "Too many arguments for %r: need at most %d argument(s)." % (target.name, nargs_max))
3.723 +
3.724 + # Where defaults are involved, put them into the frame.
3.725 +
3.726 + self._generateCallFuncDefaultArgs(target, temp, nargs_min, nargs_max, employed_positions)
3.727 +
3.728 + # Set the frame size.
3.729 +
3.730 + self._endCallFuncArgs(nargs_max)
3.731 +
3.732 + # Or generate instructions to do this at run-time.
3.733 +
3.734 + else:
3.735 + max_pos = max(max(employed_positions or [-1]), max_keyword_pos, frame_pos - 1)
3.736 +
3.737 + # Only check non-empty frames (using the callable's details).
3.738 +
3.739 + if employed_positions or max_pos >= 0:
3.740 + self.new_op(temp)
3.741 + self.new_op(CheckFrame(max_pos + 1))
3.742 +
3.743 + # Set the frame size.
3.744 +
3.745 + self._endCallFuncArgs(max_pos + 1)
3.746 +
3.747 + def _generateCallFuncDefaultArgs(self, target, temp, nargs_min, nargs_max, employed_positions):
3.748 +
3.749 + """
3.750 + For the given 'target' and 'temp' reference to the target, generate
3.751 + default arguments for those positions in the range 'nargs_min'...
3.752 + 'nargs_max' which are not present in the 'employed_positions'
3.753 + collection.
3.754 + """
3.755 +
3.756 + # Where a lambda is involved, construct a dynamic object to hold the
3.757 + # defaults.
3.758 +
3.759 + dynamic = target.name is None
3.760 +
3.761 + # Here, we use negative index values to visit the right hand end of
3.762 + # the defaults list.
3.763 +
3.764 + for pos in range(nargs_min, nargs_max):
3.765 + if pos not in employed_positions:
3.766 + if dynamic:
3.767 + self.new_op(temp)
3.768 + self.new_op(LoadAttr(target.default_attrs[pos - nargs_min]))
3.769 + else:
3.770 + self.new_op(LoadAddress(target.default_attrs[pos - nargs_min]))
3.771 + self.new_op(StoreFrame(pos))
3.772 +
3.773 + def _doCallFunc(self, instruction, target=None):
3.774 +
3.775 + "Make the invocation."
3.776 +
3.777 + if isinstance(target, Class):
3.778 + self.new_op(LoadConst(target.get_init_method()))
3.779 + else:
3.780 + self.new_op(instruction)
3.781 + self.new_op(LoadCallable())
3.782 + self.new_op(JumpWithFrame())
3.783 +
3.784 + def _endCallFuncArgs(self, nargs):
3.785 +
3.786 + "Set the frame size."
3.787 +
3.788 + self.frame_makers[-1].attr = nargs
3.789 + self.frame_makers.pop()
3.790 +
3.791 + def _endCallFunc(self, instruction=None, target=None, load_result=1):
3.792 +
3.793 + "Finish the invocation and tidy up afterwards."
3.794 +
3.795 + if isinstance(target, Class):
3.796 + self.new_op(LoadName(target.get_init_method().all_locals()["self"])) # load the context in the invocation frame
3.797 + self.new_op(StoreResult())
3.798 + self.new_op(DropFrame())
3.799 + if load_result:
3.800 + self.new_op(LoadResult())
3.801 +
3.802 + # Discard any temporary storage instructions.
3.803 +
3.804 + if instruction is not None:
3.805 + self.discard_temp(instruction)
3.806 +
3.807 + def _generateFunctionDefaults(self, function):
3.808 +
3.809 + """
3.810 + Generate the default initialisation code for 'function', returning
3.811 + a temporary storage reference if a dynamic object was created for the
3.812 + function.
3.813 + """
3.814 +
3.815 + attr_to_default = zip(function.default_attrs, function.defaults)
3.816 + if not attr_to_default:
3.817 + return None
3.818 +
3.819 + # Where a lambda is involved, construct a dynamic object to hold the
3.820 + # defaults.
3.821 +
3.822 + dynamic = function.name is None
3.823 +
3.824 + if dynamic:
3.825 + self.make_object(self.get_builtin_class("function", function), len(attr_to_default))
3.826 + temp = self.get_temp()
3.827 +
3.828 + for attr, default in attr_to_default:
3.829 + self.dispatch(default)
3.830 +
3.831 + self.record_value()
3.832 + if dynamic:
3.833 + self.new_op(temp)
3.834 + self.new_op(StoreAttr(attr))
3.835 + else:
3.836 + self.new_op(StoreAddress(attr))
3.837 + self.set_source()
3.838 + self.discard_value()
3.839 +
3.840 + if dynamic:
3.841 + return temp
3.842 + else:
3.843 + return None
3.844 +
3.845 + def _visitName(self, node, classes):
3.846 +
3.847 + """
3.848 + Visit the name-related 'node', generating instructions based on the
3.849 + given 'classes'.
3.850 + """
3.851 +
3.852 + name = node.name
3.853 + scope = self.get_scope(name)
3.854 + #print self.module.name, node.lineno, name, scope
3.855 + self._generateName(name, scope, classes, node)
3.856 +
3.857 + def _generateName(self, name, scope, classes, node):
3.858 +
3.859 + """
3.860 + Generate code for the access to 'name' in 'scope' using the given
3.861 + 'classes', and using the given 'node' as the source of the access.
3.862 + """
3.863 +
3.864 + NameInstruction, AddressInstruction = classes
3.865 +
3.866 + if scope == "local":
3.867 + unit = self.unit
3.868 + if isinstance(unit, Function):
3.869 + self.new_op(NameInstruction(unit.all_locals()[name]))
3.870 + elif isinstance(unit, Class):
3.871 + self.new_op(AddressInstruction(unit.all_class_attributes()[name]))
3.872 + elif isinstance(unit, Module):
3.873 + self.new_op(AddressInstruction(unit.module_attributes()[name]))
3.874 + else:
3.875 + raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r." % (unit, name))
3.876 +
3.877 + elif scope == "global":
3.878 + globals = self.module.module_attributes()
3.879 + if globals.has_key(name):
3.880 + self.new_op(AddressInstruction(globals[name]))
3.881 + else:
3.882 + raise TranslateError(self.module.full_name(), node, "Module %r has no attribute %r." % (self.module, name))
3.883 +
3.884 + else:
3.885 + self.new_op(AddressInstruction(self.get_builtin(name, node)))
3.886 +
3.887 + def _visitUnary(self, node):
3.888 +
3.889 + """
3.890 + _t = node.expr
3.891 + try:
3.892 + _result = _t.__pos__()
3.893 + except AttributeError:
3.894 + raise TypeError
3.895 + """
3.896 +
3.897 + method = unary_methods[node.__class__.__name__]
3.898 +
3.899 + type_error_block = self.new_block()
3.900 + end_block = self.new_block()
3.901 +
3.902 + # Evaluate and store the operand in temporary storage.
3.903 +
3.904 + self.dispatch(node.expr)
3.905 + temp = self.optimiser.optimise_temp_storage()
3.906 +
3.907 + self.new_op(temp)
3.908 +
3.909 + # Get the method on temp.
3.910 +
3.911 + self._generateAttr(node, method, self.attribute_load_instructions)
3.912 + temp_method = self.optimiser.optimise_temp_storage()
3.913 +
3.914 + self._handleAttributeError(node, type_error_block)
3.915 +
3.916 + # Add arguments.
3.917 + # NOTE: No support for defaults.
3.918 +
3.919 + self._startCallFunc()
3.920 + self.new_op(temp) # Explicit context as first argument.
3.921 + self.new_op(StoreFrame(0))
3.922 + self._endCallFuncArgs(1)
3.923 + self._doCallFunc(temp_method)
3.924 + self._endCallFunc(temp_method)
3.925 + self.new_op(Jump(end_block))
3.926 +
3.927 + # Store the result.
3.928 +
3.929 + temp_out = self.get_temp()
3.930 +
3.931 + # Raise a TypeError.
3.932 +
3.933 + self.set_block(type_error_block)
3.934 + self.load_builtin("TypeError", node)
3.935 + self.new_op(StoreException())
3.936 + self.new_op(RaiseException())
3.937 +
3.938 + self.set_block(end_block)
3.939 +
3.940 + # Produce the result.
3.941 +
3.942 + self.new_op(temp_out)
3.943 +
3.944 + # Compilation duties...
3.945 +
3.946 + self.discard_temp(temp)
3.947 + self.discard_temp(temp_out)
3.948 +
3.949 + def _visitBinary(self, node):
3.950 +
3.951 + """
3.952 + _t1 = node.left
3.953 + _t2 = node.right
3.954 + try:
3.955 + _result = _t1.__add__(_t2)
3.956 + if _result is NotImplemented:
3.957 + raise AttributeError
3.958 + except AttributeError:
3.959 + try:
3.960 + _result = _t2.__radd__(_t1)
3.961 + if _result is NotImplemented:
3.962 + raise AttributeError
3.963 + except AttributeError:
3.964 + raise TypeError
3.965 + """
3.966 +
3.967 + left_method, right_method = binary_methods[node.__class__.__name__]
3.968 +
3.969 + # Evaluate and store the left operand in temporary storage.
3.970 +
3.971 + self.dispatch(node.left)
3.972 + temp1 = self.optimiser.optimise_temp_storage()
3.973 +
3.974 + # Evaluate and store the right operand in temporary storage.
3.975 +
3.976 + self.dispatch(node.right)
3.977 + temp2 = self.optimiser.optimise_temp_storage()
3.978 +
3.979 + temp_out = self._generateBinary(node, temp1, temp2, left_method, right_method)
3.980 +
3.981 + # Produce the result.
3.982 +
3.983 + self.new_op(temp_out)
3.984 +
3.985 + # Compilation duties...
3.986 +
3.987 + self.discard_temp(temp1)
3.988 + self.discard_temp(temp2)
3.989 + self.discard_temp(temp_out)
3.990 +
3.991 + def _generateBinary(self, node, temp1, temp2, left_method, right_method):
3.992 +
3.993 + """
3.994 + For the given 'node', generate the binary operator pattern for the
3.995 + operands 'temp1' and 'temp2', employing 'left_method' and 'right_method'
3.996 + as defined for binary operators, but also used in comparisons (for which
3.997 + this method is provided).
3.998 +
3.999 + A temporary storage reference is returned from this method.
3.1000 + """
3.1001 +
3.1002 + right_block = self.new_block()
3.1003 + type_error_block = self.new_block()
3.1004 + end_block = self.new_block()
3.1005 +
3.1006 + # Left method.
3.1007 +
3.1008 + temp_out = self._generateOpMethod(node, temp1, temp2, left_method, right_block, end_block)
3.1009 + self.discard_temp(temp_out) # NOTE: Will re-use the same storage.
3.1010 +
3.1011 + # Right method.
3.1012 +
3.1013 + self.set_block(right_block)
3.1014 + temp_out = self._generateOpMethod(node, temp2, temp1, right_method, type_error_block, end_block)
3.1015 +
3.1016 + # Raise a TypeError.
3.1017 +
3.1018 + self.set_block(type_error_block)
3.1019 + self.load_builtin("TypeError", node)
3.1020 + self.new_op(StoreException())
3.1021 + self.new_op(RaiseException())
3.1022 +
3.1023 + self.set_block(end_block)
3.1024 + return temp_out
3.1025 +
3.1026 + def _generateOpMethod(self, node, temp1, temp2, method_name, next_method_block, end_block):
3.1027 +
3.1028 + """
3.1029 + For the given 'node', generate the operator method invocation using the
3.1030 + operands 'temp1' and 'temp2', employing the given 'method_name', and
3.1031 + jumping appropriately to 'next_method_block' where a NotImplemented
3.1032 + result is returned, or to 'end_block' if the method call was successful.
3.1033 +
3.1034 + A temporary storage reference is returned from this method.
3.1035 + """
3.1036 +
3.1037 + end_attempt_block = self.new_block()
3.1038 +
3.1039 + self.new_op(temp1)
3.1040 +
3.1041 + # Get method on temp1.
3.1042 +
3.1043 + self._generateAttr(node, method_name, self.attribute_load_instructions)
3.1044 + temp_method = self.optimiser.optimise_temp_storage()
3.1045 +
3.1046 + self._handleAttributeError(node, end_attempt_block)
3.1047 +
3.1048 + # Add arguments.
3.1049 + # NOTE: No support for defaults.
3.1050 +
3.1051 + self._startCallFunc()
3.1052 + self.new_op(temp1)
3.1053 + self.new_op(StoreFrame(0))
3.1054 + self.new_op(temp2)
3.1055 + self.new_op(StoreFrame(1))
3.1056 + self._endCallFuncArgs(2)
3.1057 + self._doCallFunc(temp_method)
3.1058 + self._endCallFunc(temp_method)
3.1059 +
3.1060 + # Store the result.
3.1061 +
3.1062 + temp_out = self.get_temp()
3.1063 +
3.1064 + # Test for NotImplemented.
3.1065 + # Don't actually raise an exception.
3.1066 +
3.1067 + self.new_op(TestIdentityAddress(self.importer.get_predefined_constant("NotImplemented")))
3.1068 + self.new_op(JumpIfTrue(next_method_block))
3.1069 + self.new_op(Jump(end_block))
3.1070 +
3.1071 + # End method attempt.
3.1072 +
3.1073 + self.set_block(end_attempt_block)
3.1074 + return temp_out
3.1075 +
3.1076 + def _handleAttributeError(self, node, end_call_block):
3.1077 +
3.1078 + """
3.1079 + Add exception handling to the method acquisition instructions where the
3.1080 + attribute access cannot be resolved at compile-time.
3.1081 + """
3.1082 +
3.1083 + if not self.optimiser.optimise_known_target():
3.1084 + self.load_builtin("AttributeError", node)
3.1085 + self.new_op(CheckException())
3.1086 + self.new_op(JumpIfTrue(end_call_block))
3.1087 +
3.1088 + def _generateSequence(self, sequence_type, node):
3.1089 +
3.1090 + "Make a sequence of 'sequence_type' for the given program 'node'."
3.1091 +
3.1092 + self.make_object(self.get_builtin_class(sequence_type, node), len(node.nodes))
3.1093 + temp = self.get_temp()
3.1094 +
3.1095 + for i, n in enumerate(node.nodes):
3.1096 + self.dispatch(n)
3.1097 + self.record_value()
3.1098 + self.new_op(temp)
3.1099 + self.new_op(StoreAttr(Attr(i, None, None)))
3.1100 + self.set_source()
3.1101 + self.discard_value()
3.1102 +
3.1103 + self.new_op(temp)
3.1104 + self.discard_temp(temp)
3.1105 +
3.1106 + def _generateTestBoolean(self, node, temp):
3.1107 +
3.1108 + """
3.1109 + Generate a test of the boolean status of the current value for the given
3.1110 + program 'node'.
3.1111 + """
3.1112 +
3.1113 + # Get method on temp.
3.1114 + # NOTE: Using __bool__ instead of __nonzero__.
3.1115 +
3.1116 + self._generateAttr(node, "__bool__", self.attribute_load_instructions)
3.1117 + temp_method = self.optimiser.optimise_temp_storage()
3.1118 +
3.1119 + self._startCallFunc()
3.1120 + self.new_op(temp)
3.1121 + self.new_op(StoreFrame(0))
3.1122 + self._endCallFuncArgs(1)
3.1123 + self._doCallFunc(temp_method)
3.1124 + self._endCallFunc(temp_method)
3.1125 +
3.1126 + self.discard_temp(temp_method)
3.1127 +
3.1128 + # Convert result to boolean (a StoreBoolean operation).
3.1129 +
3.1130 + self.new_op(TestIdentityAddress(self.importer.get_predefined_constant("True")))
3.1131 +
3.1132 + def _generateLoadBoolean(self, node):
3.1133 +
3.1134 + """
3.1135 + Generate instructions to load the appropriate value given the current
3.1136 + boolean status.
3.1137 + """
3.1138 +
3.1139 + true_block = self.new_block()
3.1140 + end_block = self.new_block()
3.1141 +
3.1142 + self.new_op(JumpIfTrue(true_block))
3.1143 + self.new_op(LoadAddress(self.importer.get_predefined_constant("False")))
3.1144 + self.new_op(Jump(end_block))
3.1145 +
3.1146 + self.set_block(true_block)
3.1147 + self.new_op(LoadAddress(self.importer.get_predefined_constant("True")))
3.1148 +
3.1149 + self.set_block(end_block)
3.1150 +
3.1151 +# vim: tabstop=4 expandtab shiftwidth=4
4.1 --- a/rsvp.py Fri Mar 20 00:32:43 2009 +0100
4.2 +++ b/rsvp.py Sun Apr 05 03:05:59 2009 +0200
4.3 @@ -78,16 +78,21 @@
4.4
4.5 "A really simple virtual processor."
4.6
4.7 - def __init__(self, memory, objlist, paramlist, pc=None, debug=0):
4.8 + def __init__(self, memory, objlist, paramlist, true_constant, false_constant, pc=None, debug=0):
4.9
4.10 """
4.11 Initialise the processor with a 'memory' (a list of values containing
4.12 - instructions and data) and the optional program counter 'pc'.
4.13 + instructions and data), the object and parameter lists 'objlist' and
4.14 + 'paramlist', the addresses 'true_constant' and 'false_constant', and the
4.15 + optional program counter 'pc'.
4.16 """
4.17
4.18 self.memory = memory
4.19 self.objlist = objlist.as_raw()
4.20 self.paramlist = paramlist.as_raw()
4.21 + self.true_constant = true_constant
4.22 + self.false_constant = false_constant
4.23 +
4.24 self.pc = pc or 0
4.25 self.debug = debug
4.26
4.27 @@ -120,6 +125,7 @@
4.28 # Native class constants.
4.29
4.30 cls = objlist.access("__builtins__", "int")
4.31 + self.int_class_location = cls and cls.get_value() and cls.get_value().location
4.32 self.int_instance_location = cls and cls.get_value() and cls.get_value().instance_template_location
4.33
4.34 # Debugging attributes.
4.35 @@ -307,8 +313,11 @@
4.36 # of proper locations.
4.37
4.38 if isinstance(addr, str):
4.39 - self.native_functions[addr](self)
4.40 - return next
4.41 + handler = self.native_functions[addr](self)
4.42 + if handler is None:
4.43 + return next
4.44 + else:
4.45 + return handler
4.46 else:
4.47 self.push_pc(self.pc + 1)
4.48 return addr
4.49 @@ -639,7 +648,7 @@
4.50
4.51 # Test operand suitability.
4.52
4.53 - if not self._CheckInstance(left, self.int_instance_location) and self._CheckInstance(right, self.int_instance_location):
4.54 + if not self._CheckInstance(left, self.int_class_location) and self._CheckInstance(right, self.int_class_location):
4.55 self.exception = self.type_error
4.56 return self.RaiseException()
4.57
4.58 @@ -651,6 +660,10 @@
4.59 # Make a new object.
4.60
4.61 addr = self._MakeObject(2, self.int_instance_location)
4.62 +
4.63 + # Store the result.
4.64 + # NOTE: The data is considered ready to use.
4.65 +
4.66 self.save(addr + 1, self.load(left_data) + self.load(right_data))
4.67
4.68 # Return the new object.
4.69 @@ -658,10 +671,36 @@
4.70
4.71 self.result = addr, addr
4.72
4.73 + def builtins_int_bool(self):
4.74 + frame = self.local_sp_stack[-1]
4.75 +
4.76 + # Get operands addresses.
4.77 +
4.78 + left_context, left = self.frame_stack[frame]
4.79 +
4.80 + # Test operand suitability.
4.81 +
4.82 + if not self._CheckInstance(left, self.int_class_location):
4.83 + self.exception = self.type_error
4.84 + return self.RaiseException()
4.85 +
4.86 + # NOTE: Assume single location for data.
4.87 +
4.88 + left_data = left + 1
4.89 +
4.90 + # Test the data.
4.91 + # NOTE: The data is considered ready to use.
4.92 +
4.93 + if self.load(left_data) != 0:
4.94 + self.result = self.true_constant, self.true_constant
4.95 + else:
4.96 + self.result = self.false_constant, self.false_constant
4.97 +
4.98 native_functions = {
4.99 "__builtins__.object.__init__" : builtins_object_init,
4.100 "__builtins__.int.__init__" : builtins_int_init,
4.101 "__builtins__.int.__add__" : builtins_int_add,
4.102 + "__builtins__.int.__bool__" : builtins_int_bool,
4.103 }
4.104
4.105 # vim: tabstop=4 expandtab shiftwidth=4
5.1 --- a/test.py Fri Mar 20 00:32:43 2009 +0100
5.2 +++ b/test.py Sun Apr 05 03:05:59 2009 +0200
5.3 @@ -40,7 +40,10 @@
5.4 print "Getting raw image..."
5.5 rc = program.get_raw_image()
5.6 print "Initialising the machine..."
5.7 - rm = rsvp.RSVPMachine(rc, objlist, paramlist, debug=debug)
5.8 + importer = program.get_importer()
5.9 + true_constant = importer.get_constant(True).location
5.10 + false_constant = importer.get_constant(False).location
5.11 + rm = rsvp.RSVPMachine(rc, objlist, paramlist, true_constant, false_constant, debug=debug)
5.12 rm.pc = program.code_location
5.13 return rm
5.14
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/tests/swap.py Sun Apr 05 03:05:59 2009 +0200
6.3 @@ -0,0 +1,7 @@
6.4 +#!/usr/bin/env python
6.5 +
6.6 +a = 1
6.7 +b = 2
6.8 +b, a = a, b
6.9 +
6.10 +# vim: tabstop=4 expandtab shiftwidth=4