# HG changeset patch # User Paul Boddie # Date 1372462092 -7200 # Node ID b772a168a2ba6c8bb777a91fb3cffcccc3780629 # Parent 979e2a19fc4a1998330b613090aa093b04101170 Moved code generation and execution support to a separate syspython distribution. Removed optimisation controls since only unused object removal is applicable to the inspection process. diff -r 979e2a19fc4a -r b772a168a2ba README.txt --- a/README.txt Fri Jun 28 21:17:02 2013 +0200 +++ b/README.txt Sat Jun 29 01:28:12 2013 +0200 @@ -1,3 +1,6 @@ +NOTE: This document needs updating for the separation of micropython and +NOTE: syspython functionality. + Introduction ------------ diff -r 979e2a19fc4a -r b772a168a2ba micropython/__init__.py --- a/micropython/__init__.py Fri Jun 28 21:17:02 2013 +0200 +++ b/micropython/__init__.py Sat Jun 29 01:28:12 2013 +0200 @@ -28,10 +28,9 @@ importer = Importer(sys.path) To generate programs, the above importer should be supplied in the -initialisation of a program instance, and then various methods are called: +initialisation of a program instance: program = Program(importer) -image = program.get_raw_image() Such importer and program objects are the most convenient mechanism through which the functionality of the micropython package may be accessed. @@ -40,14 +39,9 @@ from micropython.data import * from micropython.errors import * from micropython.objectset import ObjectSet -from micropython.program import Location from micropython.types import * -import micropython.ast -import micropython.native -import micropython.opt import micropython.inspect import micropython.table -import bisect import os import sys @@ -60,32 +54,20 @@ "This class supports the generation of a program image." - supported_optimisations = micropython.opt.Optimiser.supported_optimisations - - def __init__(self, importer, optimisations=None): + def __init__(self, importer): """ Initialise the program representation with an 'importer' which is able to locate and load Python modules. - - The optional 'optimisations' cause certain techniques to be used in - reducing program size and improving program efficiency. """ self.importer = importer - self.optimisations = optimisations or set() - self.native = micropython.native.NativeLibrary(self) # Remember the tables once generated. self.objtable = None self.paramtable = None - # Main program information. - - self.code = None - self.code_location = None - # A record of nodes for which no attribute target could be found. self.unknown_target_nodes = [] @@ -116,166 +98,6 @@ self.importer.finalise(objtable) - def get_image(self, with_builtins=0): - - """ - Return the program image including built-in objects if 'with_builtins' - is specified and set to a true value. - """ - - if self.code is not None: - return self.code - - # Optimise and regenerate the object table. - - self.finalise() - self.code = [] - - # Append constants to the image. - - for const in self.importer.constants(): - self.code.append(const) - - # Generate each module. - - last_module = self.importer.modules_ordered[-1] - - for module in self.importer.modules_ordered: - suppress_builtins = not with_builtins and module.name in ("__builtins__", "native") - - # Position the module in the image and make a translation. - - trans = micropython.ast.Translation(module, self) - - # Add header details. - - self.code.append(module) - - # Append module attributes to the image. - - attributes = module.module_attributes() - self.code += module.attributes_as_list() - - # Append classes and functions to the image. - - for obj in module.all_objects: - if isinstance(obj, Class): - - # Add header details. - - self.code.append(obj) - - # Append class attributes to the image. - - attributes = obj.class_attributes() - self.code += obj.attributes_as_list() - - # Omit built-in function code where requested. - - if suppress_builtins and obj.astnode.doc is None: - continue - - # Generate the instantiator/initialiser. - # Append the function code to the image. - - code = trans.get_instantiator_code(obj) - self.code += code - - # Class-level code is generated separately at the module - # level, and the code location is set within the code - # generation process for the module. - - elif isinstance(obj, Function): - - # Add header details. - - self.code.append(obj) - - # Append any default values to the image. - # Only do this for functions which are not dynamic. - - if not obj.is_dynamic(): - self.code += obj.default_attrs - - # Omit built-in function code where requested. - - if suppress_builtins and obj.astnode.doc is None: - pass - - # Append the function code to the image. - - else: - code = trans.get_code(obj) - self.code += code - - # Omit built-in module code where requested. - - if suppress_builtins: - pass - - # Append the module top-level code to the image. - - else: - code = trans.get_module_code() - self.code += code - - # Generate the native library once we know how much of it is used. - - self.code += self.native.get_native_code() - - return self.code - - def get_raw_image(self, architecture=None, with_builtins=0): - - "Return the raw image representation of the program." - - architecture = architecture or micropython.rsvp - - self.get_image(with_builtins) - - objtable = self.get_object_table() - paramtable = self.get_parameter_table() - - # Position the objects. - - pos = 0 - - for item in self.code: - arch_item = architecture.get_object(item) - - # Get the raw version for the architecture. - - if arch_item is not None: - pos = arch_item.set_location(pos, objtable, with_builtins) - else: - pos += 1 - - # Generate the raw code. - - self.raw_code = [] - - for item in self.code: - arch_item = architecture.get_object(item) - - # Get the raw version for the architecture. - - if arch_item is not None: - arch_item.finalise_location(with_builtins) - self.raw_code += arch_item.as_raw(objtable, paramtable, with_builtins) - arch_item.finalise_body_location(with_builtins) - else: - self.raw_code.append(item) - - # Fix the module locations. - - for module in self.importer.modules_ordered: - - if not with_builtins and module.name in ("__builtins__", "native"): - continue - - self.code_location = self.importer.modules["__main__"].code_location - return self.raw_code - def get_object_table(self, reset=0): "Return a table with details of attributes for classes and modules." @@ -380,16 +202,6 @@ return self.paramtable - def object_at(self, pos): - - "Return the object whose code can be found at 'pos'." - - i = bisect.bisect_left(self.code, Location(pos)) - if i > 0: - return self.code[i-1] - else: - return None - class Importer: "An import machine, searching for and loading modules." @@ -406,7 +218,7 @@ "__call__" ] - def __init__(self, path=None, verbose=0, optimisations=None): + def __init__(self, path=None, verbose=0): """ Initialise the importer with the given search 'path' - a list of @@ -414,14 +226,10 @@ The optional 'verbose' parameter causes output concerning the activities of the object to be produced if set to a true value (not the default). - - The optional 'optimisations' cause certain techniques to be used in - reducing program size and improving program efficiency. """ self.path = path or [os.getcwd()] self.verbose = verbose - self.optimisations = optimisations or set() self.modules = {} self.modules_ordered = [] diff -r 979e2a19fc4a -r b772a168a2ba micropython/ast.py --- a/micropython/ast.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,941 +0,0 @@ -#!/usr/bin/env python - -""" -Translate the AST of a Python program into a more interpretable representation. - -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from micropython.common import * -from micropython.data import * -from micropython.errors import * -from micropython.rsvp import * -from micropython.trans import Helper -from micropython.code import Assembler -import compiler.ast - -# Program visitors. - -class Translation(ASTVisitor, Assembler, Helper): - - "A module translator." - - # Attribute access instructions, for use with the appropriate handlers. - - attribute_load_instructions = ( - LoadAddress, LoadAddressContext, LoadAddressContextCond, - LoadAttr, LoadAttrIndex, LoadAttrIndexContextCond - ) - # attribute_store_instructions are defined by the optimiser - - # Name access instructions, for use with the appropriate handlers. - - name_load_instructions = (LoadName, LoadAddress, None) - name_store_instructions = (StoreName, StoreAddress, StoreAddressContext) - - def __init__(self, module, program): - - """ - Initialise the translation with an inspected 'module' and the 'program' - container. - """ - - Assembler.__init__(self, program) - self.visitor = self - self.module = module - - # Global program dependencies. - - self.native = program.native - self.objtable = self.program.get_object_table() - self.paramtable = self.program.get_parameter_table() - self.importer = self.program.get_importer() - self.builtins = self.importer.modules.get("__builtins__") - - # Status flags. - - self.in_exception_handler = False - self.in_assignment = False # for slicing and subscript - - # Reset the assembler. - - self.reset() - - def __repr__(self): - return "Translation(%r)" % self.module - - def get_unit(self): - return self.unit - - def get_module_code(self): - - """ - Return the top-level module code. - """ - - self.unit = self.module - self.reset() - self.add_exception_unit() - - block = self.new_block() - self.set_block(block) - - # Handle exceptions for the program. - - if self.module.name == "__main__": - handler_block = self.new_block() - self.new_op(PushHandler(handler_block)) - - # Generate code for the module. - - if self.module.astnode is not None: - self.dispatch(self.module.astnode) - - # Finish off the translated program if appropriate. - - if self.module.name == "__main__": - self.set_block(handler_block) - self.new_op(PopHandler(1)) - - # All modules return if invoked. - - self.new_op(Return()) - - self.drop_exception_unit() - self.unit.temp_usage = self.max_temp_position + 1 - self.unit.blocks = self.blocks - return self.blocks - - def get_code(self, unit): - - "Return the code for the given 'unit'." - - self.unit = unit - self.reset() - self.add_exception_unit() - - block = self.new_block() - self.set_block(block) - - if unit.astnode is not None: - self.dispatch(unit.astnode) - - self.drop_exception_unit() - self.unit.temp_usage = self.max_temp_position + 2 # include space for instantiators to expand backwards - self.unit.blocks = self.blocks - return self.blocks - - def get_instantiator_code(self, cls): - - "Return the code for the given class 'cls'." - - # Obtain the function object to be populated. - - self.unit = cls.get_instantiator() - self.reset() - - block = self.new_block() - self.set_block(block) - - init_method = cls.get_init_method() - - # Make an object and store it in the unused first slot. - - self.make_instance(cls, len(cls.instance_attributes())) - self.new_op(StoreTemp(0)) - - # Invoke the appropriate initialiser. - - self.new_op(LoadFunction(init_method)) - self.new_op(LoadCallable()) - self.new_op(JumpInFrame()) - - # Store the object as the result. - - self.new_op(LoadTemp(0)) # load the context from the locals - self.new_op(Return()) - - self.unit.blocks = self.blocks - return self.blocks - - # Visitor methods. - - def default(self, node, *args): - raise TranslateError("Node class %r is not supported." % node.__class__) - - # Concrete visitor methods. - - # Binary operators. - - visitBitand = \ - visitBitor = \ - visitBitxor = Helper._visitBinaryBit - - visitAdd = \ - visitDiv = \ - visitFloorDiv = \ - visitLeftShift = \ - visitMod = \ - visitMul = \ - visitPower = \ - visitRightShift = \ - visitSub = Helper._visitBinary - - # Unary operators. - - visitInvert = \ - visitUnaryAdd = \ - visitUnarySub = Helper._visitUnary - - # Logical operators. - - def visitAnd(self, node): - end_block = self.new_block() - temp_pos = self.reserve_temp() - - for n in node.nodes[:-1]: - self.dispatch(n) - self.new_op(StoreTemp(temp_pos)) - - self._generateTestBoolean(n, LoadTemp(temp_pos)) - self.new_op(JumpIfFalse(end_block, working="status")) - - self.dispatch(node.nodes[-1]) - self.new_op(StoreTemp(temp_pos)) - - self.set_block(end_block) - - temp = LoadTemp(temp_pos) - self.new_op(temp) - self.discard_temp(temp) - - def visitNot(self, node): - self.dispatch(node.expr) - - temp = self.optimiser.optimise_temp_storage() - self.new_op(temp) - self._generateTestBoolean(node.expr, temp) - self.discard_temp(temp) - - self.new_op(InvertBoolean(source="status", target="status")) - self._generateLoadBoolean(node) - - def visitOr(self, node): - end_block = self.new_block() - temp_pos = self.reserve_temp() - - for n in node.nodes[:-1]: - self.dispatch(n) - self.new_op(StoreTemp(temp_pos)) - - self._generateTestBoolean(n, LoadTemp(temp_pos)) - self.new_op(JumpIfTrue(end_block, working="status")) - - self.dispatch(node.nodes[-1]) - self.new_op(StoreTemp(temp_pos)) - - self.set_block(end_block) - - temp = LoadTemp(temp_pos) - self.new_op(temp) - self.discard_temp(temp) - - # Comparisons. - - def visitCompare(self, node): - - """ - _t1 = node.expr - _t1 op1 _t2 and _t2 op2 _t3 and ... - """ - - end_block = self.new_block() - - self.dispatch(node.expr) - temp2 = self.optimiser.optimise_temp_storage() - - # NOTE: Replicated by some code in micropython.inspect.visitCompare. - - last_op = node.ops[-1] - - for op in node.ops: - op_name, next_node = op - operator_fn = operator_functions.get(op_name) - - # Propagate the arguments as we traverse the construct. - - temp1 = temp2 - self.dispatch(next_node) - temp2 = self.optimiser.optimise_temp_storage() - - # Use the appropriate mechanism, setting the boolean status for the - # comparison. - - if operator_fn is not None: - - # Generate function call using evaluated argument and next node. - - temp_fn = self._generateOperatorFunction(op_name) - self._generateInvocation(temp_fn, (temp1, temp2)) - self.discard_temp(temp_fn) - - temp_result = self.optimiser.optimise_temp_storage() - self._generateTestBoolean(node, temp_result) - self.discard_temp(temp_result) - - else: - # Deal with the special operators. - - if op_name.startswith("is"): - self.new_op(temp1) - self.record_value() - self.start_target() - self.new_op(temp2) - self.new_op(TestIdentity(target="status")) - self.assign_value() - self.discard_value() - - elif op_name.endswith("in"): - self.new_op(temp2) - - # Get method on temp2. - - self._generateAttr(node, "__contains__", self.attribute_load_instructions) - temp_method = self.optimiser.optimise_temp_storage() - - # Add arguments. - # NOTE: No support for defaults. - - self._generateInvocation(temp_method, (temp2, temp1)) - - temp_result = self.get_temp() - self._generateTestBoolean(node, temp_result) - self.discard_temp(temp_result) - - if op_name.find("not") != -1: - self.new_op(InvertBoolean(source="status", target="status")) - - # Test the result and jump to the end block if false. - - if op is not last_op: - self.new_op(JumpIfFalse(end_block, working="status")) - - # Compilation duties... - - self.discard_temp(temp1) - - self.discard_temp(temp2) - - # With the status set above, produce a boolean result. - - self.set_block(end_block) - - # Yield the appropriate value. - - self._generateLoadBoolean(node) - - # Expressions. - - def visitBackquote(self, node): raise TranslationNotImplementedError("Backquote") - - def visitCallFunc(self, node): - - """ - Evaluate positional arguments, evaluate and store keyword arguments in - the correct location, then invoke the function. - """ - - # Mark the frame, evaluate the target, generate the call. - - self._startCallFunc() - self.dispatch(node.node) - temp_target, target, temp_context = self._generateCallFunc(node.args, node) - self._doCallFunc(temp_target, target) - self._endCallFunc(temp_target, temp_context) - - def visitConst(self, node): - const = self.importer.get_constant(node.value) - self.new_op(LoadConst(const)) - - def visitDict(self, node): raise TranslationNotImplementedError("Dict") - - def visitEllipsis(self, node): raise TranslationNotImplementedError("Ellipsis") - - def visitExec(self, node): raise TranslationNotImplementedError("Exec") - - def visitExpression(self, node): raise TranslationNotImplementedError("Expression") - - def visitGenExpr(self, node): raise TranslationNotImplementedError("GenExpr") - - def visitGenExprFor(self, node): raise TranslationNotImplementedError("GenExprFor") - - def visitGenExprIf(self, node): raise TranslationNotImplementedError("GenExprIf") - - def visitGenExprInner(self, node): raise TranslationNotImplementedError("GenExprInner") - - def visitGetattr(self, node): - self._visitAttr(node, self.attribute_load_instructions) - - def visitList(self, node): - self._generateList(node, node.nodes) - - def visitListComp(self, node): - self._generateList(node) - temp_list = self.optimiser.optimise_temp_storage() - - # Get the list's append method. - - self._generateAttr(node, "append", self.attribute_load_instructions) - temp_method = self.optimiser.optimise_temp_storage() - - self.visitListCompFor(node.quals[0], node.quals[1:], node.expr, temp_list, temp_method) - - # Generate a reference to the list. - - self.new_op(temp_list) - - self.discard_temp(temp_method) - self.discard_temp(temp_list) - - def visitListCompFor(self, node, following_quals, expr, temp_list, temp_method): - temp_iterator, next_block, exit_block, else_block = self._startFor(node) - - # Explicit dispatch to tests, following loops, expression. - - if node.ifs: - self.visitListCompIf(node.ifs[0], node.ifs[1:], following_quals, expr, temp_list, temp_method) - - # Explicit dispatch to following loops, expression. - - elif following_quals: - self.visitListCompFor(following_quals[0], following_quals[1:], expr, temp_list, temp_method) - - # Explicit dispatch to the expression. - - else: - self.dispatch(expr) - - # Append the value to the list. - - temp_value = self.optimiser.optimise_temp_storage() - self._generateInvocation(temp_method, (temp_list, temp_value,)) - - self._endFor(node, temp_iterator, next_block, exit_block, else_block) - - def visitListCompIf(self, node, following_tests, following_quals, expr, temp_list, temp_method): - exit_block = self.new_block() - - # Evaluate the test and jump beyond the body if it is not satisfied. - - self.dispatch(node.test) - - temp = self.optimiser.optimise_temp_storage() - self.new_op(temp) - self._generateTestBoolean(node, temp) - self.new_op(JumpIfFalse(exit_block, working="status")) - - # Explicit dispatch to tests, following loops, expression. - - if following_tests: - self.visitListCompIf(following_tests[0], following_tests[1:], following_quals, expr, temp_list, temp_method) - - # Explicit dispatch to following loops, expression. - - elif following_quals: - self.visitListCompFor(following_quals[0], following_quals[1:], expr, temp_list, temp_method) - - # Explicit dispatch to the expression. - - else: - self.dispatch(expr) - - # Append the value to the list. - - temp_value = self.optimiser.optimise_temp_storage() - self._generateInvocation(temp_method, (temp_list, temp_value,)) - - self.set_block(exit_block) - - def visitName(self, node): - self._visitName(node, self.name_load_instructions) - - def visitSlice(self, node): - args = [node.expr] - - if node.lower is None: - args.append(compiler.ast.Name("None")) - if node.upper is None: - args.append(compiler.ast.Name("None")) - else: - args.append(node.upper) - else: - args.append(node.lower) - if node.upper is None: - args.append(compiler.ast.Name("None")) - else: - args.append(node.upper) - - temp_fn = self._getOperatorFunction(node, self.in_assignment and "AssSlice" or "Slice") - self._visitCall(node, temp_fn, args) - self.discard_temp(temp_fn) - - def visitSubscript(self, node): - temp_fn = self._getOperatorFunction(node, self.in_assignment and "AssSubscript" or "Subscript") - self._visitCall(node, temp_fn, [node.expr] + node.subs) - self.discard_temp(temp_fn) - - def visitTuple(self, node): - self._generateTuple(node) - - # Definitions. - - def visitAssign(self, node): - - """ - Evaluate the expression from the given 'node' and assign it to the - associated recipients. - """ - - self.dispatch(node.expr) - - # Record the value and then dispatch to the assignment targets. - - self.record_value(self.has_immediate_usage(node.nodes)) - - self.in_assignment = True - - for n in node.nodes: - self.dispatch(n) - - self.in_assignment = False - self.discard_value() - - def visitAssAttr(self, node): - - "Assign the assignment expression to the recipient 'node'." - - self.start_target() - self._visitAttr(node, self.optimiser.get_attribute_store_instructions()) - self.assign_value() - - def visitAssList(self, node): - - """ - Assign items from the assignment expression to each of the recipients - found within the given 'node'. - """ - - self.new_op(self.expr_temp[-1]) - self._generateAttr(node, "__getitem__", self.attribute_load_instructions) - temp_getitem = self.optimiser.optimise_temp_storage() - - for i, n in enumerate(node.nodes): - self._startCallFunc() - self.new_op(temp_getitem) - temp_target, target, temp_context = self._generateCallFunc([compiler.ast.Const(i)], node) - self._doCallFunc(temp_target, target) - self._endCallFunc(temp_context=temp_context) - - # Provide a different source value. - # NOTE: Permitting immediate usage given that neither name nor - # NOTE: attribute accesses should involve a function call - # NOTE: overwriting the above result. - - self.record_value(self.is_immediate_user(n)) - self.dispatch(n) - self.discard_value() - - self.discard_temp(temp_getitem) - - def visitAssName(self, node): - - "Assign the assignment expression to the recipient 'node'." - - if node.flags == "OP_DELETE": - raise TranslationNotImplementedError("AssName(OP_DELETE)") - self._visitAssName(node) - - def _visitAssName(self, node): - self.start_target() - self._visitName(node, self.name_store_instructions) - self.assign_value() - - # Add any attribute usage guards. - - self._generateGuards(node) - - visitAssTuple = visitAssList - - def visitAugAssign(self, node): - - # Find the augmented assignment function and attempt to use it. - - temp_fn = self._getOperatorAugAssignFunction(node) - self._visitCall(node, temp_fn, (node.node, node.expr)) - self.discard_temp(temp_fn) - - # Assign the result to the name. - - self.record_value(1) - - if isinstance(node.node, compiler.ast.Name): - self._visitAssName(node.node) - elif isinstance(node.node, compiler.ast.Getattr): - self.visitAssAttr(node.node) - else: - raise TranslationNotImplementedError("AugAssign(Slice or Subscript)") - - self.discard_value() - - def visitClass(self, node): - if not used_by_unit(node): - return - - # Store the name. - - self.new_op(LoadClass(node.unit)) - self._storeName(node) - - # Visit the code. - - unit = self.unit - self.unit = node.unit - self.dispatch(node.code) - self.unit = unit - - def visitDecorators(self, node): raise TranslationNotImplementedError("Decorators") - - def visitFrom(self, node): - self._visitImport(node) - - # Store each imported name if its reference is not set as a constant - # module global. - - module = self.importer.get_module(node.modname) - - for name, alias in node.names: - if name != "*": - self._importName(module, name, alias, node) - else: - for attrname in module.module_attributes().keys(): - self._importName(module, attrname, None, node) - - def visitFunction(self, node): - if not used_by_unit(node): - return - - # Only store the name when visiting this node from outside. - - if self.unit is not node.unit: - self._visitFunctionDeclaration(node) - - # Record the declared function. - - self._storeName(node) - - # Visiting of the code occurs when get_code is invoked on this node. - - else: - self._visitFunctionDefinition(node) - - def visitGlobal(self, node): pass - - def visitImport(self, node): - self._visitImport(node) - - for name, alias in node.names: - module = self.importer.get_module(name) - self.new_op(LoadConst(module)) - self._storeName(node, alias or name) - - def visitKeyword(self, node): pass - - def visitLambda(self, node): - - """ - Lambda functions can be represented as globally defined functions - provided they do not define any default parameter values, since these - may defined in a non-global scope. - - Where defaults are defined, an object must be created and its content - defined: the callable member of the object's structure must be set to - the lambda function definition; each default must be attached to the - object as an attribute, as is the case with normal functions and - methods. - """ - - # Produce the reference to this function when visiting this node from - # outside. - - if self.unit is not node.unit: - - # Provide the declared function. - - self._visitFunctionDeclaration(node) - - # Visiting of the code occurs when get_code is invoked on this node. - - else: - self._visitFunctionDefinition(node) - - def visitModule(self, node): - extend = ExtendFrame() - self.new_op(extend) - self.dispatch(node.node) - self.set_frame_usage(node, extend) - - # Statements. - - def visitStmt(self, node): - - "Process the collection of statements provided by 'node'." - - for n in node.nodes: - - # Process the statement. - - self.dispatch(n) - - # Prevent incorrect optimisation by resetting the optimiser after - # each statement. - - self.optimiser.reset() - - def visitAssert(self, node): raise TranslationNotImplementedError("Assert") - - def visitBreak(self, node): - next_block, exit_block = self.get_loop_blocks() - self.new_op(Jump(exit_block)) - - def visitContinue(self, node): - next_block, exit_block = self.get_loop_blocks() - self.new_op(Jump(next_block)) - - def visitDiscard(self, node): - self.dispatch(node.expr) - self.optimiser.optimise_unused_results() - - def visitFor(self, node): - temp_iterator, next_block, exit_block, else_block = self._startFor(node, node.else_) - self.dispatch(node.body) - self._endFor(node, temp_iterator, next_block, exit_block, else_block, node.else_) - - def visitIf(self, node): - first = True - next_block = None - exit_block = self.new_block() - - clauses = node.tests + [(None, node.else_)] - - for clause in clauses: - test, body = clause - if body is None: - break - - if not first: - self.new_op(Jump(exit_block)) # finish last body - self.set_block(next_block) # start next test - next_block = None - - if test is not None: - self.dispatch(test) - - temp = self.optimiser.optimise_temp_storage() - self.new_op(temp) - self._generateTestBoolean(node, temp) - - next_block = self.new_block() - self.new_op(JumpIfFalse(next_block, working="status")) - - self.dispatch(body) - first = False - - if next_block is not None: - self.set_block(next_block) - - self.set_block(exit_block) - - def visitIfExp(self, node): raise TranslationNotImplementedError("IfExp") - - def visitPass(self, node): pass - - def visitPrint(self, node): - self._visitPrint(node, "_print") - - def visitPrintnl(self, node): - self._visitPrint(node, "_printnl") - - def visitRaise(self, node): - - if node.expr1 is not None: - - # NOTE: expr1 only => instance provided - - self.dispatch(node.expr1) - - if node.expr2 is not None: - temp = self.optimiser.optimise_temp_storage() - - self.dispatch(node.expr2) - temp_arg = self.optimiser.optimise_temp_storage() - - self._generateInvocation(temp, (temp_arg,)) - - self.discard_temp(temp_arg) - - self.new_op(Transfer(source="working", target="exception")) - - self.new_op(RaiseException()) - - def visitReturn(self, node): - if node.value is not None: - self.dispatch(node.value) - else: - self.dispatch(compiler.ast.Name("None")) - - if self.in_exception_handler: - self.new_op(ClearException(target="exception")) - - # NOTE: Support finally blocks here. - - if self.exception_blocks[-1]: - self.new_op(PopHandler(len(self.exception_blocks[-1]))) - - self.new_op(Return()) - - def visitTryExcept(self, node): - exit_block = self.new_block() - else_block = self.new_block() - handler_block = self.new_block() - - self.add_exception_blocks(handler_block, exit_block) - - # Try... - # Produce the code, then jump to the exit. - - self.new_op(PushHandler(handler_block)) - self.dispatch(node.body) - self.new_op(PopHandler(1)) - - if node.else_ is not None: - self.new_op(Jump(else_block)) - else: - self.new_op(Jump(exit_block)) - - # Start of handlers. - - self.set_block(handler_block) - self.new_op(PopHandler(1)) - - # Disable the handlers. - - self.drop_exception_blocks() - - for name, assignment, handler in node.handlers: - next_block = self.new_block() - - # Test the given exception against the current exception. - - if name is not None: - self.dispatch(name) - - self.new_op(CheckException(target="status")) - self.new_op(JumpIfFalse(next_block, working="status")) - - # Handle assignment to exception variable. - - if assignment is not None: - self.new_op(Transfer(source="working", target="exception")) - - # Record the value to be assigned. - - self.record_value() - self.dispatch(assignment) - self.discard_value() - - # Produce the handler code, then jump to the exit. - - self.in_exception_handler = True - self.dispatch(handler) - self.in_exception_handler = False - - self.new_op(Jump(exit_block)) - - self.set_block(next_block) - - # Unhandled exceptions. - - self.new_op(RaiseException()) - - # Optional else clause. - - if node.else_ is not None: - self.set_block(else_block) - self.dispatch(node.else_) - - # Clear the exception. - - self.set_block(exit_block) - self.new_op(ClearException(target="exception")) - - def visitTryFinally(self, node): - - """ - Add finally handler, potentially as an exception handler. - Generate body, potentially changing return statements so that they do - not return immediately. - Generate handler, removing the handler from the active handler list, - adding instructions which raise active exceptions. - """ - - raise TranslationNotImplementedError("TryFinally") - - def visitWhile(self, node): - exit_block = self.new_block() - next_block = self.new_block() - else_block = self.new_block() - - self.set_block(next_block) - self.dispatch(node.test) - - temp = self.optimiser.optimise_temp_storage() - self.new_op(temp) - self._generateTestBoolean(node, temp) - - if node.else_ is not None: - self.new_op(JumpIfFalse(else_block, working="status")) - else: - self.new_op(JumpIfFalse(exit_block, working="status")) - - self.add_loop_blocks(next_block, exit_block) - - self.dispatch(node.body) - self.new_op(Jump(next_block)) - - if node.else_ is not None: - self.set_block(else_block) - - self.dispatch(node.else_) - - self.set_block(exit_block) - - self.drop_loop_blocks() - - def visitWith(self, node): raise TranslationNotImplementedError("With") - - def visitYield(self, node): raise TranslationNotImplementedError("Yield") - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba micropython/cmd.py --- a/micropython/cmd.py Fri Jun 28 21:17:02 2013 +0200 +++ b/micropython/cmd.py Sat Jun 29 01:28:12 2013 +0200 @@ -4,7 +4,7 @@ Command option processing and other utilities for compiling and testing programs. -Copyright (C) 2009 Paul Boddie +Copyright (C) 2009, 2013 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -23,36 +23,14 @@ from os.path import exists, join import micropython -def parse_optimisations(args): - - "Parse 'args' for optimisation flags." - - if "-omax" in args: - requested_optimisations = micropython.Program.supported_optimisations - else: - requested_optimisations = [] - for arg in args: - if arg.startswith("-o"): - for arg_part in arg[2:].split(","): - requested_optimisations.append(arg_part) - - return requested_optimisations - -def show_optimisations(): - - "Show available optimisation flags." - - return ",".join(micropython.Program.supported_optimisations) - -def program(path, requested_optimisations, verbose=0): +def program(path, verbose=0): """ - Return a program object for the given module search 'path' and - 'requested_optimisations'. + Return a program object for the given module search 'path'. """ - i = micropython.Importer(path, verbose, requested_optimisations) - p = micropython.Program(i, requested_optimisations) + i = micropython.Importer(path, verbose) + p = micropython.Program(i) for d in path: builtins = join(d, "builtins.py") @@ -70,7 +48,6 @@ 'args'. """ - requested_optimisations = parse_optimisations(args) - return program(path, requested_optimisations, "-v" in args) + return program(path, "-v" in args) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba micropython/code.py --- a/micropython/code.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,492 +0,0 @@ -#!/usr/bin/env python - -""" -Generate low-level code. - -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from micropython.opt import Optimiser -from micropython.data import * -from micropython.errors import * -from micropython.rsvp import * -import compiler.ast - -class Assembler: - - "Support for generating low-level code." - - def __init__(self, program): - - """ - Initialise the assembler with a program, optimiser and status - attributes. - """ - - self.program = program - - # Optimisation. - - self.optimiser = Optimiser(self, program.optimisations) - - # The current unit being translated. - - self.unit = None - - # The temporary storage used by the current assignment expression. - - self.expr_temp = [] - - # The start of the current assignment target instructions. - - self.target_start = None - - # Wiring within the code. - - self.labels = {} - self.label_number = 0 - self.loop_blocks = [] - self.exception_blocks = [] - - def reset(self): - - "Reset the state of the assembler." - - # The code itself. This is limited to the code for a particular block - # being processed. - - self.blocks = [] - - # Information about temporary values. - - self.temp_positions = set() - self.max_temp_position = -1 - - # Information about instructions which construct frames. - - self.frame_makers = [] - - # Optimiser state must be reset for each unit. - - self.optimiser.reset() - - def new_block(self): - - "Return a new code block." - - return Block(self.unit) - - def get_block(self): - - "Return the current block." - - return self.blocks[-1] - - def set_block(self, block, preceding=None): - - """ - Add the given 'block' to the unit's list of blocks, noting any active - value information from 'preceding' blocks on the new block. - """ - - self.optimiser.reset(block, preceding) - self.blocks.append(block) - - def get_loop_blocks(self): - return self.loop_blocks[-1] - - def add_loop_blocks(self, next_block, exit_block): - self.loop_blocks.append((next_block, exit_block)) - - def drop_loop_blocks(self): - self.loop_blocks.pop() - - def add_exception_unit(self): - self.exception_blocks.append([]) - - def get_exception_blocks(self): - return self.exception_blocks[-1][-1] - - def add_exception_blocks(self, handler_block, exit_block): - self.exception_blocks[-1].append((handler_block, exit_block)) - - def drop_exception_blocks(self): - self.exception_blocks[-1].pop() - - def drop_exception_unit(self): - self.exception_blocks.pop() - - # Assignment expression values. - - def record_value(self, immediate=1): - - """ - Record the current active value for an assignment. If the optional - 'immediate' parameter is set to a false value, new temporary storage to - hold the recorded value will be allocated; otherwise, the - value-providing instruction may be replicated in order to provide the - active value later on. - """ - - if immediate: - temp = self.optimiser.optimise_temp_storage() - else: - temp = self.get_temp() - self.expr_temp.append(temp) - - def discard_value(self): - - "Discard any temporary storage in use for the current assignment value." - - self.discard_temp(self.expr_temp.pop()) - - def start_target(self): - - """ - Start recording instructions used to define the target of an assignment. - """ - - self.target_start = len(self.blocks[-1]) - - def remove_target_ops(self): - - "Remove the assignment target instructions." - - del self.blocks[-1].code[self.target_start:] - self.target_start = None - self.optimiser.clear_active() - - def get_target_ops(self): - - "Return the assignment target instructions." - - return self.blocks[-1].code[self.target_start:] - - def assign_value(self, expr=None): - - """ - Set the source of an assignment using 'expr' or the current assignment - value. This may set the source register of the current instruction. - """ - - expr = expr or self.expr_temp[-1] - - # Optimise away constant storage if appropriate. - - if self.optimiser.optimise_constant_storage(expr): - self.remove_target_ops() - return - - # Otherwise, insert the assignment source. - - expr_copy = expr.copy() - assign_ops = self.get_target_ops() - - if not assign_ops: - self.target_start = None - return - - assign_op = assign_ops[-1] - - # Either insert the instruction yielding the value and adjust the - # assignment source. - - expr_copy.target = "source" - - if self.insert_op(-1, expr_copy): - assign_op.source = "source" - self.update_temp(expr, expr_copy) - - # (Now, the instruction need not be inserted.) - - # Or transfer the working value to the source register. - - elif assign_op.working == "working": - self.insert_op(-1, Transfer(source="working_context", target="source_context")) - self.insert_op(-1, Transfer(source="working", target="source")) - assign_op.source = "source" - - # Or let the assignment use the working register. - - else: - assign_op.source = "working" - - self.target_start = None - - def set_target(self, target): - - "Reset the target of the active instruction to 'target'." - - self.optimiser.set_target("working", target) - - def is_immediate_user(self, node): - - """ - Return whether 'node' is an immediate user of an assignment expression. - """ - - return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr)) - - def has_immediate_usage(self, nodes): - - """ - Return whether 'nodes' are all immediate users of an assignment expression. - """ - - for n in nodes: - if not self.is_immediate_user(n): - return False - return True - - # Temporary storage administration. - - def get_temp(self): - - """ - Return a temporary storage access instruction for the current value. - Initially, this instruction is not associated with an allocated unit of - temporary storage, and if used as a new instruction will not be added to - the code, but if the current value changes, the 'set_temp' method will - be called by the optimiser and a unit of storage will be allocated. - """ - - temp = LoadTemp(None) - self.optimiser.request_active_value("working", temp, self.blocks[-1], len(self.blocks[-1].code)) - return temp - - def set_temp(self, temp, block, pos): - - """ - Emit a storage instruction for the given 'temp' loading instruction, - reserving a new temporary storage location. - """ - - if temp.attr is None: - temp.attr = self.reserve_temp() - block.insert(pos, StoreTemp(temp.attr)) - - def update_temp(self, temp, updated): - - "Update 'temp' using the given 'updated' instruction." - - if isinstance(temp, LoadTemp): - temp.attr = updated.attr - - def reserve_temp(self, temp_position=None): - - """ - Reserve a new temporary storage position, or if the optional - 'temp_position' is specified, ensure that this particular position is - reserved. - """ - - if temp_position is not None: - pass - elif not self.temp_positions: - temp_position = 0 - else: - temp_position = max(self.temp_positions) + 1 - - self.temp_positions.add(temp_position) - self.max_temp_position = max(self.max_temp_position, temp_position) - return self.unit.all_local_usage + temp_position # position in frame - - def ensure_temp(self, instruction=None): - - """ - Ensure that the 'instruction' is using a reserved temporary storage - position. - """ - - if isinstance(instruction, LoadTemp): - temp_position = instruction.attr - self.unit.all_local_usage - self.reserve_temp(temp_position) - - def discard_temp(self, instruction=None): - - "Discard any temporary storage position used by 'instruction'." - - if isinstance(instruction, LoadTemp) and instruction.attr is not None: - temp_position = instruction.attr - self.unit.all_local_usage - self.free_temp(temp_position) - - # Prevent any requested active value from generating instructions now - # that our interest in it has passed. - - self.optimiser.ignore_active_value("working") - - def free_temp(self, temp_position): - - "Free the temporary storage position specified by 'temp_position'." - - if temp_position in self.temp_positions: - self.temp_positions.remove(temp_position) - - def set_frame_usage(self, node, extend): - - """ - Ensure that the frame usage for the unit associated with 'node' is set - on the 'extend' instruction. - """ - - # Remove any ExtendFrame instructions which do nothing. - - if self.last_op() is extend: - self.remove_op() - return - - ntemp = self.max_temp_position + 1 - extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. - - # Code writing methods. - - def new_op(self, op): - - """ - Add 'op' to the generated code, returning a true value if an instruction - was added. - """ - - if not self.check_op(op): - return False - - # Add the operation to the current block. - - self.optimiser.set_new("working", op) - self.blocks[-1].append(op) - return True - - def insert_op(self, i, op): - - "Insert at index 'i' in the current block the instruction 'op'." - - if not self.check_op(op): - return False - - self.blocks[-1].insert(i, op) - return True - - def check_op(self, op): - - "Return whether the given 'op' is to be added to the code." - - # Optimise away temporary storage instructions where the active value is - # still available and was not recorded. - - if isinstance(op, LoadTemp) and op.attr is None: - return False - - # Optimise load operations employed by this instruction. - - if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): - return False - - return True - - def remove_op(self): - - "Remove the last instruction." - - op = self.blocks[-1].code.pop() - self.optimiser.clear_active() - return op - - def replace_op(self, op): - - "Replace the last added instruction with 'op'." - - self.remove_op() - self.new_op(op) - - def replace_active_value(self, register, op): - - """ - Replace the active instruction providing 'register' with its value with - the given 'op' if appropriate. - """ - - removed = self.optimiser.remove_active_value(register) - self.new_op(op) - return removed - - def last_op(self): - - "Return the last added instruction." - - try: - return self.blocks[-1].code[-1] - except IndexError: - return None - - # Allocation-related methods. - - def make_instance(self, cls, n): - - """ - Request a new instance using the given class 'cls' and with 'n' - attributes. - """ - - # Load the class in order to locate the instance template. - - self.new_op(LoadConst(cls)) - - # NOTE: Instance headers are one location. - - self.new_op(MakeInstance(n + 1)) - - def make_exception(self, name): - - "Make an exception of the given 'name' using 'node'." - - # NOTE: Reserving an attribute. - - self.make_instance(self.get_builtin_class(name), 1) - - # Name-related methods. - - def get_scope(self, name): - - "Return the scope for the given 'name'." - - attr, scope, from_name = self.unit._get_with_scope(name) - return scope - - def load_builtin(self, name, node): - - "Generate an instruction loading 'name' for the given 'node'." - - self.new_op(LoadAddress(self.get_builtin(name))) - - def get_builtin_class(self, name): - - "Return the built-in class with the given 'name'." - - return self.get_builtin(name).get_value() - - def get_builtin(self, name): - - "Return the built-in module definition for the given 'name'." - - if self.builtins is not None: - try: - return self.builtins[name] - except KeyError: - raise TranslateError("No __builtins__ definition is available for name %r." % name) - else: - raise TranslateError("No __builtins__ module is available for name %r." % name) - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba micropython/inspect.py --- a/micropython/inspect.py Fri Jun 28 21:17:02 2013 +0200 +++ b/micropython/inspect.py Sat Jun 29 01:28:12 2013 +0200 @@ -115,7 +115,6 @@ # Import machinery links. - self.optimisations = self.importer.optimisations self.builtins = self.importer.modules.get("__builtins__") self.loaded = False self.completed = False @@ -316,13 +315,12 @@ names. """ - if self.should_optimise_unused_objects(): - self.vacuum_object(self) + self.vacuum_object(self) - all_objects = list(self.all_objects) + all_objects = list(self.all_objects) - for obj in all_objects: - self.vacuum_object(obj) + for obj in all_objects: + self.vacuum_object(obj) def vacuum_object(self, obj, delete_all=0): @@ -399,11 +397,6 @@ if any_scope or not (self.namespaces and isinstance(self.namespaces[-1], Function)): self.all_objects.add(obj) - # Optimisation tests. - - def should_optimise_unused_objects(self): - return "unused_objects" in self.optimisations - # Namespace methods. def in_class(self, namespaces=None): diff -r 979e2a19fc4a -r b772a168a2ba micropython/native.py --- a/micropython/native.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -#!/usr/bin/env python - -""" -Native library generation. - -Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from micropython.code import Assembler -from micropython.rsvp import * - -class NativeLibrary(Assembler): - - "A native library generator providing common routines." - - def __init__(self, program): - - """ - Initialise a native library for the given 'program'. - """ - - Assembler.__init__(self, program) - - self.importer = self.program.get_importer() - self.builtins = None - - self.unknown_target_block = None - self.unknown_target_testable_self_block = None - self.known_target_testable_self_block = None - - # Reset the assembler. - - self.reset() - - def get_native_code(self): - self.builtins = self.importer.modules.get("__builtins__") - - if self.unknown_target_block is not None: - self._generateCallFuncUnknownTarget() - - if self.unknown_target_testable_self_block is not None: - self._generateCallFuncUnknownTargetTestableSelf() - - if self.known_target_testable_self_block is not None: - self._generateCallFuncKnownTargetTestableSelf() - - return self.blocks - - def _commonCallFunc(self, adjust_block, continue_block): - self.new_op(CheckContext(working="source", target="status")) - self.new_op(JumpIfFalse(adjust_block, working="status")) - - # Skip adjustment and tests if the context is not a class. - # Classes themselves employ a placeholder context so that - # instantiators can be callable with a context which will be - # overwritten in the frame. - - # Here, the source value should still refer to the context. - - self.new_op(CheckClass(working="source", target="status")) - self.new_op(JumpIfFalse(continue_block, working="status")) - - def _commonTestContext(self, success_block): - self.new_op(CheckInstance(source="source", target="status")) - self.new_op(JumpIfTrue(success_block, working="status")) - - # Where the context is inappropriate, drop the incomplete frame and - # raise an exception. - - self.new_op(DropFrame()) - - self.make_exception("TypeError") - self.set_target("exception") - self.new_op(RaiseException()) - - def _commonAdjustFrame(self, adjust_block, continue_block): - self.set_block(adjust_block) - self.new_op(AdjustFrame(1)) - self.set_block(continue_block) - - def getCallFuncUnknownTarget(self): - if self.unknown_target_block is None: - self.unknown_target_block = self.new_block() - - return self.unknown_target_block - - def _generateCallFuncUnknownTarget(self): - - """ - Some preliminary tests where the target of an invocation is not known. - Requires the context in the working value register. - """ - - adjust_block = self.new_block() - continue_block = self.new_block() - - self.set_block(self.unknown_target_block) - self._commonCallFunc(adjust_block, continue_block) - self._commonAdjustFrame(adjust_block, continue_block) - self.new_op(Return()) - - def getCallFuncUnknownTargetTestableSelf(self): - if self.unknown_target_testable_self_block is None: - self.unknown_target_testable_self_block = self.new_block() - - return self.unknown_target_testable_self_block - - def _generateCallFuncUnknownTargetTestableSelf(self): - - """ - Test any explicit first argument against the context. - """ - - adjust_block = self.new_block() - continue_block = self.new_block() - - self.set_block(self.unknown_target_testable_self_block) - self._commonCallFunc(adjust_block, continue_block) - self._commonTestContext(adjust_block) - self._commonAdjustFrame(adjust_block, continue_block) - self.new_op(Return()) - - def getCallFuncKnownTargetTestableSelf(self): - if self.known_target_testable_self_block is None: - self.known_target_testable_self_block = self.new_block() - - return self.known_target_testable_self_block - - def _generateCallFuncKnownTargetTestableSelf(self): - - """ - Test any explicit first argument against the context. - """ - - adjust_block = self.new_block() - continue_block = self.new_block() - - self.set_block(self.known_target_testable_self_block) - self._commonTestContext(continue_block) - self._commonAdjustFrame(adjust_block, continue_block) - self.new_op(Return()) - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba micropython/opt.py --- a/micropython/opt.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,538 +0,0 @@ -#!/usr/bin/env python - -""" -Optimise code produced by the AST translator. - -Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from micropython.common import * -from micropython.data import * -from micropython.rsvp import * - -class Optimiser: - - "A code optimiser." - - supported_optimisations = [ - # Code generation optimisations: - "constant_storage", "constant_accessor", "known_target", "self_access", - "temp_storage", "no_operations", "unused_results", - "unused_handlers", "accesses_by_usage", - # Inspection optimisations: - "unused_objects" - ] - - def __init__(self, translation, optimisations=None): - - """ - Provide for the given 'translation' an optimiser for the desired - 'optimisations'. See the 'supported_optimisations' attribute of this - class for permitted values. - """ - - self.translation = translation - self.optimisations = set(optimisations or []) - - # The current "active" instruction. - # As a rule, this will become the last instruction, but some - # control-flow operations will flush the "active" instruction. - - self.active = None - - # Information about instructions providing the active values for - # registers. - - self.saved_value_op = {} - self.saved_value_block = {} - self.saved_value_pos = {} - - # Sets of instructions providing the active value for each register. - - self.active_values = {} - - def get_attribute_store_instructions(self): - - """ - Return an appropriate set of instructions available when storing - attributes. - """ - - ca = self.should_optimise_accesses_by_attribute_usage() - - return ( - ca and StoreAddress or None, # plain assignment - ca and StoreAddressContext or None, # assignment via class - ca and StoreAddressContext or None, # assignment via class (never via instance) - StoreAttr, # assignment via instance - StoreAttrIndex, # special assignment via instance - None - ) - - def reset(self, block=None, blocks=None): - - """ - Reset the optimiser for the given 'block' (or the current block), - clearing the active value instructions if no 'blocks' are given, or - collecting the active instructions from each of the blocks otherwise. - """ - - self.store_active_values() - self.clear_active() - - # Make a new collection of instructions for a new block. - - if block: - self.active_values = {} - block.set_active_values(self.active_values) - - # Otherwise, clear the collection for an existing block. - - else: - self.active_values.clear() - - if blocks: - for b in blocks: - self.active_values.update(b.get_active_values()) - - def set_new(self, register, op): - - "For 'register', set the latest 'op' as the active instruction." - - self.set_active(op) - self.set_active_value(register, op) - - def set_active_value(self, register, op): - - "For 'register', set 'op' as the value-providing active instruction." - - # Since the current value provider may eventually be used, it needs to - # store its output if appropriate. - - self.store_active_value(register) - self.active_values[register] = set([op]) - - def get_active_value(self, register): - - """ - Get any single active value for the given 'register' or None if none or - more than one exist. - """ - - if self.active_values.has_key(register) and \ - len(self.active_values[register]) == 1: - - return list(self.active_values[register])[0] - - else: - return None - - def remove_active_value(self, register): - - """ - Remove the active instruction providing 'register' with its value from - the generated code, if appropriate, and return the active instruction. - """ - - if self.active_values.has_key(register) and \ - self.active in self.active_values[register]: - - removed = self.translation.remove_op() - self.active_values[register].remove(removed) - return removed - - else: - return None - - def set_target(self, register, target): - - """ - Set the target of the active instruction involving 'register' to - 'target'. - """ - - if self.active_values.has_key(register): - for expr in self.active_values[register]: - expr.target = target - - # Transfer the active instructions to their new target. - - if not self.active_values.has_key(target): - self.active_values[target] = self.active_values[register] - else: - self.active_values[target].update(self.active_values[register]) - - # Remove the association between the instructions and the specified - # register. - - del self.active_values[register] - - def set_active(self, op): - - "Set the active instruction." - - self.active = op - - def clear_active(self): - - "Clear the active instructions." - - self.active = None - - # Permit the active value to be requested and restored. - - def request_active_value(self, register, temp, block, pos): - - """ - Request the current active value for 'register' so that if the value is - changed, a temporary storage element or equivalent will be allocated. - """ - - # Cause any previously active value for the register to be saved. - - self.store_active_value(register) - - # Redefine the active value for the register. - - self.saved_value_op[register] = temp - self.saved_value_block[register] = block - self.saved_value_pos[register] = pos - - def store_active_values(self): - - "Store all active values." - - for register in self.active_values.keys(): - self.store_active_value(register) - - def store_active_value(self, register): - - "Store the requested active value for 'register'." - - if self.saved_value_op.has_key(register): - - # Cause an instruction to be inserted to store the active value for - # the register, thus keeping it available for any subsequent usage. - - self.translation.set_temp( - self.saved_value_op[register], # cause the storage of the value - self.saved_value_block[register], # in the appropriate block - self.saved_value_pos[register] # at the appropriate location - ) - - # Discard the existing active value information. - - self.ignore_active_value(register) - - def ignore_active_value(self, register): - - "Ignore the active value in 'register'." - - if self.saved_value_op.has_key(register): - del self.saved_value_op[register] - del self.saved_value_block[register] - del self.saved_value_pos[register] - - # Optimisation tests. - - def should_optimise_constant_storage(self): - return "constant_storage" in self.optimisations - - def should_optimise_constant_accessor(self): - return "constant_accessor" in self.optimisations - - def should_optimise_known_target(self): - return "known_target" in self.optimisations - - def should_optimise_self_access(self): - return "self_access" in self.optimisations - - def should_optimise_temp_storage(self): - return "temp_storage" in self.optimisations - - def should_optimise_away_no_operations(self): - return "no_operations" in self.optimisations - - def should_optimise_unused_results(self): - return "unused_results" in self.optimisations - - def should_optimise_unused_handlers(self): - return "unused_handlers" in self.optimisations - - def should_optimise_accesses_by_attribute_usage(self): - return "accesses_by_usage" in self.optimisations - - # Simple tests. - - def is_constant_input(self, instruction): - - "Return whether 'instruction' provides a constant input." - - return isinstance(instruction, LoadAddress) and instruction.attr.assignments == 1 and \ - isinstance(instruction.attr.get_value(), Constant) or \ - isinstance(instruction, (LoadConst, LoadClass, LoadFunction)) - - def is_constant_target(self, instruction): - - "Return whether 'instruction' provides a constant target." - - # NOTE: Removed StoreName, since this would then demand population of - # NOTE: locals/frames. - - return isinstance(instruction, (StoreAddress, StoreAddressContext)) and \ - instruction.attr.is_constant() and \ - instruction.attr.is_strict_constant() - - def is_simple_input(self, instruction): - - """ - Return whether 'instruction' provides a simple input (typically a load - instruction). A simple input is something which would be represented by - a load operation from a CPU register or special memory location. - """ - - return isinstance(instruction, (LoadConst, LoadClass, LoadFunction, - LoadName, LoadTemp, LoadAddress - )) - - def is_resultant_no_operation(self, instruction, last_op=None): - - """ - Return whether 'instruction' merely stores its input where the input - originally came from. - """ - - last_op = last_op or self.translation.last_op() - return last_op and last_op.attr == instruction.attr and ( - isinstance(instruction, StoreTemp) and isinstance(last_op, LoadTemp) or - isinstance(instruction, StoreAddress) and isinstance(last_op, LoadAddress) or - last_op.source == instruction.target and ( - isinstance(instruction, LoadTemp) and isinstance(last_op, StoreTemp) or - isinstance(instruction, LoadAddress) and isinstance(last_op, StoreAddress) - )) - - # Convenience tests on outputs. - - def have_constant_target(self): - - "Return whether the active instruction provides a constant target." - - return self.is_constant_target(self.active) - - def have_constant_source(self, expr): - - "Return whether the active assignment source instruction is constant." - - return self.is_constant_input(expr) - - # Convenience tests on inputs. - - def have_constant_input(self): - - "Return whether the active instruction provides a constant input." - - return self.get_active_value("working") and self.is_constant_input(self.get_active_value("working")) - - def have_simple_input(self): - - "Return whether the active instruction provides a simple input." - - return self.get_active_value("working") and self.is_simple_input(self.get_active_value("working")) - - # Indicate whether the active instruction can be used in place of access - # to a temporary variable retaining the result of the last instruction. - - have_temp_compatible_access = have_simple_input - - def have_self_input(self, unit): - - """ - Return whether the active instruction is a reference to self in the - given 'unit'. - """ - - if not (isinstance(unit, Function) and unit.is_method()): - return False - - expr = self.get_active_value("working") - return expr and isinstance(expr, LoadName) and expr.attr.name == "self" - - def have_correct_self_for_target(self, context, unit): - - "Return whether the 'context' is compatible with the given 'unit'." - - if context is not None and self.have_self_input(unit): - - parent = unit.parent - if parent is context or parent.has_subclass(context) or context.has_subclass(parent): - return True - - return False - - def have_empty_handler(self, instruction): - - """ - Return whether the active instruction defined a handler for exceptions - which is then discarded by the given 'instruction'. - """ - - return isinstance(self.translation.last_op(), PushHandler) and isinstance(instruction, PopHandler) - - # Optimisation methods. See the supported_optimisations class attribute. - - def optimise_constant_storage(self, expr): - - """ - Where the last operation stores a constant into a target which is also - constant, indicate that both operations should be optimised away. - """ - - return self.should_optimise_constant_storage() and \ - self.have_constant_target() and \ - self.have_constant_source(expr) - - def optimise_constant_accessor(self): - - """ - Where the object whose attribute is being accessed is constant, provide - information about the object and its full name. - """ - - if self.should_optimise_constant_accessor() and self.have_constant_input(): - value = self.get_active_value("working") - - # Get the details of the access. - - if isinstance(value.attr, Const): - return value.attr, value.attr.value_type_name() - else: - target = value.attr.get_value() - - if target is None: - return None # no clearly defined target - elif isinstance(target, Const): - return target, target.value_type_name() - elif isinstance(target, Instance): - return None # skip production of optimised code - else: - return target, target.full_name() - - else: - return None - - def optimise_known_target(self): - - """ - Where the target of an invocation is known, provide information about it - and its context. If a class is being invoked and the conditions are - appropriate, get information about the specific initialiser. - """ - - if self.should_optimise_known_target() and self.have_constant_input(): - value = self.get_active_value("working") - target = value.attr.get_value() - context = value.attr.get_context() - - # Return target details only if this is clearly defined. - - if target is not None: - return target, context - - return None - - def optimise_self_access(self, unit, attrname): - - """ - Return whether code in the given 'unit' is able to access the given - 'attrname' through the same position in all possible objects which might - be accessed. - """ - - return self.should_optimise_self_access() and \ - self.have_self_input(unit) and not unit.is_relocated(attrname) - - def optimise_temp_storage(self): - - """ - Where the next operation would involve storing a value into temporary - storage, record and remove any simple instruction which produced the - value to be stored such that instead of subsequently accessing the - temporary storage, that instruction is substituted. - - If no optimisation can be achieved, temporary storage is requested - and the appropriate LoadTemp instruction is returned. - - Restriction: for use only in situations where the source of the - temporary data will not be disturbed between its first access and its - subsequent use. - """ - - if self.should_optimise_temp_storage(): - - # Emitted instructions can be obtained. - - if self.have_temp_compatible_access(): - - # Remove the active value contributor if possible. - - removed = self.remove_active_value("working") - if removed is not None: - - # Extend the lifetime of any temporary storage location. - - self.translation.ensure_temp(removed) - return removed - - # Otherwise, just leave it in place, but return the instruction. - - else: - return self.get_active_value("working") - - # Or provisional temporary instructions. - - elif self.saved_value_op.has_key("working"): - return self.saved_value_op["working"] - - return self.translation.get_temp() - - def optimise_away_no_operations(self, instruction, last_op=None): - - """ - Optimise away operations which just store their inputs in the place - the inputs originally came from. - """ - - return self.should_optimise_away_no_operations() and \ - self.is_resultant_no_operation(instruction, last_op) - - def optimise_unused_results(self): - - "Discard results which will not be used." - - if self.should_optimise_unused_results() and self.have_simple_input(): - self.remove_active_value("working") - - def optimise_unused_handlers(self, instruction): - - "Discard handlers which will not be used." - - if self.should_optimise_unused_handlers() and self.have_empty_handler(instruction): - self.translation.remove_op() - return True - else: - return False - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba micropython/program.py --- a/micropython/program.py Fri Jun 28 21:17:02 2013 +0200 +++ b/micropython/program.py Sat Jun 29 01:28:12 2013 +0200 @@ -3,7 +3,7 @@ """ Program code and data representations. -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -19,123 +19,6 @@ this program. If not, see . """ -try: - set -except NameError: - from sets import Set as set - -class Location: - - """ - A special representation for locations which are to be compared to program - objects. - """ - - def __init__(self, location): - self.location = location - - def _op(self, other, op): - if hasattr(other, "location"): - return op(self.location, other.location) - else: - raise NotImplemented - - def __eq__(self, other): - return self._op(other, operator.eq) - - def __ne__(self, other): - return self._op(other, operator.ne) - - def __lt__(self, other): - return self._op(other, operator.lt) - - def __le__(self, other): - return self._op(other, operator.le) - - def __gt__(self, other): - return self._op(other, operator.gt) - - def __ge__(self, other): - return self._op(other, operator.ge) - - def __repr__(self): - return "Location(%r)" % self.location - -class Block: - - "A code block." - - def __init__(self, unit): - self.unit = unit - self.code = [] - self.location = None - self.active_values = set() - - def __repr__(self): - return "Block(%r, id=%r, location=%r)" % (self.unit, id(self), self.location) - - def set_active_values(self, values): - self.active_values = values - - def get_active_values(self): - return self.active_values - - def insert(self, pos, op): - self.code.insert(pos, op) - - def append(self, op): - self.code.append(op) - - def __len__(self): - return len(self.code) - -class DataValue: - - "A representation of a raw program value." - - def __init__(self, context, ref): - self.context = context - self.ref = ref - - def __repr__(self): - return "value: (%r, %r)" % ( - self.context, self.ref - ) - -class DataObject: - - "A representation of a raw program data object." - - def __init__(self, classcode, attrcode, codeaddr, name, size, funccode=None): - self.classcode = classcode - self.attrcode = attrcode - self.codeaddr = codeaddr - self.name = name - self.size = size - self.funccode = funccode - - def with_size(self, size): - return DataObject(self.classcode, self.attrcode, self.codeaddr, self.name, size, self.funccode) - - def with_callable(self, codeaddr): - return DataObject(self.classcode, self.attrcode, codeaddr, self.name, self.size, self.funccode) - - def __repr__(self): - return "object: %r # %s" % ( - (self.classcode, self.attrcode, self.codeaddr, self.funccode, self.size), self.name - ) - -class FragmentObject: - - "A representation of a list fragment, used by list instances." - - def __init__(self, occupied_size, allocated_size): - self.occupied_size = occupied_size - self.allocated_size = allocated_size - - def __repr__(self): - return "%r" % ((self.occupied_size, self.allocated_size),) - class Context: """ diff -r 979e2a19fc4a -r b772a168a2ba micropython/raw.py --- a/micropython/raw.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -#!/usr/bin/env python - -""" -Classes used to help with the generation of raw image data. - -Copyright (C) 2009, 2010, 2011, 2012 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -class RawObject: - - "The basis for serialised program objects." - - def __init__(self, item): - self.item = item - - def set_location(self, location, objtable, with_builtins): - self.item.location = location - return location + 1 - - def finalise_location(self, with_builtins): - pass - - def finalise_body_location(self, with_builtins): - pass - - def is_generated(self, with_builtins): - return with_builtins or self.item.module.name not in ("__builtins__", "native") or self.item.astnode.doc is not None - -class UntranslatableInstruction(Exception): - pass - -class RSVPTranslator: - - "A class which provides the basis for the translation of RSVP instructions." - - def get_method(self, instruction): - - "Return the handler method for the given 'instruction'." - - instruction_name = instruction.__class__.__name__ - method = getattr(self, instruction_name, None) - if method is None: - raise UntranslatableInstruction, instruction_name - return method - - def perform(self, instruction): - - "Perform the 'instruction', returning the next PC value or None." - - self.get_method(instruction)() - - def translate(self, code): - - "Translate the given 'code'." - - self.output_code = [] - - for instruction in code: - self.perform(instruction) - - def new_op(self, op): - self.output_code.append(op) - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba micropython/rsvp.py --- a/micropython/rsvp.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,729 +0,0 @@ -#!/usr/bin/env python - -""" -RSVP instruction and serialisation classes. - -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from micropython.data import Attr, Const, Class, Function, Module -from micropython.program import Block, DataObject, DataValue, PlaceholderContext -from micropython.raw import RawObject - -def name(attr): - if isinstance(attr, Attr): - return attr.name or "" - elif isinstance(attr, (Block, Const)): - return attr - else: - return attr.full_name() or "" - -# Serialisation-related classes. - -class RSVPObject(RawObject): - - "A generic data object wrapper." - - def _finalise_location(self, with_builtins): - - """ - Set the code body location for items now that the code blocks have been - positioned. - """ - - item = self.item - - if not self.is_generated(with_builtins): - item.code_body_location = item.full_name() - else: - item.code_body_location = item.get_body_block().location - -class RSVPAttr(RSVPObject): - - "A wrapper for attributes/values." - - def as_raw(self, objtable, paramtable, with_builtins): - item = self.item - return [ - DataValue( - item.get_context() and item.get_context().location, - item.get_value() and item.get_value().location - ) - ] - -class RSVPBlock(RSVPObject): - - "A wrapper for blocks." - - def set_location(self, location, objtable, with_builtins): - item = self.item - item.location = location - return location + len(item.code) - - def as_raw(self, objtable, paramtable, with_builtins): - return self.item.code - -class RSVPClass(RSVPObject): - - "A wrapper for classes." - - def set_location(self, location, objtable, with_builtins): - self.item.instance_template_location = location - - # Include the instance template and __class__ attribute in the size. - - return RSVPObject.set_location(self, location + 1, objtable, with_builtins) - - def finalise_body_location(self, with_builtins): - self._finalise_location(with_builtins) - - def as_raw(self, objtable, paramtable, with_builtins): - item = self.item - classcode = objtable.as_list().get_code(item.full_name()) - attrcode = objtable.get_index("#" + item.full_name()) - - # Include a template of an instance for use when instantiating classes. - - call_method = item.all_class_attributes().get("__call__") - call_method_value = call_method and call_method.get_value() - call_method_code_location = call_method_value and call_method_value.code_location - call_method_funccode = call_method_value and paramtable.as_list().get_code(call_method_value.full_name()) - - instantiator_funccode = paramtable.as_list().get_code(item.get_instantiator().full_name()) - - # NOTE: The instantiator code is the first block of the class. - - if not self.is_generated(with_builtins): - instantiator_code_location = item.full_name() - else: - instantiator_code_location = item.get_instantiator().blocks[0].location - - return [ - - # Template instance... - - DataObject( - classcode, - attrcode, # is instance - call_method_code_location, - item.full_name(), - len(item.instance_attributes()), # size (excluding __class__) - call_method_funccode # funccode - ), - - # Class... - - DataObject( - classcode, - None, # is not instance - instantiator_code_location, - item.full_name(), - len(item.class_attributes()) + 1, # size - instantiator_funccode # funccode - ) - ] - -class RSVPConst(RSVPObject): - - "A wrapper for constants." - - def set_location(self, location, objtable, with_builtins): - item = self.item - value = item.get_value() - type_name = self.type_name(item.value_type_name(), value, objtable) - - location = RSVPObject.set_location(self, location, objtable, with_builtins) - return location + self.raw_size(type_name, value) - - def as_raw(self, objtable, paramtable, with_builtins): - item = self.item - value = item.get_value() - type_name = self.type_name(item.value_type_name(), value, objtable) - raw_data = self.raw_data(type_name, value, objtable) - size = self.raw_size(type_name, value) + 1 - - # Generate the footprint of the constant. - - classcode = objtable.as_list().get_code(type_name) - attrcode = objtable.get_index("#" + type_name) - - return [ - DataObject( - classcode, - attrcode, # is instance - None, - type_name, - size # header plus data - ) - - # NOTE: The RSVP library needs changing if more attributes are added - # NOTE: here (such as a __class__ attribute for all instances). - - ] + raw_data - - def type_name(self, type_name, value, objtable): - - # Change the type of string constants if they might be used as attribute - # accessors. - - accessor_cls = "__builtins__._accessor" - - if type_name == "__builtins__.str" and value in objtable.attribute_names() and \ - accessor_cls in objtable.object_names(): - - return accessor_cls - else: - return type_name - - def raw_data(self, type_name, value, objtable): - - # NOTE: Start simple and use single entries for most types, even though - # NOTE: a concrete representation may use multiple memory words to - # NOTE: represent the data. - - if type_name in ("__builtins__.tuple", "__builtins__.list"): - return [len(value)] + list(value) - elif type_name == "__builtins__._accessor": - return [value, objtable.get_index(value)] - else: - return [value] - - def raw_size(self, type_name, value): - if type_name in ("__builtins__.tuple", "__builtins__.list"): - return 1 + len(value) - elif type_name == "__builtins__._accessor": - return 2 - else: - return 1 - -class RSVPFunction(RSVPObject): - - "A wrapper for functions." - - def set_location(self, location, objtable, with_builtins): - item = self.item - location = RSVPObject.set_location(self, location, objtable, with_builtins) - - # Set the code location only where the code has been - # generated. - - if not self.is_generated(with_builtins): - item.code_location = item.full_name() - - # Skip any defaults for static functions. - - elif not item.is_dynamic(): - item.code_location = location + len(item.defaults) - - # Skip any defaults for dynamic functions. - - else: - item.code_location = location - - return location - - def finalise_body_location(self, with_builtins): - self._finalise_location(with_builtins) - - def as_raw(self, objtable, paramtable, with_builtins): - item = self.item - # NOTE: Need class and parameter details! Should arguably be an instance of types.FunctionType. - return [ - DataObject( - objtable.as_list().get_code("__builtins__.function"), - objtable.get_index("#__builtins__.function"), # is instance - item.code_location, - "__builtins__.function", - len(item.defaults) + 1, # size (not accurate for lambda functions before instantiation) - paramtable.as_list().get_code(item.full_name()) # funccode - ) - ] - -class RSVPModule(RSVPObject): - - "A wrapper for modules." - - def finalise_location(self, with_builtins): - item = self.item - if item.blocks: - item.code_location = item.blocks[0].location - - def as_raw(self, objtable, paramtable, with_builtins): - item = self.item - return [ - DataObject( - objtable.as_list().get_code(item.full_name()), - None, # modules treated like classes - item.code_location, - item.full_name(), - len(item.module_attributes()) + 1 # size - ) - ] - -# Serialisation-related data and functions. - -def get_object(item): - if isinstance(item, Attr): - cls = RSVPAttr - elif isinstance(item, Block): - cls = RSVPBlock - elif isinstance(item, Class): - cls = RSVPClass - elif isinstance(item, Const): - cls = RSVPConst - elif isinstance(item, Function): - cls = RSVPFunction - elif isinstance(item, Module): - cls = RSVPModule - else: - cls = None - - if cls is not None: - return cls(item) - else: - return None - -# Instruction-related classes. - -class Instruction: - - "A generic instruction." - - default_working = "working" - default_target = "working" - default_source = None - - # NOTE: Ultimately, instructions apart from Transfer will use specific - # NOTE: registers such as "working_value" and "working_context". - - def __init__(self, attr=None, working=None, target=None, source=None): - self.attr = attr - self.working = working or self.default_working - self.target = target or self.default_target - self.source = source or self.default_source - - def get_details(self): - return self.__class__, self.attr, self.working, self.target, self.source - - def copy(self): - return self.__class__(self.attr, self.working, self.target, self.source) - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, - ", ".join( - self.format_operand() + - self.format_working() + - self.format_source() + - self.format_target() - )) - - def __hash__(self): - return hash(self.get_details()) - - def __eq__(self, other): - return self.get_details() == other.get_details() - - def __ne__(self, other): - return not self.__eq__(other) - - def format_operand(self): - operand = self.get_operand() - return operand is not None and [repr(operand)] or [] - - def format_working(self): - return self.working != self.default_working and ["working=%r" % self.working] or [] - - def format_source(self): - return self.source != self.default_source and ["source=%r" % self.source] or [] - - def format_target(self): - return self.target != self.default_target and ["target=%r" % self.target] or [] - - def get_operand(self): - return None - -class FrameRelativeInstruction(Instruction): - - "An instruction operating on the current frame." - - def get_operand(self): - return self.attr.position - -FR = FrameRelativeInstruction - -class AddressRelativeInstruction(Instruction): - - "An instruction accessing an object's attribute." - - def format_operand(self): - return Instruction.format_operand(self) + ["name=%r" % name(self.attr)] - - def get_operand(self): - return self.attr.position - -AR = AddressRelativeInstruction - -class AddressInstruction(Instruction): - - "An instruction loading an address directly." - - def format_operand(self): - location, position, result = self.get_operands() - if location is not None: - return ["%s" % result, "location=%s" % location, "position=%s" % position, - "name=%s" % name(self.attr)] - elif result is not None: - return ["%s" % result, "name=%s" % name(self.attr)] - else: - return ["name=%s" % name(self.attr)] - - def get_operands(self): - if isinstance(self.attr, Attr): - position = self.attr.position - location = self.attr.parent.location - - # NOTE: Unpositioned attributes are handled here. - - if location is not None and position is not None: - result = location + position + 1 - else: - location = self.attr.parent.name - position = self.attr.name - result = None - return location, position, result - else: - return None, None, self.attr.location - - def get_operand(self): - return self.get_operands()[-1] - -Address = AddressInstruction - -class TargetInstruction(Instruction): - - "An instruction loading the address of a direct invocation target." - - def format_operand(self): - return Instruction.format_operand(self) + ["name=%r" % name(self.attr)] - - def get_operand(self): - return self.attr.code_body_location - -Target = TargetInstruction - -class ImmediateInstruction(Instruction): - - "An instruction employing a constant." - - def get_operand(self): - return self.attr - -Immediate = ImmediateInstruction - -# Low-level instructions. - -class Transfer(Instruction): - "Transfer a register's contents into another." - cost = 1 - -class LoadMemory(Instruction): - "Load a value from a memory address given by a register, optionally adding the operand." - cost = 1 - -class Add(Instruction): - "Add the value given by the working register to the operand." - cost = 1 - -class Multiply(Instruction): - "Multiply the value given by the working register by the operand." - cost = 1 - -# Access to stored constant data. - -class LoadConst(Address): - "Load the constant or module from the specified location." - cost = 1 - -class LoadClass(Address): - "Load the class from the specified location." - cost = 1 - -class LoadFunction(Address): - "Load the function from the specified location." - cost = 1 - -# Access within an invocation frame. - -class LoadName(FR): - "Load the current value from the given local attribute/variable." - cost = 2 - -class StoreName(FR): - "Store the source value into the given local attribute/variable." - cost = 2 - default_target = None - -class LoadTemp(Immediate): - "Load the current value from the given temporary location." - cost = 2 - -class StoreTemp(Immediate): - "Store the current value into the given temporary location." - cost = 2 - default_target = None - -# Access to static data. - -class LoadAddress(Address): - "Load the current value from the given fixed attribute address." - cost = 1 - -class StoreAddress(Address): - "Store the source value into the given fixed attribute address." - cost = 1 - default_working = None - default_target = None - -class LoadAddressContext(Address): - "Load the current value from the given fixed attribute address, using the current value as context." - cost = 2 - -class StoreAddressContext(Address): - "Store the current value into the given fixed attribute address, using the current value as context." - cost = 2 - default_target = None - -class LoadAddressContextCond(Address): - """ - Load the current value from the given fixed attribute address, only using the current value as - context if the attribute is compatible. - """ - cost = 4 - -class MakeInstance(Immediate): - "Make a new instance using the current value as a reference to a template." - cost = 5 - -class MakeFragment(Immediate): - "Make a new list fragment." - cost = 5 - -# Access to address-relative data. (LoadAttrIndexContext not defined.) - -class LoadAttr(AR): - "Load into the current value the given attribute of the object referenced by the current value." - cost = 2 - -class StoreAttr(AR): - "Store the source value into the given attribute of the object referenced by the current value." - cost = 2 - default_target = None - -class LoadAttrIndex(Immediate): - "Load into the current value the attribute of the current value with the given index." - cost = 6 - -class StoreAttrIndex(Immediate): - "Store the source value into the attribute of the current value with the given index." - cost = 6 - default_target = None - -class LoadAttrIndexContextCond(Immediate): - """ - Load into the current value the attribute of the current value with the given index, only making the - current value the context if the attribute is compatible. - """ - cost = 8 - -# Access to object details. - -class LoadCallable(Instruction): - "Load the target of an invocation." - cost = 2 - -class StoreCallable(Instruction): - "Store the source value into the object referenced by the current value." - cost = 3 - default_target = None - -# Access to invocation frames in preparation. - -class MakeFrame(Immediate): - "Make a new invocation frame." - cost = 2 - default_target = None - -class AdjustFrame(Immediate): - "Adjust the current invocation frame for corrected invocations." - cost = 2 - default_target = None - -class DropFrame(Instruction): - "Drop an invocation frame." - cost = 2 - default_target = None - -class StoreFrame(Immediate): - "Store the current value as an argument for the parameter with the given position." - cost = 2 - default_target = None - -class StoreFrameIndex(Immediate): - "Store the source value as an argument of the current value for the parameter with the given index." - cost = 6 - default_target = None - -# Context-related tests. - -class CheckContext(Instruction): - "Check to see if the context is valid." - cost = 2 - -class CheckClass(Instruction): - "Check the current value to determine whether it is a class." - cost = 2 - -class CheckInstance(Instruction): - """ - Check the current value as an instance of a class or its subclasses (used with 'self' in an - invocation). - """ - cost = 6 - -# Access to frames upon invocation. - -class CheckFrame(Immediate): - "Check the frame for the correct number of arguments." - cost = 3 - -class CheckExtra(Immediate): - "Ensure that the frame can provide extra arguments." - cost = 3 - -class FillDefaults(Immediate): - "Fill frame positions with defaults, if appropriate." - cost = 8 # variable - default_target = None - -class ExtendFrame(Immediate): - "Extend the current frame for temporary storage use." - cost = 1 - default_target = None - -class CopyExtra(Immediate): - "Copy extra arguments into a separate sequence, starting from the given position." - cost = 10 - -# Invocation-related instructions, using a special result "register". - -class JumpInFrame(Instruction): - "Jump, using the current locals, to the current callable." - cost = 2 - default_target = None - -class JumpInFrameDirect(Address): - "Jump, using the current locals, to the specified address." - cost = 2 - default_target = None - -class JumpWithFrame(Instruction): - "Jump, adopting the invocation frame, to the current callable." - cost = 3 - default_target = None - -class JumpWithFrameDirect(Target): - "Jump to the specified address, adopting the invocation frame." - cost = 3 - default_target = None - -class Return(Instruction): - "Return from a subprogram." - cost = 2 - default_target = None - -# Branch-related instructions. - -class Jump(Address): - "Jump unconditionally." - cost = 1 - default_target = None - -class JumpIfFalse(Address): - "Jump if the last evaluation gave a false result." - cost = 2 - default_target = None - -class JumpIfTrue(Address): - "Jump if the last evaluation gave a true result." - cost = 2 - default_target = None - -# Exception-related instructions, using a special exception "register". - -class RaiseException(Instruction): - "Raise an exception, jumping to the active handler." - cost = 2 - default_target = None - -class PushHandler(Address): - "Push an exception handler onto the handler stack." - cost = 3 - default_target = None - -class PopHandler(Immediate): - "Pop exception handlers from the handler stack." - cost = 3 - default_target = None - -class CheckException(Instruction): - "Check the raised exception against another." - cost = 6 - -class ClearException(Instruction): - "Clear any raised exception." - cost = 1 - -# Test instructions, operating on the boolean status register. - -class TestIdentity(Instruction): - "Test whether the current value is identical to the source value, setting the boolean status." - cost = 2 - -class TestIdentityAddress(Address): - "Test whether the current value is identical to the given address, setting the boolean status." - cost = 2 - -class InvertBoolean(Instruction): - "Invert the boolean status." - cost = 1 - -# Instructions which can affect the current value. (LoadAttrIndexContext not defined.) - -def affects_register(instruction, register): - - """ - Returns whether 'instruction' affects the given 'register', either directly - or as a consequence of its execution. - """ - - return instruction.target == register or isinstance(instruction, ( - JumpInFrame, JumpInFrameDirect, - JumpWithFrame, JumpWithFrameDirect, - JumpIfTrue, JumpIfFalse, - Jump - )) - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba micropython/trans.py --- a/micropython/trans.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1423 +0,0 @@ -#!/usr/bin/env python - -""" -Translate the AST of a Python program into a more interpretable representation. - -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from micropython.common import operator_functions -from micropython.data import * -from micropython.errors import * -from micropython.rsvp import * -import compiler.ast - -class Helper: - - "Internal helper methods for AST visitors." - - # Common methods. - - def _generateGuards(self, node): - - if not (self.optimiser.should_optimise_accesses_by_attribute_usage() and node._attrtypes): - return - - # For each name, attempt to restrict the type employed. - - for name, targets in node._attrtypes.items(): - - # NOTE: Need to merge targets using the same type but suggesting - # NOTE: different kinds of attributes (instance vs. class). - - # Where only one object type is suggested, produce a guard. - # NOTE: This only supports classes as types, not modules. - - if len(targets) == 1: - target_name, is_static = list(targets)[0] - - # Access the object table to get the attribute. - # This depends on a special entry in the table for class - # equivalence tests. - - try: - obj = self.objtable.get_object(target_name) - - # Where no attribute entry exists, the target could be a module. - # NOTE: Should perhaps raise an error. - - except TableError, exc: - print "Possible guard for", target_name, "not enforceable." - continue - - # NOTE: Could test the correctness of the guard where the nature - # NOTE: of the name is known. - # NOTE: The known value would be retrieved from the unit's - # NOTE: locals and tested as being a class or an instance of a - # NOTE: particular class. - - # Generate the guard by loading a reference to the class. - - after_test_block = self.new_block() - - self.new_op(LoadClass(obj, target="source")) - - # For only static attributes, classes are acceptable. - - if is_static: - - # Generate name is target (for classes). - - self.dispatch(compiler.ast.Name(name)) - self.new_op(TestIdentity(source="source", target="status")) - - # Jump to the next guard or the code if successful. - - self.new_op(JumpIfTrue(after_test_block, working="status")) - - # Where instance attributes are involved, only instances are - # acceptable. - - if not isinstance(obj, Module): - - # Generate isinstance(name, target). - - self.dispatch(compiler.ast.Name(name)) - self.new_op(CheckInstance(source="source", target="status")) - - # Jump to the next guard or the code if successful. - - self.new_op(JumpIfTrue(after_test_block, working="status")) - - # Where the type is inappropriate, raise an exception. - - self.make_exception("TypeError") - self.set_target("exception") - self.new_op(RaiseException()) - - self.set_block(after_test_block) - - def _visitAttr(self, node, classes): - - """ - Visit the attribute-related 'node', generating instructions based on the - given 'classes'. - """ - - self.dispatch(node.expr) - self._generateAttr(node, node.attrname, classes) - - def _generateAttr(self, node, attrname, classes): - - """ - Generate code for the access to 'attrname' using the given 'classes'. - """ - - AddressInstruction, AddressContextInstruction, AddressContextCondInstruction, \ - AttrInstruction, AttrIndexInstruction, AttrIndexContextCondInstruction = classes - - # Where the last operation (defining the attribute owner) yields a - # constant... - - target_plus_name = self.optimiser.optimise_constant_accessor() - - # Only try and discover the position if the target can be resolved. - # Since instances cannot be constants in general, this involves classes - # and modules, but constants known at compile-time must also be handled. - - if target_plus_name is not None: - target, target_name = target_plus_name - - # Check for class.__class__. - - if attrname == "__class__": - if isinstance(target, Class): - if AddressInstruction is LoadAddress: - self.replace_active_value("working", LoadAddress(self.get_builtin("type"))) - return - else: - raise TranslateError("Assigning to __class__ is not permitted.") - - # Access the object table to get the attribute. - - try: - attr = self.objtable.access(target_name, attrname) - except TableError, exc: - raise TranslateError(exc.args[0]) - - # Produce a suitable instruction. - - if AddressInstruction is not None: - - # Where the target is a constant instance, the constant input - # needs to be retained as the context of the resulting - # attribute. - - if isinstance(target, Instance): - self.new_op(AddressContextInstruction(attr)) - - # It is acceptable to replace the instruction providing the - # constant input because doing so does not lose any input - # information required by the replacement instructions. - - else: - self.replace_active_value("working", AddressInstruction(attr)) - else: - raise TranslateError("Storing of class or module attribute %r via an object is not permitted." % attrname) - - return - - # Where the last operation involves the special 'self' name, check to - # see if the attribute is acceptably positioned and produce a direct - # access to the attribute. - - # This is the only reliable way of detecting instance accesses at - # compile-time since in general, objects could be classes or modules, - # but 'self' should only refer to instances. - - elif self.optimiser.optimise_self_access(self.unit, attrname): - - # Either generate an instruction operating on an instance attribute. - - try: - attr = self.unit.parent.instance_attributes()[attrname] - self.new_op(AttrInstruction(attr)) - return - - # Or generate an instruction operating on a class attribute. - # NOTE: Any simple instruction providing self is not removed. - - except KeyError: - - try: - attr = self.unit.parent.all_attributes()[attrname] - - # Switch the context if the class attribute is compatible with - # the instance. - - if attr.defined_within_hierarchy(): - - # Only permit loading (not storing) of class attributes via self. - - if AddressContextInstruction is not None: - self.new_op(AddressContextInstruction(attr)) - else: - raise TranslateError("Storing of class attribute %r via self not permitted." % attrname) - - # Preserve the context if the class attribute comes from an - # incompatible class. - - elif attr.defined_outside_hierarchy(): - - # Only permit loading (not storing) of class attributes via self. - - if AddressInstruction is not None: - self.new_op(AddressInstruction(attr)) - else: - raise TranslateError("Storing of class attribute %r via self not permitted." % attrname) - - # Otherwise, test for a suitable context at run-time. - - else: - - # Only permit loading (not storing) of class attributes via self. - - if AddressContextCondInstruction is not None: - self.new_op(AddressContextCondInstruction(attr)) - else: - raise TranslateError("Storing of class attribute %r via self not permitted." % attrname) - - return - - # Or delegate the attribute access to a general instruction - # since the kind of attribute cannot be deduced. - - except KeyError: - pass - - # Attempt to deduce the target of an attribute access by searching for a - # unique type providing the names associated with the accessed object. - - elif self.optimiser.should_optimise_accesses_by_attribute_usage() and \ - isinstance(node, compiler.ast.AttributeAccessor): - - targets = self.possible_accessors_from_usage(node) - - if targets and len(targets) == 1: - target = list(targets)[0] - attr = target.all_attributes().get(attrname) - - # Produce a suitable instruction. - - if attr: - if AddressContextCondInstruction is not None and attr.is_static_attribute(): - self.new_op(AddressContextCondInstruction(attr)) - elif AttrInstruction is not None and not attr.is_static_attribute(): - self.new_op(AttrInstruction(attr)) - else: - raise TranslateError("Storing of class or module attribute %r via an object is not permitted." % attrname) - - return - - # Check for class.__class__. - - if attrname == "__class__": - - # Remember the accessor. - - temp_accessor = self.get_temp() - - attr_block = self.new_block() - end_block = self.new_block() - - self.new_op(CheckClass(target="status")) - self.new_op(JumpIfFalse(attr_block, working="status")) - self.load_builtin("type", node) - self.new_op(Jump(end_block)) - self.set_block(attr_block) - - # Recall the accessor. - - self.new_op(temp_accessor) - - # Otherwise, perform a normal operation. - - try: - index = self.objtable.get_index(attrname) - - except self.objtable.TableError: - - # If this error arises on generated code, check the names_used - # attribute on the Importer. - - raise TranslateError("No attribute entry exists for name %r." % attrname) - - # NOTE: Test for class vs. instance attributes, generating - # NOTE: context-related instructions. - - if AttrIndexContextCondInstruction is not None: - self.new_op(AttrIndexContextCondInstruction(index)) - - # Store instructions do not need to consider context modifications. - - else: - self.new_op(AttrIndexInstruction(index)) - - # Where __class__ was involved, define the start of the following code. - - if attrname == "__class__": - self.set_block(end_block) - self.discard_temp(temp_accessor) - - # Invocations involve the following: - # - # 1. Reservation of a frame for the arguments - # 2. Identification of the target which is then held in temporary storage - # 3. Optional inclusion of a context (important for methods) - # 4. Preparation of the argument frame - # 5. Invocation of the target - # 6. Discarding of the frame - # - # In order to support nested invocations - such as a(b(c)) - use of the - # temporary storage is essential. - - def _startCallFunc(self): - - "Record the location of the invocation." - - op = MakeFrame() - self.new_op(op) # records the start of the frame - self.frame_makers.append(op) - - def _generateCallFunc(self, args, node): - - """ - Support a generic function invocation using the given 'args', occurring - on the given 'node', where the expression providing the invocation - target has just been generated. - - In other situations, the invocation is much simpler and does not need to - handle the full flexibility of a typical Python invocation. Internal - invocations, such as those employed by operators and certain - control-flow mechanisms, use predetermined arguments and arguably do not - need to support the same things as the more general invocations. - """ - - target, context, temp_target, temp_context = self._generateCallFuncContext() - self._generateCallFuncArgs(target, context, temp_target, temp_context, args, node) - return temp_target, target, temp_context - - def _generateCallFuncContext(self): - - """ - Produce code which loads and checks the context of the current - invocation, the instructions for whose target have already been - produced, returning a list of instructions which reference the - invocation target. - """ - - t = self.optimiser.optimise_known_target() - if t: - target, context = t - - # Detect dynamic functions acting like instances. - - if isinstance(target, Function) and target.is_dynamic(): - target, context = None, None - else: - target, context = None, None - - # Store the target in temporary storage for subsequent referencing. - - temp_target = self.optimiser.optimise_temp_storage() - - # Where a target or context are not known or where an instance is known - # to be the context, load the context. - - if target is None or isinstance(context, Instance): - self.new_op(temp_target) - self.new_op(Transfer(source="working_context", target="working")) - temp_context = self.optimiser.optimise_temp_storage() - self.new_op(StoreFrame(0)) - - # Class contexts should be made available for testing of the first - # argument. - # NOTE: Class methods should eventually be supported. - - elif isinstance(context, Class): - self.new_op(temp_target) - self.new_op(Transfer(source="working_context", target="working")) - temp_context = self.optimiser.optimise_temp_storage() - - # Otherwise omit the context. - - else: - temp_context = None - - return target, context, temp_target, temp_context - - def _generateCallFuncArgs(self, target, context, temp_target, temp_context, args, node): - - """ - Given invocation 'target' and 'context' information, the 'temp_target' - reference to the target, the 'temp_context' reference to the context, a - list of nodes representing the 'args' (arguments), generate instructions - which load the arguments for the invocation defined by the given 'node'. - """ - - # Evaluate the arguments. - - employed_positions = set() - employed_keywords = set() - extra_keywords = [] - positional_args = [] - keyword_args = [] - - # Find keyword arguments in advance in order to help resolve targets. - - have_keywords = False - - for arg in args: - if isinstance(arg, compiler.ast.Keyword): - employed_keywords.add(arg.name) - keyword_args.append(arg) - have_keywords = True - elif not have_keywords: - positional_args.append(arg) - - possible_targets = self.paramtable.all_possible_objects(employed_keywords) - - # Note the presence of the context in the frame where appropriate. - - # For unknown invocations and method invocations. - - if target is None or isinstance(context, Instance): - ncontext = 1 - expect_testable_self = False - - # Handle calls to classes by obtaining the instantiator function. - # A context is reserved for the new instance, but this is not provided - # in the invocation (since the instantiator will fill the locals slot - # concerned). - - elif isinstance(target, Class): - ncontext = 1 - expect_testable_self = False - target = target.get_instantiator() - - # Method calls via classes. - - elif isinstance(context, Class): - ncontext = 0 - expect_testable_self = True - - # Function calls. - - else: - ncontext = 0 - expect_testable_self = False - - # Traverse the positional arguments adding them using the incrementing - # frame position. - - first = True - frame_pos = ncontext - temp_first_argument = None - - for arg in positional_args: - self.dispatch(arg) - self.new_op(StoreFrame(frame_pos)) - employed_positions.add(frame_pos) - - # Check to see if the first argument is appropriate (compatible with - # the target where methods are being invoked via classes). - - if first and (expect_testable_self or target is None): - - # Drop any test if the target and the context are known. - - if not self.optimiser.have_correct_self_for_target(context, self.unit): - - # Otherwise, remember the first argument for a subsequent - # test. - - temp_first_argument = self.optimiser.optimise_temp_storage() - - first = False - frame_pos += 1 - - # Adjust the invocation frame for unknown invocations. - # Test the first argument if appropriate. - - self._generateCallFuncContextTest(target, temp_context, temp_first_argument, node) - - # Traverse the keyword arguments adding them at the appropriate frame - # positions. - - max_keyword_pos = -1 - - for arg in keyword_args: - - # Optimise where the target is known now. - - if target is not None: - - # Find the parameter table entry for the target. - - target_name = target.full_name() - - # Look for a callable with the precise target name. - - table_entry = self.paramtable.table[target_name] - - # Look the name up in the parameter table entry. - - try: - pos = table_entry[arg.name] - - # Where no position is found, this could be an extra keyword - # argument. - - except KeyError: - extra_keywords.append(arg) - continue - - # Test for illegal conditions. - - if pos in employed_positions: - raise TranslateError("Keyword argument %r overwrites parameter %r." % (arg.name, pos)) - - employed_positions.add(pos) - - # Generate code for the keyword and the positioning - # operation. - - self.dispatch(arg.expr) - self.new_op(StoreFrame(pos)) - - # Otherwise, generate the code needed to obtain the details of - # the parameter location. - - else: - - # Combine the target details with the name to get the location. - # See the access method on the List class. - - try: - paramindex = self.paramtable.get_index(arg.name) - - # Where no position is found, this could be an extra keyword - # argument. - - except self.paramtable.TableError: - extra_keywords.append(arg) - continue - - # Generate code for the keyword and the positioning - # operation. Get the value as the source of the assignment. - - self.dispatch(arg.expr) - self.record_value() - self.start_target() - - # Store the source value using the callable's parameter - # table information. - - self.new_op(temp_target) - self.new_op(StoreFrameIndex(paramindex)) - - self.assign_value() - self.discard_value() - - # Record the highest possible frame position for this argument. - - max_keyword_pos = max(max_keyword_pos, max(self.paramtable.all_attribute_positions(arg.name))) - - # Use the frame position counter as a general argument counter. - - frame_pos += 1 - - # NOTE: Extra keywords are not supported. - # NOTE: Somehow, the above needs to be combined with * arguments. - - if extra_keywords: - print "Warning: extra keyword argument(s) %s not handled." % ", ".join([arg.name for arg in extra_keywords]) - - # Either test for a complete set of arguments. - - if target is not None: - - # Make sure that enough arguments have been given. - - nargs_max = len(target.positional_names) - ndefaults = len(target.defaults) - nargs_min = nargs_max - ndefaults - - # Visit each argument position and look for a supplied argument. - - for i in range(ncontext, nargs_min): - if i not in employed_positions: - raise TranslateError( - "Argument %r not supplied for %r: need at least %d argument(s)." % (i+1, target.name, nargs_min)) - - nargs = frame_pos - - # Determine whether too many arguments have been given and how big - # the frame should be. - - # For parameter lists with * or ** parameters, accept as many - # arguments as are allowed or as many as we have. - - if target.has_star or target.has_dstar: - frame_size = max(nargs, nargs_max) - - # NOTE: We now need to pack these arguments into a suitable - # NOTE: structure for the * parameter. - - # For other parameter lists, only accept as many arguments as we are - # allowed. - - elif nargs > nargs_max: - raise TranslateError( - "Too many arguments for %r: need at most %d argument(s)." % (target.name, nargs_max)) - - else: - frame_size = nargs_max - - # Where defaults are involved, put them into the frame. - - self._generateCallFuncDefaultArgs(target, temp_target, nargs_min, nargs_max, employed_positions) - - # Set the frame size. - - self._endCallFuncArgs(frame_size) - - # Or just set the frame size and have the function check the arguments. - - else: - max_pos = max(max(employed_positions or [-1]), max_keyword_pos, frame_pos - 1) - self._endCallFuncArgs(max_pos + 1) - - def _generateCallFuncDefaultArgs(self, target, temp_target, nargs_min, nargs_max, employed_positions): - - """ - For the given 'target' and 'temp_target' reference to the target, - generate default arguments for those positions in the range - 'nargs_min'...'nargs_max' which are not present in the - 'employed_positions' collection. - """ - - # Where appropriate, construct a dynamic object to hold the defaults. - - dynamic = target.is_dynamic() - - # Here, we use negative index values to visit the right hand end of - # the defaults list. - - for pos in range(nargs_min, nargs_max): - if pos not in employed_positions: - if dynamic: - self.new_op(temp_target) - self.new_op(LoadAttr(target.default_attrs[pos - nargs_min])) - else: - self.new_op(LoadAddress(target.default_attrs[pos - nargs_min])) - self.new_op(StoreFrame(pos)) - - def _generateCallFuncContextTest(self, target, temp_context, temp_first_argument, node): - - """ - Generate code involved in a call to the given 'target' to test the - context provided by 'temp_context' against 'temp_first_argument', and to - signal an exception if the context is incompatible with the first frame - argument. - - In addition, the invocation frame will be shifted if 'temp_context' - indicates a function or a class. - """ - - # Need to store the explicit first argument in the working register for - # the eventual test. - - if temp_first_argument is not None: - self.new_op(temp_first_argument) - - # Put the context in the source register. - - if target is None or temp_first_argument is not None: - if self.new_op(temp_context.copy()): - self.last_op().target = "source" - else: - self.new_op(Transfer(source="working", target="source")) - - # Add some preliminary tests where the target is not known. - - if target is None: - if temp_first_argument is not None: - self.new_op(JumpInFrameDirect(self.native.getCallFuncUnknownTargetTestableSelf())) - else: - self.new_op(JumpInFrameDirect(self.native.getCallFuncUnknownTarget())) - - elif temp_first_argument is not None: - self.new_op(JumpInFrameDirect(self.native.getCallFuncKnownTargetTestableSelf())) - - def _doCallFunc(self, temp_target, target=None): - - "Make the invocation." - - # For classes, the target itself is used, since the instantiator will be - # obtained via the class. - - if isinstance(target, (Class, Function)): - self.new_op(JumpWithFrameDirect(target, working="status")) - else: - self.new_op(temp_target) - self.new_op(LoadCallable()) - self.new_op(JumpWithFrame()) - - def _endCallFuncArgs(self, nargs): - - "Set the frame size." - - self.frame_makers[-1].attr = nargs - self.frame_makers.pop() - - def _endCallFunc(self, temp_target=None, temp_context=None): - - "Finish the invocation and tidy up afterwards." - - self.new_op(DropFrame()) - - # Discard any temporary storage instructions. - - if temp_target is not None: - self.discard_temp(temp_target) - - if temp_context is not None: - self.discard_temp(temp_context) - - def _visitFunctionDeclaration(self, node): - - """ - Visit the function declaration at 'node', which can be a lambda or a - named function. As a consequence an instruction will be generated which - provides a reference to the function. - """ - - fn = node.unit - ndefaults = len(fn.defaults) - temp = self._generateFunctionDefaults(fn) - - # Populate the new object required for the function. - - if temp is not None: - self.new_op(LoadConst(fn)) - self.new_op(LoadCallable(target="source")) - self.new_op(temp) - self.new_op(StoreCallable(source="source")) - self.new_op(temp) - #self.discard_temp(temp) - else: - self.new_op(LoadFunction(fn)) - - def _visitFunctionDefinition(self, node): - - """ - Visit the function definition at 'node', which can be a lambda or a - named function, generating the prelude with argument and default - checking, plus the body of the function itself. - """ - - # Check frames using the function's details. - - fn = node.unit - nparams = len(fn.positional_names) - ndefaults = len(fn.defaults) - - fn.body_block = self.new_block() - - # Check the number of parameters and defaults. - - self.new_op(CheckFrame((nparams, ndefaults), target="status")) - - if ndefaults > 0: - if fn.is_dynamic(): - self.new_op(LoadTemp(0)) # context provides storage - else: - self.new_op(LoadFunction(fn)) - - self.new_op(FillDefaults((nparams, ndefaults))) - - # Produce the body. - - self.set_block(fn.body_block) - - # For functions with star parameters, make a special list for the - # extra arguments and re-map the parameter. - - if fn.has_star: - self.new_op(CopyExtra(nparams)) - - # Ensure that the star parameter has a slot in the frame. - - self.new_op(CheckExtra(nparams, target="status")) - self.new_op(StoreTemp(nparams)) - - # Extend the frame for local usage. - - extend = ExtendFrame() - self.new_op(extend) - - # Perform tuple assignment for any tuple parameters. - - self._visitFunctionTupleParameters(fn, node) - - # Add any attribute usage guards. - - self._generateGuards(node) - - # Visit the actual code. - - self.dispatch(node.code) - - # Add a return statement where one is not already produced. - - if not isinstance(self.last_op(), Return): - - # Return None for normal functions without explicit return - # statements. - - if not fn.is_lambda(): - self.dispatch(compiler.ast.Name("None")) - - self.new_op(Return()) - - # Make sure that enough frame space is reserved from the start. - - self.set_frame_usage(node, extend) - - def _visitFunctionTupleParameters(self, fn, node, parameters=None): - - """ - Visit the tuple parameters for function 'fn', obtaining the appropriate - elements from each supplied argument and assigning them to the specified - names for each parameter. - """ - - if parameters is not None: - self._generateAttr(node, "__getitem__", self.attribute_load_instructions) - temp_getitem = self.optimiser.optimise_temp_storage() - - for i, parameter in parameters or fn.tuple_parameters(): - - # Either load the parameter from the frame. - - if parameters is None: - self.new_op(LoadName(Attr(i, None, None))) - - # Or load a value from the current collection. - - else: - self._startCallFunc() - self.new_op(temp_getitem) - temp_target, target, temp_context = self._generateCallFunc([compiler.ast.Const(i)], node) - self._doCallFunc(temp_target, target) - self._endCallFunc() - - # Where a tuple is the target, attempt to descend into the value - # obtained. - - if isinstance(parameter, list): - self._visitFunctionTupleParameters(fn, node, parameter) - - # Store the item in the namespace entry for the given name. - - else: - self.record_value() - self.start_target() - self.new_op(StoreName(fn[parameter])) - self.assign_value() - self.discard_value() - - if parameters is not None: - self.discard_temp(temp_getitem) - - def _generateFunctionDefaults(self, function): - - """ - Generate the default initialisation code for 'function', returning - a temporary storage reference if a dynamic object was created for the - function. - """ - - attr_to_default = zip(function.default_attrs, function.defaults) - if not attr_to_default: - return None - - # Where non-constant defaults are involved, construct a dynamic object - # to hold the defaults. - - dynamic = function.is_dynamic() - - if dynamic: - self.make_instance(self.get_builtin_class("function"), len(attr_to_default)) - temp = self.get_temp() - - for attr, default in attr_to_default: - self.dispatch(default) - - self.record_value() - self.start_target() - if dynamic: - self.new_op(temp) - self.new_op(StoreAttr(attr)) - else: - self.new_op(StoreAddress(attr)) - self.assign_value() - self.discard_value() - - if dynamic: - return temp - else: - return None - - def _storeName(self, node, name=None): - - """ - A convenience method that stores the current working value using the - name provided by the given 'node' or the specified 'name' if provided. - """ - - self.record_value() - self.start_target() - self._generateName(name or node.name, self.name_store_instructions, node) - self.assign_value() - self.discard_value() - - def _visitName(self, node, classes): - - """ - Visit the name-related 'node', generating instructions based on the - given 'classes'. - """ - - name = node.name - self._generateName(name, classes, node) - - def _generateName(self, name, classes, node): - - """ - Generate code for the access to 'name' in 'scope' using the given - 'classes', and using the given 'node' as the source of the access. - """ - - NameInstruction, AddressInstruction, AddressContextInstruction = classes - - # Get the expected scope of the name. - - scope = getattr(node, "_scope", None) or self.get_scope(name) - - # Handle names referring to constants. - - if scope == "constant": - const = self.importer.get_predefined_constant(name) - self.new_op(LoadConst(const)) - - # Handle all other names. - - elif scope == "local": - unit = self.unit - if isinstance(unit, Function): - self.new_op(NameInstruction(unit.all_locals()[name])) - elif isinstance(unit, Class): - if AddressContextInstruction is not None: - self.new_op(LoadConst(unit)) - self.new_op(AddressContextInstruction(unit.all_class_attributes()[name])) - else: - self.new_op(AddressInstruction(unit.all_class_attributes()[name])) - elif isinstance(unit, Module): - self.new_op(AddressInstruction(unit.module_attributes()[name])) - else: - raise TranslateError("Program unit has no local %r." % name) - - elif scope == "global": - globals = self.module.module_attributes() - if globals.has_key(name): - self.new_op(AddressInstruction(globals[name])) - else: - raise TranslateError("Module has no attribute %r." % name) - - elif scope == "builtins": - self.new_op(AddressInstruction(self.get_builtin(name))) - - else: - # NOTE: This may happen because a class attribute is optimised away. - print >>sys.stderr, "Program unit uses unknown name %r." % name - - def _visitUnary(self, node): - - """ - Invoke the appropriate operator module function for the operation - represented by 'node'. - """ - - temp_fn = self._getOperatorFunction(node) - self._visitCall(node, temp_fn, (node.expr,)) - self.discard_temp(temp_fn) - - def _visitBinaryBit(self, node): - - """ - Need to impose binary rules over a sequence of nodes. The - short-circuiting of the similar logical operators is not imposed by the - bitwise operators. - """ - - temp_fn = self._getOperatorFunction(node) - left = None - - for right in node.nodes: - if left is not None: - self._visitCall(node, temp_fn, (left, right)) - left = right - - self.discard_temp(temp_fn) - - def _visitBinary(self, node): - - """ - Invoke the appropriate operator module function for the operation - represented by 'node'. - """ - - temp_fn = self._getOperatorFunction(node) - self._visitCall(node, temp_fn, (node.left, node.right)) - self.discard_temp(temp_fn) - - def _visitCall(self, node, temp_fn, args): - - """ - Invoke the appropriate operator module function for the operation - represented by 'node', given a 'temp_fn' reference to a function, along - with the 'args' (the operand nodes). - """ - - # Evaluate and store the operands in temporary storage. - - temp_list = [] - - for arg in args: - self.dispatch(arg) - temp_list.append(self.optimiser.optimise_temp_storage()) - - self._generateInvocation(temp_fn, temp_list) - - # Compilation duties... - - for temp in temp_list: - self.discard_temp(temp) - - def _generateInvocation(self, temp_fn, temp_list): - - """ - Invoke the function 'temp_fn' using the operands from 'temp_list' as - arguments. - """ - - self._startCallFunc() - - for i, temp in enumerate(temp_list): - self.new_op(temp) - self.new_op(StoreFrame(i)) - - self._endCallFuncArgs(len(temp_list)) - self._doCallFunc(temp_fn) - self._endCallFunc(temp_fn) - - def _getOperatorFunction(self, node, operator_name=None): - - "Return an operator function reference for the given 'node'." - - return self._generateOperatorFunction(operator_name or node.__class__.__name__) - - def _getOperatorAugAssignFunction(self, node): - - """ - Return an operator augmented assignment function reference for the given - 'node'. - """ - - return self._generateOperatorFunction(node.op) - - def _generateOperatorFunction(self, opname): - - "Return an operator function reference for the given 'opname'." - - operator_fn = operator_functions[opname] - - # Get the operator module. - - operator_module = self.importer.get_module("operator") - - # Get the appropriate function from the operator module. - - self.new_op(LoadAddress(operator_module[operator_fn])) - return self.optimiser.optimise_temp_storage() - - def _handleAttributeError(self, node, temp_method, handled_block): - - """ - Add exception handling to the method acquisition instructions where the - attribute access cannot be resolved at compile-time. - """ - - if not (self.optimiser.should_optimise_known_target() and self.optimiser.is_constant_input(temp_method)): - self.load_builtin("AttributeError", node) - self.new_op(CheckException(target="status")) - self.new_op(JumpIfTrue(handled_block, working="status")) - self.new_op(RaiseException()) - - def _generateTuple(self, node): - - "Make a tuple using the given program 'node'." - - # Reserve space for the elements themselves. - - self.make_instance(self.get_builtin_class("tuple"), len(node.nodes)) - temp = self.get_temp() - - # Store using 0-based index values. - - self._populateSequence(temp, node.nodes) - - self.new_op(temp) - self.discard_temp(temp) - - def _generateList(self, node, nodes=None): - - "Make a list using the given program 'node' and optional 'nodes'." - - nodes = nodes or [] - - # Make a fragment containing the list elements. - - self.new_op(MakeFragment(len(nodes) + 1)) - temp = self.get_temp() - self._populateSequence(temp, nodes) - self.new_op(temp) - self.record_value() - - # Reserve space for _elements (the fragment reference). - - self.make_instance(self.get_builtin_class("list"), 1) - list_temp = self.get_temp() - - self.start_target() - self.new_op(list_temp) - self.new_op(StoreAttr(Attr(0, None, None))) # _elements is at position 0 - self.assign_value() - self.discard_value() - - self.new_op(list_temp) - self.discard_temp(temp) - self.discard_temp(list_temp) - - def _populateSequence(self, temp, nodes, offset=0): - - """ - Populate a sequence using the given 'temp' reference and 'nodes'. - """ - - for i, n in enumerate(nodes): - self.dispatch(n) - self.record_value() - self._storeInSequence(temp, i, offset) - self.discard_value() - - def _storeInSequence(self, temp, i, offset=0): - - """ - Store the current active value in the fragment referenced by 'temp' at - position 'i' with the given starting 'offset'. - """ - - self.start_target() - self.new_op(temp) - self.new_op(StoreAttr(Attr(i + offset, None, None))) - self.assign_value() - - def _generateTestBoolean(self, node, temp): - - """ - Generate a test of the boolean status of the current value for the given - program 'node'. - """ - - # Get method on temp. - # NOTE: Using __bool__ instead of __nonzero__. - - self._generateAttr(node, "__bool__", self.attribute_load_instructions) - temp_method = self.optimiser.optimise_temp_storage() - - self._generateInvocation(temp_method, (temp,)) - - self.discard_temp(temp_method) - - # Convert result to boolean (a StoreBoolean operation). - - self.new_op(TestIdentityAddress(self.importer.get_predefined_constant("True"), target="status")) - - def _generateLoadBoolean(self, node): - - """ - Generate instructions to load the appropriate value given the current - boolean status. - """ - - false_block = self.get_block() - true_block = self.new_block() - end_block = self.new_block() - - self.new_op(JumpIfTrue(true_block, working="status")) - self.new_op(LoadConst(self.importer.get_predefined_constant("False"))) - self.new_op(Jump(end_block)) - - self.set_block(true_block) - self.new_op(LoadConst(self.importer.get_predefined_constant("True"))) - - self.set_block(end_block, [false_block, true_block]) - - # Common AST operations. - - def _startFor(self, node, else_=None): - - """ - Generate the start of a loop using the given 'node' and 'else_' clause, - if defined. The iterator and next, exit and else blocks are returned. - """ - - next_handler_block = self.new_block() - end_handler_block = self.new_block() - exit_block = self.new_block() - next_block = self.new_block() - else_block = self.new_block() - - temp_iterator = self._visitForList(node) - - # In the loop... - - self.set_block(next_block) - - # Handle exceptions when calling "next"... - - self.add_exception_blocks(next_handler_block, end_handler_block) - self.new_op(PushHandler(next_handler_block)) - - # Invoke the next method. - - self._visitForNext(node, temp_iterator) - - # Record the value to be assigned. - - self.record_value() - - # Skip the handler where the call was successful. - - self.new_op(PopHandler(1)) - self.new_op(Jump(end_handler_block)) - - # Enter the exception handler. - - self.set_block(next_handler_block) - self.new_op(PopHandler(1)) - - # Disable the handlers. - - self.drop_exception_blocks() - - # Test for StopIteration. - - self.load_builtin("StopIteration", node) - self.new_op(CheckException(target="status")) - if else_ is not None: - self.new_op(JumpIfTrue(else_block, working="status")) - else: - self.new_op(JumpIfTrue(exit_block, working="status")) - - # Re-raise the exception otherwise. - - self.new_op(RaiseException()) - - # After the handler, clear the exception. - - self.set_block(end_handler_block) - - # Assign to the target. - - self.dispatch(node.assign) - self.discard_value() - - # Process the body with the current next and exit points. - - self.add_loop_blocks(next_block, exit_block) - - return temp_iterator, next_block, exit_block, else_block - - def _endFor(self, node, temp_iterator, next_block, exit_block, else_block=None, else_=None): - - """ - Generate the end of a loop for the given 'node' using the given - 'temp_iterator' and 'next_block', 'exit_block' and 'else_block' - definitions, together with an 'else_' clause, if defined. - """ - - self.drop_loop_blocks() - - # Repeat the loop. - - self.new_op(Jump(next_block)) - - # Produce the "else" section. - - if else_ is not None: - self.set_block(else_block) - self.new_op(ClearException(target="exception")) - self.dispatch(else_) - - # After the loop... - - self.set_block(exit_block) - - else: - # After the loop... - - self.set_block(exit_block) - self.new_op(ClearException(target="exception")) - - # Compilation duties... - - self.discard_temp(temp_iterator) - - def _visitForList(self, node): - - "Get the list to be iterated over, returning its iterator." - - self._startCallFunc() - self.dispatch(node.list) - self._generateAttr(node, "__iter__", self.attribute_load_instructions) - temp_target, target, temp_context = self._generateCallFunc([], node) - self._doCallFunc(temp_target, target) - self._endCallFunc(temp_target, temp_context) - - # Use a long-lasting temporary storage slot, since any result from the - # __iter__ method will not remain around for long. - - return self.get_temp() - - def _visitForNext(self, node, temp_iterator): - - "Use the iterator to get the next value." - - self._startCallFunc() - self.new_op(temp_iterator) - self._generateAttr(node, "next", self.attribute_load_instructions) - temp_target, target, temp_context = self._generateCallFunc([], node) - self._doCallFunc(temp_target, target) - self._endCallFunc(temp_target, temp_context) - - def _visitPrint(self, node, function_name): - self._startCallFunc() - self.load_builtin(function_name, node) - - args = [node.dest or compiler.ast.Name("None")] + node.nodes - - temp_target, target, temp_context = self._generateCallFunc(args, node) - self._doCallFunc(temp_target, target) - self._endCallFunc(temp_target, temp_context) - - def _visitImport(self, node): - - """ - Although imported code already resides in any generated image, the - module code must be executed. Module code uses its own frame for any - temporary storage and thus behaves like a function. - """ - - modules = self.importer.importers.get(node) - if modules: - for module in modules: - self.new_op(MakeFrame(0)) - self.new_op(LoadConst(module)) - self.new_op(LoadCallable()) - self.new_op(JumpWithFrame()) - self.new_op(DropFrame()) - - def _importName(self, module, name, alias, node): - - # Get the source of the name. - - attr = module[name] - self.new_op(LoadAddress(attr)) - - # Record the object in the current namespace. - - self.record_value() - - self.start_target() - self._generateName(alias or name, self.name_store_instructions, node) # AssName equivalent - self.assign_value() - self.discard_value() - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba rsvp.py --- a/rsvp.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,915 +0,0 @@ -#!/usr/bin/env python - -""" -A really simple virtual processor employing a simple set of instructions which -ignore low-level operations and merely concentrate on variable access, structure -access, structure allocation and function invocations. - -Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . - --------- - -The execution model of the virtual processor involves the following things: - - * Memory contains constants, global variable - references and program code - - * PC (program counter) stack contains the return address associated - with each function invocation - - * Frame stack contains invocation frames in use and in - preparation plus temporary storage - - * Local frame pointer stack refers to the frames in the frame stack - - * Invocation frame pointer stack - - * Exception handler stack - - * Exception handler locals stack refers to the state of the local frame - pointer stack - - * Exception handler PC stack refers to the state of the PC stack - - * Registers: working context/value, - assignment source context/value, - current exception value, - boolean status value, - object list start (constant), - parameter list start (constant) -""" - -from micropython.program import DataValue, ReplaceableContext, PlaceholderContext, FragmentObject -from rsvplib import Library - -class IllegalInstruction(Exception): - pass - -class IllegalAddress(Exception): - def __init__(self, address): - self.address = address - def __repr__(self): - return "IllegalAddress(%r)" % self.address - def __str__(self): - return repr(self) - -class EmptyPCStack(Exception): - pass - -class EmptyFrameStack(Exception): - pass - -class BreakpointReached(Exception): - pass - -class RSVPMachine: - - "A really simple virtual processor." - - def __init__(self, memory, objlist, paramlist, pc=None, debug=0, abort_upon_exception=0): - - """ - Initialise the processor with a 'memory' (a list of values containing - instructions and data), the object and parameter lists 'objlist' and - 'paramlist', and the optional program counter 'pc'. - """ - - self.memory = memory - self._objlist = objlist - self._paramlist = paramlist - end_of_code = len(self.memory) - - # Add the object list. - - objlist_start = len(self.memory) - self.memory += objlist.as_raw() - - # Add the parameter list. - - paramlist_start = len(self.memory) - self.memory += paramlist.as_raw() - - # A reference to the native library. - - self.library = None - - # Program counter and machine configuration. - - self.pc = pc or 0 - self.debug = debug - self.abort_upon_exception = abort_upon_exception - - # Profiling. - - self.coverage = [0] * end_of_code - self.counter = 0 - self.cost = 0 - - # Stacks. - - self.pc_stack = [] - self.frame_stack = [] - self.local_sp_stack = [0] - self.invocation_sp_stack = [] - - # Exception handler stacks are used to reset the state of the main - # stacks. - - self.handler_stack = [end_of_code - 1] # final handler is the end of the code - self.handler_local_sp_stack = [] - self.handler_invocation_sp_stack = [] - self.handler_pc_stack = [] - - # Registers. - - self.register_names = ( - "working", - "working_context", - "source", - "source_context", - "exception", - "status", - "objlist", - "paramlist" - ) - self.registers = {} - - for name in self.register_names: - self.registers[name] = None - - self.registers["objlist"] = objlist_start - self.registers["paramlist"] = paramlist_start - - self.instruction = None - - # Constants. - # NOTE: These should eventually be removed once the code using them has - # NOTE: been migrated to the native code library. - - cls = self._get_class("__builtins__", "AttributeError") - self.attr_error = cls.location - self.attr_error_instance = cls.instance_template_location - cls = self._get_class("__builtins__", "TypeError") - self.type_error = cls.location - self.type_error_instance = cls.instance_template_location - cls = self._get_class("__builtins__", "tuple") - self.tuple_class = cls.location - self.tuple_instance = cls.instance_template_location - - # Debugging attributes. - - self.breakpoints = set() - - def get_program_size(self): - return self.registers["objlist"] - - def _get_class(self, module, name): - attr = self._objlist.access(module, name) - if attr is not None: - return attr.get_value() - else: - return None - - # Debugging methods. - - def dump(self): - print "PC", self.pc, "->", self.load(self.pc) - print "PC stack", self.pc_stack - print "Frame stack:" - if self.local_sp_stack: - start = self.local_sp_stack[0] - for end in self.local_sp_stack[1:]: - print " %2d" % start, self.frame_stack[start:end] - start = end - else: - print " %2d" % start, self.frame_stack[start:] - print - print "Local stack pointers", self.local_sp_stack - print "Invocation stack pointers", self.invocation_sp_stack - print "Handler stack", self.handler_stack - print "Handler frame stack", self.handler_local_sp_stack - print "Handler PC stack", self.handler_pc_stack - print - print "Registers:" - print - for name in self.register_names: - print "%s: %s" % (name, self.registers[name]) - print - print "Instruction", self.instruction - - def show(self, start=None, end=None): - self.show_memory(self.memory[start:end], self.coverage[start:end], start or 0) - - def show_pc(self, run_in=10): - start = max(0, self.pc - run_in) - end = self.pc + run_in - memory = self.memory[start:end] - coverage = self.coverage[start:end] - self.show_memory(memory, coverage, start) - - def show_object(self, start): - obj = self.memory[start] - end = start + obj.size - self.show_memory(self.memory[start:end], self.coverage[start:end], start) - - def show_memory(self, memory, coverage, start): - for i, (c, x) in enumerate(map(None, coverage, memory)): - location = start + i - if location == self.pc: - print "->", - elif location in self.pc_stack: - print "..", - else: - print " ", - print "%-5s %5d %r" % (c or "", location, x) - - def step(self, dump=0): - self.execute() - self.show_pc() - if dump: - self.dump() - - def set_break(self, location): - self.breakpoints.add(location) - - def up(self): - retaddr = self.pc_stack[-1] - self.set_break(retaddr) - self.run() - - # Internal operations. - - def load(self, address): - - "Return the value at the given 'address'." - - try: - return self.memory[address] - except IndexError: - raise IllegalAddress(address) - except TypeError: - raise IllegalAddress(address) - - def save(self, address, value): - - "Save to the given 'address' the specified 'value'." - - try: - self.memory[address] = value - except IndexError: - raise IllegalAddress(address) - except TypeError: - raise IllegalAddress(address) - - def new(self, size): - - """ - Allocate space of the given 'size', returning the address of the space. - """ - - addr = len(self.memory) - for i in range(0, size): - self.memory.append(None) - return addr - - def push_pc(self, data): - - "Push 'data' onto the PC stack." - - self.pc_stack.append(data) - - def pull_pc(self): - - "Pull a value from the PC stack and return it." - - try: - return self.pc_stack.pop() - except IndexError: - raise EmptyPCStack - - def run(self): - - "Execute code in the memory, starting from the current PC address." - - breakpoint = 0 - - try: - while 1: - self.execute() - except EmptyPCStack: - pass - except BreakpointReached: - breakpoint = 1 - - print "Execution terminated", - if breakpoint: - print "with breakpoint." - print "At address", self.pc - elif self.registers["exception"] is not None: - ref = self.registers["exception"] - addr = self.load(ref + Library.instance_data_offset) - print "with exception:", self.load(ref) - print "At address %d: %r" % (addr, self.load(addr)) - else: - print "successfully." - print "After", self.counter, "instructions at cost", self.cost, "units." - - def test(self, module): - - """ - Test the code in the memory by running the code and investigating the - contents of variables. Use 'module' to identify result variables. - """ - - self.run() - success = 1 - - if self.registers["exception"] is None: - for name in module.keys(): - if name.startswith("result"): - label, expected = name.split("_") - attr = module[name] - - # Need to skip the header to get at the members. - - attr_location = module.location + Library.instance_data_offset + attr.position - value = self.load(attr_location) - - if value is not None: - content = self.load(value.ref + Library.instance_data_offset) - print label, expected, content - success = success and (int(expected) == content) - else: - print label, expected, "missing" - success = 0 - - return success - else: - return 0 - - def execute(self): - - "Execute code in the memory at the current PC address." - - if self.pc in self.breakpoints: - self.breakpoints.remove(self.pc) - raise BreakpointReached - - self.instruction = self.load(self.pc) - - # Perform the instruction itself. - - next_pc = self.perform(self.instruction) - - # Update the coverage. - - self.coverage[self.pc] += 1 - - # Update the program counter. - - if next_pc is None: - self.pc += 1 - else: - self.pc = next_pc - - def get_method(self, instruction): - - "Return the handler method for the given 'instruction'." - - instruction_name = instruction.__class__.__name__ - if self.debug: - print "%8d %s" % (self.pc, instruction_name) - method = getattr(self, instruction_name, None) - if method is None: - raise IllegalInstruction, (self.pc, instruction_name) - return method - - def perform(self, instruction): - - "Perform the 'instruction', returning the next PC value or None." - - self.counter += 1 - self.cost += instruction.cost - operand = instruction.get_operand() - method = self.get_method(instruction) - return method(operand, instruction) - - def jump(self, addr, next): - - """ - Jump to the subroutine at (or identified by) 'addr'. If 'addr' - identifies a library function then invoke the library function and set - PC to 'next' afterwards; otherwise, set PC to 'addr'. - """ - - # Trap library functions introduced through the use of strings instead - # of proper locations. - - if isinstance(addr, str): - handler = self.library and self.library.native_functions[addr](self.library) - if handler is None: - return next - else: - return handler - else: - self.push_pc(self.pc + 1) - return addr - - # Helper methods. - - def context_of(self, register): - return "%s_context" % register - - def load_from_frame(self, operand): - frame = self.local_sp_stack[-1] - return self.frame_stack[frame + operand] - - # Low-level instructions. - - def LoadImmediate(self, operand, target): - self.registers[target] = operand - - def LoadMemory(self, operand, target, source): - self.registers[target] = self.load( - self.registers[source] + (operand is not None and operand or 0) - ) - - # Instructions. - - def Transfer(self, operand, instruction): - self.registers[instruction.target] = self.registers[instruction.source] - - def LoadConst(self, operand, instruction): - self.LoadImmediate(operand, self.context_of(instruction.target)) - self.LoadImmediate(operand, instruction.target) - - def LoadClass(self, operand, instruction): - self.LoadImmediate(PlaceholderContext, self.context_of(instruction.target)) - self.LoadImmediate(operand, instruction.target) - - def LoadFunction(self, operand, instruction): - self.LoadImmediate(ReplaceableContext, self.context_of(instruction.target)) - self.LoadImmediate(operand, instruction.target) - - def LoadName(self, operand, instruction): - data = self.load_from_frame(operand) - self.LoadImmediate(data.context, self.context_of(instruction.target)) - self.LoadImmediate(data.ref, instruction.target) - - def StoreName(self, operand, instruction): - frame = self.local_sp_stack[-1] - self.frame_stack[frame + operand] = DataValue( - self.registers[self.context_of(instruction.source)], - self.registers[instruction.source]) - - LoadTemp = LoadName - - def StoreTemp(self, operand, instruction): - frame = self.local_sp_stack[-1] - self.frame_stack[frame + operand] = DataValue( - self.registers[self.context_of(instruction.working)], - self.registers[instruction.working]) - - def LoadAddress(self, operand, instruction): - # Preserve context (potentially null). - data = self.load(operand) - self.LoadImmediate(data.context, self.context_of(instruction.target)) - self.LoadImmediate(data.ref, instruction.target) - - def LoadAddressContext(self, operand, instruction): - # Value becomes context. - data = self.load(operand) - self.LoadImmediate(self.registers[instruction.working], self.context_of(instruction.target)) - self.LoadImmediate(data.ref, instruction.target) - - def LoadAddressContextCond(self, operand, instruction): - data = self.load(operand) - data = self._LoadAddressContextCond(data.context, data.ref, self.registers[instruction.working]) - self.LoadImmediate(data.context, self.context_of(instruction.target)) - self.LoadImmediate(data.ref, instruction.target) - - def StoreAddress(self, operand, instruction): - # Preserve context. - self.save(operand, DataValue( - self.registers[self.context_of(instruction.source)], - self.registers[instruction.source])) - - def StoreAddressContext(self, operand, instruction): - # Overwrite context if null. - self._StoreAddressContext(operand, - self.registers[self.context_of(instruction.working)], - self.registers[instruction.working], - self.registers[self.context_of(instruction.source)], - self.registers[instruction.source]) - - def MakeInstance(self, size, instruction): - # NOTE: Referencing the instance template. - addr = self._MakeObject(size, self.registers[instruction.working] - Library.instance_template_size) - # Introduce object as context for the new object. - self.LoadImmediate(addr, self.context_of(instruction.target)) - self.LoadImmediate(addr, instruction.target) - - def MakeFragment(self, size, instruction): - # Reserve twice the amount of space. - addr = self._MakeFragment(size, size * 2) - # NOTE: Context is not relevant for fragments. - self.LoadImmediate(None, self.context_of(instruction.target)) - self.LoadImmediate(addr, instruction.target) - - def LoadAttr(self, pos, instruction): - # Retrieved context should already be appropriate for the instance. - # Skip any header. - data = self.load(self.registers[instruction.working] + Library.instance_data_offset + pos) - self.LoadImmediate(data.context, self.context_of(instruction.target)) - self.LoadImmediate(data.ref, instruction.target) - - def StoreAttr(self, pos, instruction): - # Target should already be an instance. - # Skip any header. - self.save(self.registers[instruction.working] + Library.instance_data_offset + pos , DataValue( - self.registers[self.context_of(instruction.source)], - self.registers[instruction.source])) - - def LoadAttrIndex(self, index, instruction): - data = self.load(self.registers[instruction.working]) - element = self.load(self.registers["objlist"] + data.classcode + index) - - if element is not None: - attr_index, static_attr, offset = element - if attr_index == index: - if static_attr: - data = self.load(offset) # offset is address of class/module attribute - else: - data = self.load(self.registers[instruction.working] + offset) - self.LoadImmediate(data.context, self.context_of(instruction.target)) - self.LoadImmediate(data.ref, instruction.target) - return - - self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance) - return self.RaiseException() - - # LoadAttrIndexContext not defined. - - def LoadAttrIndexContextCond(self, index, instruction): - data = self.load(self.registers[instruction.working]) - element = self.load(self.registers["objlist"] + data.classcode + index) - - if element is not None: - attr_index, static_attr, offset = element - if attr_index == index: - if static_attr: - loaded_data = self.load(offset) # offset is address of class/module attribute - # Absent attrcode == class/module. - if data.attrcode is not None: - loaded_data = self._LoadAddressContextCond( - loaded_data.context, loaded_data.ref, - self.registers[instruction.working]) - else: - loaded_data = self.load(self.registers[instruction.working] + offset) - self.LoadImmediate(loaded_data.context, self.context_of(instruction.target)) - self.LoadImmediate(loaded_data.ref, instruction.target) - return - - self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance) - return self.RaiseException() - - def StoreAttrIndex(self, index, instruction): - data = self.load(self.registers[instruction.working]) - element = self.load(self.registers["objlist"] + data.classcode + index) - - if element is not None: - attr_index, static_attr, offset = element - if attr_index == index: - if static_attr: - self._StoreAddressContext(offset, - self.registers[self.context_of(instruction.working)], - self.registers[instruction.working], - self.registers[self.context_of(instruction.source)], - self.registers[instruction.source]) - return - else: - self.save(self.registers[instruction.working] + offset, DataValue( - self.registers[self.context_of(instruction.source)], - self.registers[instruction.source])) - return - - self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance) - return self.RaiseException() - - # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. - - def MakeFrame(self, size, instruction): - self.invocation_sp_stack.append(len(self.frame_stack)) - self.frame_stack.extend([None] * size) - - def DropFrame(self, operand, instruction): - self.local_sp_stack.pop() - frame = self.invocation_sp_stack.pop() - del self.frame_stack[frame:] # reset stack before call - - def StoreFrame(self, pos, instruction): - frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame - self.frame_stack[frame + pos] = DataValue( - self.registers[self.context_of(instruction.working)], - self.registers[instruction.working]) - - def StoreFrameIndex(self, index, instruction): - frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame - data = self.load(self.registers[instruction.working]) - element = self.load(self.registers["paramlist"] + data.funccode + index) - - if element is not None: - # NOTE: Need to ensure correct positioning where a context has been generated. - param_index, offset = element - if param_index == index: - self.frame_stack[frame + offset] = DataValue( - self.registers[self.context_of(instruction.source)], - self.registers[instruction.source]) - return - - self.registers["exception"] = self._MakeObject(Library.instance_size, self.type_error_instance) - return self.RaiseException() - - def LoadCallable(self, operand, instruction): - data = self.load(self.registers[instruction.working]) - self.registers[instruction.target] = data.codeaddr - - def StoreCallable(self, operand, instruction): - # NOTE: Should improve the representation and permit direct saving. - data = self.load(self.registers[instruction.working]) - self.save(self.registers[instruction.working], data.with_callable(self.registers[instruction.source])) - - def CheckContext(self, operand, instruction): - self.registers[instruction.target] = self.registers[instruction.working] is not ReplaceableContext - - def CheckClass(self, operand, instruction): - if self.registers[instruction.working] in (ReplaceableContext, PlaceholderContext): - self.registers[instruction.target] = 0 - return - - data = self.load(self.registers[instruction.working]) - - # Classes are not themselves usable as the self argument. - # NOTE: This may change at some point. - # However, where classes appear as the context, instance - # compatibility is required in the first argument. - - self.registers[instruction.target] = data.attrcode is None # absent attrcode == class - - def CheckFrame(self, operand, instruction): - (nargs, ndefaults) = operand - - # The frame is actually installed as the locals. - # Retrieve the context from the first local. - - frame = self.local_sp_stack[-1] - nlocals = len(self.frame_stack[frame:]) - - if not ((nargs - ndefaults) <= nlocals): - raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (operand, nargs - ndefaults, nlocals, nargs) - #self.registers["exception"] = self._MakeObject(Library.instance_size, self.type_error_instance) - #return self.RaiseException() - - def CheckExtra(self, nargs, instruction): - - # The frame is actually installed as the locals. - # Retrieve the context from the first local. - - frame = self.local_sp_stack[-1] - nlocals = len(self.frame_stack[frame:]) - - # Provide the extra star parameter if necessary. - - if nlocals == nargs: - self.frame_stack.extend([None]) # ExtendFrame(1) - - def FillDefaults(self, operand, instruction): - (nargs, ndefaults) = operand - - # The frame is actually installed as the locals. - - frame = self.local_sp_stack[-1] - nlocals = len(self.frame_stack[frame:]) - - # Support population of defaults. - # This involves copying the "attributes" of a function into the frame. - - default = nlocals - (nargs - ndefaults) - self.frame_stack.extend([None] * (nargs - nlocals)) - pos = nlocals - - while pos < nargs: - - # Skip any header. - - self.frame_stack[frame + pos] = self.load(self.registers[instruction.working] + Library.instance_data_offset + default) - default += 1 - pos += 1 - - def CopyExtra(self, start, instruction): - - # The frame is the source of the extra arguments. - - frame = self.local_sp_stack[-1] - nlocals = len(self.frame_stack[frame:]) - - # Make a tuple to hold the arguments. - - ref = self._MakeObject(nlocals - start + 1, self.tuple_instance) - - extra = 0 - pos = start - - while pos < nlocals: - - # Skip any header. - - self.save(ref + Library.instance_data_offset + extra, self.frame_stack[frame + pos]) - extra += 1 - pos += 1 - - self.LoadImmediate(ref, self.context_of(instruction.working)) - self.LoadImmediate(ref, instruction.working) - - def CheckInstance(self, operand, instruction): - # For the 'self' parameter in an invoked function, the proposed context - # ('self') is checked against the target's context. - self.registers[instruction.target] = self._CheckInstance( - self.registers[instruction.working], - self.registers[instruction.source]) - - def JumpInFrame(self, operand, instruction): - codeaddr = self.registers[instruction.working] - return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one - - def JumpInFrameDirect(self, addr, instruction): - return self.jump(addr, self.pc + 1) # return to the instruction after this one - - def JumpWithFrame(self, operand, instruction): - codeaddr = self.registers[instruction.working] - self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame - return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one - - def JumpWithFrameDirect(self, addr, instruction): - self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame - return self.jump(addr, self.pc + 1) # return to the instruction after this one - - def ExtendFrame(self, size, instruction): - self.frame_stack.extend([None] * size) - - def AdjustFrame(self, size, instruction): - self.invocation_sp_stack[-1] += size - - def Return(self, operand, instruction): - return self.pull_pc() - - def Jump(self, addr, instruction): - return addr - - def JumpIfTrue(self, addr, instruction): - if self.registers[instruction.working]: - return addr - - def JumpIfFalse(self, addr, instruction): - if not self.registers[instruction.working]: - return addr - - def ClearException(self, operand, instruction): - self.LoadImmediate(None, instruction.target) - - def RaiseException(self, operand=None, instruction=None): - # NOTE: Adding the program counter as the first attribute after __class__. - self.save(self.registers["exception"] + Library.instance_data_offset, self.pc) - # Jumping to the current handler. - if self.abort_upon_exception: - raise Exception - return self.handler_stack[-1] - - def PushHandler(self, addr, instruction): - self.handler_stack.append(addr) - self.handler_local_sp_stack.append(len(self.local_sp_stack)) - self.handler_invocation_sp_stack.append(len(self.invocation_sp_stack)) - self.handler_pc_stack.append(len(self.pc_stack)) - - def PopHandler(self, nframes, instruction): - # Get the new local frame pointer and PC stack references. - local_sp_top = self.handler_local_sp_stack[-nframes] - invocation_sp_top = self.handler_invocation_sp_stack[-nframes] - pc_top = self.handler_pc_stack[-nframes] - # Reduce the local frame pointer stack to refer to the handler's frame. - del self.local_sp_stack[local_sp_top:] - # Reduce the invocation frame pointer stack to refer to an outer frame. - del self.invocation_sp_stack[invocation_sp_top:] - # Reduce the PC stack to discard all superfluous return addresses. - del self.pc_stack[pc_top:] - # Remove elements from the handler stacks. - del self.handler_pc_stack[-nframes:] - del self.handler_local_sp_stack[-nframes:] - del self.handler_invocation_sp_stack[-nframes:] - del self.handler_stack[-nframes:] - - def CheckException(self, operand, instruction): - self.registers[instruction.target] = self.registers["exception"] is not None and \ - self._CheckInstance(self.registers["exception"], self.registers[instruction.working]) - - def TestIdentity(self, operand, instruction): - self.registers[instruction.target] = self.registers[instruction.working] == self.registers[instruction.source] - - def TestIdentityAddress(self, addr, instruction): - self.registers[instruction.target] = self.registers[instruction.working] == addr - - def InvertBoolean(self, operand, instruction): - self.registers[instruction.target] = not self.registers[instruction.source] - - # Common implementation details. - - def _CheckInstance(self, ref, cls): - data = self.load(ref) - target_data = self.load(cls) - - # Insist on instance vs. class. - - if data.attrcode is None: # absent attrcode == class/module - return 0 - - if target_data.attrcode is not None: # present attrcode == instance - return 0 - - # Find the table entry for the descendant. - - element = self.load(self.registers["objlist"] + target_data.classcode + data.attrcode) - - if element is not None: - attr_index, static_attr, offset = element - return attr_index == data.attrcode - else: - return 0 - - def _MakeObject(self, size, ref): - # Load the template. - data = self.load(ref) - addr = self.new(size) - # Save the header, overriding the size. - self.save(addr, data.with_size(size)) - return addr - - def _MakeFragment(self, occupied, size): - addr = self.new(size) - # Save the header, overriding the size. - self.save(addr, FragmentObject(occupied, size)) - return addr - - def _LoadAddressContextCond(self, context, value, inst_value): - # Check the instance context against the target's context. - # This provides the context overriding for methods. - if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_value, context): - # Replace the context with the instance. - return DataValue(inst_value, value) - else: - return DataValue(context, value) - - def _StoreAddressContext(self, location, context, value, source_context, source_value): - if source_context is ReplaceableContext: - context = value - else: - context = source_context - self.save(location, DataValue(context, source_value)) - -# Convenience functions. - -def machine(program, with_builtins=0, debug=0, abort_upon_exception=0): - print "Making the image..." - code = program.get_image(with_builtins) - print "Getting raw structures..." - ot = program.get_object_table() - pt = program.get_parameter_table() - objlist = ot.as_list() - paramlist = pt.as_list() - print "Getting raw image..." - rc = program.get_raw_image() - print "Initialising the machine..." - importer = program.get_importer() - constants = {} - for x in (True, False, NotImplemented): - constants[x] = importer.get_constant(x).location - rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception) - library = Library(rm, constants) - rm.library = library - rm.pc = program.code_location - print "Returning program occupying %d locations." % rm.get_program_size() - return rm - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba rsvplib.py --- a/rsvplib.py Fri Jun 28 21:17:02 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,484 +0,0 @@ -#!/usr/bin/env python - -""" -A native function library for a really simple virtual processor. -NOTE: Ultimately, this should only implement only operations at the lowest -NOTE: possible level, with generated native methods providing the functionality -NOTE: instead. - -Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from micropython.program import DataValue -import operator - -class Library: - - "Native function implementations." - - # NOTE: These attributes need changing if the instance layout changes. - - instance_template_size = instance_data_offset = 1 - instance_size = instance_template_size + 1 - fragment_data_offset = 1 - - def __init__(self, machine, constants): - - """ - Initialise the library with the 'machine' and the 'constants' addresses - dictionary. - """ - - self.machine = machine - self.constants = constants - - # Native class constants. - - self.int_class, self.int_instance = self._get_builtin_class_and_template("int") - self.list_class, self.list_instance = self._get_builtin_class_and_template("list") - self.index_error, self.index_error_instance = self._get_builtin_class_and_template("IndexError") - self.str_class, self.str_instance = self._get_builtin_class_and_template("basestring") - self.accessor_class, self.accessor_instance = self._get_builtin_class_and_template("_accessor") - - self.tuple_class = self.machine.tuple_class - self.tuple_instance = self.machine.tuple_instance - - self.attr_error_instance = self.machine.attr_error_instance - self.type_error_instance = self.machine.type_error_instance - - def _get_builtin_class_and_template(self, name): - cls = self.machine._get_class("__builtins__", name) - if cls is not None: - return cls.location, cls.instance_template_location - else: - return None, None - - def _check_index(self, pos, nelements): - return pos >= 0 and pos < nelements - - # Native functionality. - - def builtins_no_op(self): - pass - - def native_int_arithmetic_op(self, op): - left = self.machine.load_from_frame(0) - left_data = left.ref + self.instance_data_offset - right = self.machine.load_from_frame(1) - right_data = right.ref + self.instance_data_offset - - # Make a new object. - - addr = self.machine._MakeObject(self.instance_size, self.int_instance) - - # Store the result. - # NOTE: The data is considered ready to use. - - self.machine.save(addr + self.instance_data_offset, op(self.machine.load(left_data), self.machine.load(right_data))) - - # Return the new object. - # Introduce object as context for the new object. - - self.machine.LoadImmediate(addr, "working_context") - self.machine.LoadImmediate(addr, "working") - - def native_logical_op(self, op): - left = self.machine.load_from_frame(0) - left_data = left.ref + self.instance_data_offset - right = self.machine.load_from_frame(1) - right_data = right.ref + self.instance_data_offset - - # Test the data. - # NOTE: The data is considered ready to use. - - if op(self.machine.load(left_data), self.machine.load(right_data)): - self.machine.LoadImmediate(self.constants[True], "working_context") - self.machine.LoadImmediate(self.constants[True], "working") - else: - self.machine.LoadImmediate(self.constants[False], "working_context") - self.machine.LoadImmediate(self.constants[False], "working") - - # Operators. - # Although this takes a short-cut by using the operator module, testing is - # still performed on the operands to ensure that they qualify for these - # native operations. - - def native_int_add(self): - return self.native_int_arithmetic_op(operator.add) - - def native_int_sub(self): - return self.native_int_arithmetic_op(operator.sub) - - def native_int_pow(self): - return self.native_int_arithmetic_op(operator.pow) - - def native_int_and(self): - return self.native_int_arithmetic_op(operator.and_) - - def native_int_or(self): - return self.native_int_arithmetic_op(operator.or_) - - def native_int_lt(self): - return self.native_logical_op(operator.lt) - - def native_int_gt(self): - return self.native_logical_op(operator.gt) - - def native_int_eq(self): - return self.native_logical_op(operator.eq) - - def native_str_lt(self): - return self.native_logical_op(operator.lt) - - def native_str_gt(self): - return self.native_logical_op(operator.gt) - - def native_str_eq(self): - return self.native_logical_op(operator.eq) - - # Specific operator methods. - - def builtins_int_neg(self): - left = self.machine.load_from_frame(0) - left_data = left.ref + self.instance_data_offset - - # Make a new object. - - addr = self.machine._MakeObject(self.instance_size, self.int_instance) - - # Store the result. - # NOTE: The data is considered ready to use. - - self.machine.save(addr + self.instance_data_offset, -self.machine.load(left_data)) - - # Return the new object. - # Introduce object as context for the new object. - - self.machine.LoadImmediate(addr, "working_context") - self.machine.LoadImmediate(addr, "working") - - # Various built-in methods. - - def builtins_list_new(self): - list_value = self.machine.load_from_frame(0) - list_addr = list_value.ref + self.instance_data_offset - - # Make a new sequence. - # NOTE: Using an arbitrary size. - - new_fragment = self.machine._MakeFragment(self.fragment_data_offset, 5) # include the header - - # Complete the list instance by saving the fragment reference. - # NOTE: This requires an attribute in the list structure. - - self.machine.save(list_addr, DataValue(None, new_fragment)) - - def builtins_list_get_single_item(self): - obj_value = self.machine.load_from_frame(0) - fragment_member = obj_value.ref + self.instance_data_offset - item_value = self.machine.load_from_frame(1) - item_pos_member = item_value.ref + self.instance_data_offset - - # Get the fragment address. - - fragment = self.machine.load(fragment_member) - - # Get the fragment header. - - header = self.machine.load(fragment.ref) - nelements = header.occupied_size - self.fragment_data_offset - - # Get the item position. - - item_pos = self.machine.load(item_pos_member) - - if not self._check_index(item_pos, nelements): - self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.index_error_instance), "exception") - return self.machine.RaiseException() - - # Get the item itself. - - data = self.machine.load(fragment.ref + self.fragment_data_offset + item_pos) - self.machine.LoadImmediate(data.context, "working_context") - self.machine.LoadImmediate(data.ref, "working") - - def builtins_list_len(self): - obj_value = self.machine.load_from_frame(0) - fragment_member = obj_value.ref + self.instance_data_offset - - # Get the fragment address. - - fragment = self.machine.load(fragment_member) - - # Get the fragment header. - - header = self.machine.load(fragment.ref) - nelements = header.occupied_size - self.fragment_data_offset - - # Make a new object. - - addr = self.machine._MakeObject(self.instance_size, self.int_instance) - - # Store the result. - # NOTE: The data is considered ready to use. - - self.machine.save(addr + self.instance_data_offset, nelements) - - # Return the new object. - # Introduce object as context for the new object. - - self.machine.LoadImmediate(addr, "working_context") - self.machine.LoadImmediate(addr, "working") - - def builtins_list_append(self): - obj_value = self.machine.load_from_frame(0) - fragment_member = obj_value.ref + self.instance_data_offset - arg_value = self.machine.load_from_frame(1) - - # Get the fragment address. - - fragment = self.machine.load(fragment_member) - - # Get the fragment header. - - header = self.machine.load(fragment.ref) - - # Attempt to add the reference. - - if header.occupied_size < header.allocated_size: - self.machine.save(fragment.ref + header.occupied_size, arg_value) - header.occupied_size += 1 - else: - - # Make a new fragment, maintaining more space than currently - # occupied in order to avoid reallocation. - - new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2) - - # Copy existing elements. - - for i in range(self.fragment_data_offset, header.occupied_size): - self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i)) - - self.machine.save(new_fragment + header.occupied_size, arg_value) - - # Set the new fragment in the object. - # NOTE: The old fragment could be deallocated. - - self.machine.save(fragment_member, DataValue(None, new_fragment)) - - def builtins_tuple_new(self): - - # Get the sequence address. - # The first argument should be empty since this function acts as an - # instantiator, and in instantiators the first argument is reserved so - # that it can be filled in for the call to an initialiser without - # allocating a new frame. - - obj_value = self.machine.load_from_frame(1) - return self._builtins_tuple(obj_value) - - def builtins_tuple(self): - - # Get the sequence address. - - obj_value = self.machine.load_from_frame(0) - return self._builtins_tuple(obj_value) - - def _builtins_tuple(self, obj_value): - fragment_member = obj_value.ref + self.instance_data_offset - - if self.machine._CheckInstance(obj_value.ref, self.tuple_class): - self.machine.LoadImmediate(obj_value.context, "working_context") - self.machine.LoadImmediate(obj_value.ref, "working") - return - - # Reject non-list, non-tuple types. - # NOTE: This should probably accept any sequence. - - elif not self.machine._CheckInstance(obj_value.ref, self.list_class): - self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.type_error_instance), "exception") - return self.machine.RaiseException() - - # Get the fragment address. - - fragment = self.machine.load(fragment_member) - - # Get the fragment header. - - header = self.machine.load(fragment.ref) - - # Make a new object. - - addr = self.machine._MakeObject(self.instance_data_offset + header.occupied_size - self.fragment_data_offset, self.tuple_instance) - - # Copy the fragment contents into the tuple. - # NOTE: This might be done by repurposing the fragment in some situations. - - for i in range(self.fragment_data_offset, header.occupied_size): - self.machine.save(addr + self.instance_data_offset + i - self.fragment_data_offset, self.machine.load(fragment.ref + i)) - - # Return the new object. - # Introduce object as context for the new object. - - self.machine.LoadImmediate(addr, "working_context") - self.machine.LoadImmediate(addr, "working") - - def builtins_tuple_len(self): - obj_value = self.machine.load_from_frame(0) - - # Get the header. - - header = self.machine.load(obj_value.ref) - nelements = header.size - self.instance_data_offset - - # Make a new object. - - addr = self.machine._MakeObject(self.instance_size, self.int_instance) - - # Store the result. - # NOTE: The data is considered ready to use. - - self.machine.save(addr + self.instance_data_offset, nelements) - - # Return the new object. - # Introduce object as context for the new object. - - self.machine.LoadImmediate(addr, "working_context") - self.machine.LoadImmediate(addr, "working") - - def builtins_tuple_get_single_item(self): - obj_value = self.machine.load_from_frame(0) - fragment_member = obj_value.ref + self.instance_data_offset - item_value = self.machine.load_from_frame(1) - item_pos_member = item_value.ref + self.instance_data_offset - - # Get the header. - - header = self.machine.load(obj_value.ref) - nelements = header.size - self.instance_data_offset - - # NOTE: Assume single location for data and header. - - item_pos = self.machine.load(item_pos_member) - - if not self._check_index(item_pos, nelements): - self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.index_error_instance), "exception") - return self.machine.RaiseException() - - # Get the item. - - data = self.machine.load(fragment_member + item_pos) - self.machine.LoadImmediate(data.context, "working_context") - self.machine.LoadImmediate(data.ref, "working") - - def builtins_getattr(self): - obj_value = self.machine.load_from_frame(0) - name_value = self.machine.load_from_frame(1) - index_member = name_value.ref + self.instance_data_offset + 1 - - if not self.machine._CheckInstance(name_value.ref, self.accessor_class): - self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.attr_error_instance), "exception") - return self.machine.RaiseException() - - # Get the object table index from the name. It is a bare integer, not a reference. - - index = self.machine.load(index_member) - - # NOTE: This is very much like LoadAttrIndexContextCond. - - data = self.machine.load(obj_value.ref) - element = self.machine.load(self.machine.registers["objlist"] + data.classcode + index) - - if element is not None: - attr_index, static_attr, offset = element - if attr_index == index: - if static_attr: - loaded_data = self.machine.load(offset) # offset is address of class/module attribute - if data.attrcode is not None: # absent attrcode == class/module - loaded_data = self.machine._LoadAddressContextCond(loaded_data.context, loaded_data.ref, obj_value.ref) - else: - loaded_data = self.machine.load(obj_value.ref + offset) - self.machine.LoadImmediate(loaded_data.context, "working_context") - self.machine.LoadImmediate(loaded_data.ref, "working") - return - - self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.attr_error_instance), "exception") - return self.machine.RaiseException() - - def builtins_isinstance(self): - obj_value = self.machine.load_from_frame(0) - cls_value = self.machine.load_from_frame(1) - - if self.machine._CheckInstance(obj_value.ref, cls_value.ref): - self.machine.LoadImmediate(self.constants[True], "working_context") - self.machine.LoadImmediate(self.constants[True], "working") - else: - self.machine.LoadImmediate(self.constants[False], "working_context") - self.machine.LoadImmediate(self.constants[False], "working") - - def builtins_print(self): - # NOTE: Do nothing for now. - pass - - def builtins_printnl(self): - # NOTE: Do nothing for now. - pass - - native_functions = { - - # Native method implementations: - - "native._int_add" : native_int_add, - "native._int_sub" : native_int_sub, - "native._int_pow" : native_int_pow, - "native._int_and" : native_int_and, - "native._int_or" : native_int_or, - "native._int_lt" : native_int_lt, - "native._int_gt" : native_int_gt, - "native._int_eq" : native_int_eq, - "native._str_lt" : native_str_lt, - "native._str_gt" : native_str_gt, - "native._str_eq" : native_str_eq, - "__builtins__.int.__neg__" : builtins_int_neg, - "__builtins__.list.__get_single_item__" : builtins_list_get_single_item, - "__builtins__.list.__len__" : builtins_list_len, - "__builtins__.list.append" : builtins_list_append, - "__builtins__.tuple" : builtins_tuple_new, - "__builtins__.tuple.__len__" : builtins_tuple_len, - "__builtins__.tuple.__get_single_item__" : builtins_tuple_get_single_item, - - # Native initialisers: - - "__builtins__.BaseException.__init__" : builtins_no_op, # NOTE: To be made distinct, potentially in the builtins module. - - # Native functions: - - "__builtins__._getattr" : builtins_getattr, - - # Native instantiator helpers: - - "__builtins__.list.__new__" : builtins_list_new, - - # Native helper functions: - - "__builtins__._isinstance" : builtins_isinstance, - "__builtins__._print" : builtins_print, - "__builtins__._printnl" : builtins_printnl, - "__builtins__._tuple" : builtins_tuple, - } - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 979e2a19fc4a -r b772a168a2ba test.py --- a/test.py Fri Jun 28 21:17:02 2013 +0200 +++ b/test.py Sat Jun 29 01:28:12 2013 +0200 @@ -5,7 +5,6 @@ import micropython.deduce import micropython.report import micropython.syspython -import rsvp import sys import os @@ -14,16 +13,6 @@ "/usr/share/micropython/lib" ] -def show_code(code): - for i, x in enumerate(code): - print i, x - -def show_table_usage(raw_table, slice_size=100): - for x in xrange(0, len(raw_table), slice_size): - table_slice = raw_table[x:x+slice_size] - print "%6d" % (len(table_slice) - table_slice.count(None)), \ - "".join(entry and "#" or "_" for entry in table_slice) - def show_warnings(attribute_usage_failures): failures = list(attribute_usage_failures) failures.sort() @@ -33,25 +22,12 @@ print >>sys.stderr, "%s: Name %r with %s attributes %r" % ( unit_name, name, all_attributes and "all" or "any", ", ".join(attrnames)) -def attrs(obj): - for name, attr in obj.items(): - print name, attr - -def reset(): - global rm - rm = rsvp.machine(p) - # Main program. if __name__ == "__main__": args = sys.argv[2:] path = libdirs + sys.path[:] - if "--help" in sys.argv: - print "Optimisations:" - print micropython.cmd.show_optimisations() - sys.exit(1) - if len(sys.argv) > 1 and sys.argv[1] != "-": filename = os.path.abspath(sys.argv[1]) path.append(os.path.split(filename)[0]) @@ -124,17 +100,6 @@ finally: f.close() - # Build the program. - - if "-m" in args or "-t" in args: - rm = rsvp.machine(p, debug=("-g" in args), abort_upon_exception=("-x" in args)) - - if "-t" in args: - success = rm.test(m) - print "Test successful?", success - - print "RSVP machine: rm = %r" % rm - ot = p.get_object_table() pt = p.get_parameter_table()