# HG changeset patch # User Paul Boddie # Date 1200186886 -3600 # Node ID 33a0e574dfa036e26a3cec3959ab50bd80b6a25c # Parent 3b4392ceb09a17d27eb179e88bf89523f43ff362 Added some support for invocations and constants. Integrated the tables into the image generation process. Added back references from AST nodes to class and function objects, although these may not be useful ultimately. diff -r 3b4392ceb09a -r 33a0e574dfa0 README.txt --- a/README.txt Tue Dec 11 00:55:23 2007 +0100 +++ b/README.txt Sun Jan 13 02:14:46 2008 +0100 @@ -97,3 +97,73 @@ -------------------- Attribute access needs to go through the attribute lookup table. + +Instruction Evaluation Model +============================ + +Programs use a value stack where evaluated instructions may save their +results. A value stack pointer indicates the top of this stack. In addition, a +separate stack is used to record the invocation frames. All stack pointers +refer to the next address to be used by the stack, not the address of the +uppermost element. + + Frame Stack Value Stack + ----------- ----------- Address of Callable + ------------------- + previous ... + current ------> callable -----> identifier + arg1 reference to code + arg2 + arg3 + local4 + local5 + ... + +Loading local names is a matter of performing frame-relative accesses to the +value stack. + +Invocations and Argument Evaluation +----------------------------------- + +When preparing for an invocation, the caller first sets the invocation frame +pointer. Then, positional arguments are added to the stack such that the first +argument positions are filled. A number of stack locations for the remaining +arguments specified in the program are then reserved. The names of keyword +arguments are used (in the form of table index values) to consult the +parameter table and to find the location in which such arguments are to be +stored. + + fn(a, b, d=1, e=2, c=3) -> fn(a, b, c, d, e) + + Value Stack + ----------- + + ... ... ... ... + fn fn fn fn + a a a a + b b b b + ___ ___ ___ --> 3 + ___ --> 1 1 | 1 + ___ | ___ --> 2 | 2 + 1 ----------- 2 ----------- 3 ----------- + +Conceptually, the frame can be considered as a collection of attributes, as +seen in other kinds of structures: + +Frame for invocation of fn: + + 0 1 2 3 4 5 + code a b c d e + reference + +However, where arguments are specified positionally, such "attributes" are not +set using a comparable approach to that employed with other structures. +Keyword arguments are set using an attribute-like mechanism, though, where the +position of each argument discovered using the parameter table. + +Tuples, Frames and Allocation +----------------------------- + +Using the approach where arguments are treated like attributes in some kind of +structure, we could choose to allocate frames in places other than a stack. +This would produce something somewhat similar to a plain tuple object. diff -r 3b4392ceb09a -r 33a0e574dfa0 micropython/__init__.py --- a/micropython/__init__.py Tue Dec 11 00:55:23 2007 +0100 +++ b/micropython/__init__.py Sun Jan 13 02:14:46 2008 +0100 @@ -73,10 +73,13 @@ return self.modules.values() - def get_image(self): + def get_image(self, objtable=None, paramtable=None): "Return a dictionary mapping modules to structures." + objtable = objtable or self.get_object_table() + paramtable = paramtable or self.get_parameter_table() + image = [] for module_name, module in self.modules.items(): @@ -85,7 +88,14 @@ # Position the module in the image and make a translation. module.location = pos - trans = micropython.ast.Translation(module) + trans = micropython.ast.Translation(module, objtable, paramtable) + + # Append constants to the image. + + for const in module.constants: + const.location = pos + image.append(const) + pos += 1 # Append module attributes to the image. diff -r 3b4392ceb09a -r 33a0e574dfa0 micropython/ast.py --- a/micropython/ast.py Tue Dec 11 00:55:23 2007 +0100 +++ b/micropython/ast.py Sun Jan 13 02:14:46 2008 +0100 @@ -51,14 +51,20 @@ "A translated module." - def __init__(self, module): + def __init__(self, module, objtable, paramtable): - "Initialise the translation with an inspected 'module'." + """ + Initialise the translation with an inspected 'module' and an attribute + table 'objtable' and parameter table 'paramtable'. + """ ASTVisitor.__init__(self) self.visitor = self self.module = module + self.objtable = objtable + self.paramtable = paramtable self.unit = None + self.constants = {} # Wiring within the code. @@ -114,7 +120,9 @@ label.location = len(self.code) + self.unit.code_location def new_op(self, op): + print self.module.name, len(self.code) + self.unit.code_location, self.code.append(op) + print op # Visitor methods. @@ -151,16 +159,16 @@ builtins = micropython.inspect.builtins.module_attributes() self.new_op(AttrInstruction(builtins[name])) - def visitAdd(self, node): pass + def visitAdd(self, node): - """ - _t1 = node.left - _t2 = node.right - try: - _t1.__add__(_t2) - except AttributeError: - _t2.__radd__(_t1) - """ + """ + _t1 = node.left + _t2 = node.right + try: + _t1.__add__(_t2) + except AttributeError: + _t2.__radd__(_t1) + """ def visitAnd(self, node): pass @@ -192,11 +200,61 @@ def visitBreak(self, node): pass - def visitCallFunc(self, node): pass + def visitCallFunc(self, node): + + """ + Evaluate positional arguments, evaluate and store keyword arguments in + the correct location, then invoke the function. + """ + + # Record the location of the invocation. + + self.new_op(MakeFrame()) # records the start of the frame + + # Evaluate the target. + + self.dispatch(node.node) + + # Evaluate the arguments. + + for i, arg in enumerate(node.args): + if isinstance(arg, compiler.ast.Keyword): + self.dispatch(arg.expr) + + # Combine the target details with the name to get the location. + # See the access method on the Table class. + + paramindex = self.paramtable.get_index(arg.name) + self.new_op(StoreFrame(paramindex)) + + # use (callable+0)+paramindex+table + # checks embedded offset against (callable+0) + # moves the top of stack to frame+position + + else: + self.dispatch(arg) + + self.new_op(LoadCallable()) # uses the start of the frame to get the callable + self.new_op(Jump()) + + # NOTE: Exception handling required. + + self.new_op(DropFrame()) def visitClass(self, node): pass - def visitCompare(self, node): pass + def visitCompare(self, node): + + """ + self.dispatch(node.expr) + for op_name, next_node in compare.ops: + methods = self.comparison_methods[op_name] + if methods is not None: + # Generate method call using evaluated argument and next node. + else: + # Deal with the special operators. + # Provide short-circuiting. + """ def visitConst(self, node): pass @@ -228,9 +286,11 @@ # Only store the name when visiting this node from outside. if self.unit is self.module: + self.new_op(LoadConst(node.function)) self._visitName(node, (StoreName, StoreAttr)) else: self.dispatch(node.code) + self.new_op(Return()) def visitGenExpr(self, node): pass @@ -305,7 +365,10 @@ def visitRaise(self, node): pass - def visitReturn(self, node): pass + def visitReturn(self, node): + if node.value is not None: + self.dispatch(node.value) + self.new_op(Return()) def visitRightShift(self, node): pass @@ -335,4 +398,19 @@ def visitYield(self, node): pass + # Useful data. + + comparison_methods = { + "==" : ("__eq__", "__ne__"), + "!=" : ("__ne__", "__eq__"), + "<" : ("__lt__", "__gt__"), + "<=" : ("__le__", "__ge__"), + ">=" : ("__ge__", "__le__"), + ">" : ("__gt__", "__lt__"), + "is" : None, + "is not" : None, + "in" : None, + "not in" : None + } + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 3b4392ceb09a -r 33a0e574dfa0 micropython/inspect.py --- a/micropython/inspect.py Tue Dec 11 00:55:23 2007 +0100 +++ b/micropython/inspect.py Sun Jan 13 02:14:46 2008 +0100 @@ -123,6 +123,26 @@ def __repr__(self): return "Attr(%d, %r, %r)" % (self.position, self.parent, self.value) +class Const: + + "A constant object." + + def __init__(self, value): + self.value = value + + # Image generation details. + + self.location = None + + def __repr__(self): + return "Const(%r, location=%d)" % (self.value, self.location) + + def __eq__(self, other): + return self.value == other.value + + def __hash__(self): + return hash(self.value) + class Class(NamespaceDict, Naming): "An inspected class." @@ -354,8 +374,6 @@ def __repr__(self): return "UnresolvedName(%r, %r)" % (self.name, self.parent_name) -# Program visitors. - class Module(NamespaceDict): "An inspected module's core details." @@ -410,6 +428,8 @@ return self.modattr +# Program visitors. + class InspectedModule(ASTVisitor, Module): """ @@ -434,6 +454,7 @@ self.in_init = 0 self.namespaces = [] self.module = None + self.constants = [] def parse(self, filename): @@ -507,6 +528,8 @@ return ASTVisitor.dispatch(self, node, *args) def NOP(self, node): + for n in node.getChildNodes(): + self.dispatch(n) return None visitAdd = NOP @@ -563,6 +586,10 @@ raise InspectError, "Base class %r for class %r in %r is not found: it may be hidden in some way." % (base, self, cls) cls.add_base(base_ref) + # Make a back reference from the node for code generation. + + node.cls = cls + # Make an entry for the class. self.store(node.name, cls) @@ -575,7 +602,10 @@ visitCompare = NOP - visitConst = NOP + def visitConst(self, node): + const = Const(node.value) + if not const in self.constants: + self.constants.append(const) visitContinue = NOP @@ -633,6 +663,10 @@ node ) + # Make a back reference from the node for code generation. + + node.function = function + self.namespaces.append(function) # Current namespace is the function. diff -r 3b4392ceb09a -r 33a0e574dfa0 micropython/rsvp.py --- a/micropython/rsvp.py Tue Dec 11 00:55:23 2007 +0100 +++ b/micropython/rsvp.py Sun Jan 13 02:14:46 2008 +0100 @@ -23,16 +23,38 @@ "A generic instruction." - def __init__(self, attr): + def __init__(self, attr=None): self.attr = attr def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.attr) +# Instructions operating on the value stack. + +class LoadConst(Instruction): pass + +# Access within an invocation frame. + class LoadName(Instruction): pass -class LoadAttr(Instruction): pass class StoreName(Instruction): pass + +# Access to address-relative data. + +class MakeObject(Instruction): pass +# ... DropObject not defined: assume garbage collection. +class LoadAttr(Instruction): pass class StoreAttr(Instruction): pass + +# Access to invocation frames in preparation. + +class MakeFrame(Instruction): pass +class DropFrame(Instruction): pass +class StoreFrame(Instruction): pass + +# Invocation-related instructions. + class Jump(Instruction): pass +class LoadCallable(Instruction): pass +class Return(Instruction): pass # vim: tabstop=4 expandtab shiftwidth=4 diff -r 3b4392ceb09a -r 33a0e574dfa0 micropython/table.py --- a/micropython/table.py Tue Dec 11 00:55:23 2007 +0100 +++ b/micropython/table.py Sun Jan 13 02:14:46 2008 +0100 @@ -159,6 +159,12 @@ self.names = self.names or list(self.attributes) return self.names + def get_index(self, name): + + "Return the index of the given 'name'." + + return self.attribute_names().index(name) + def as_matrix(self): """ diff -r 3b4392ceb09a -r 33a0e574dfa0 tests/call_func.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_func.py Sun Jan 13 02:14:46 2008 +0100 @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +def f(a, b, c): + pass + +f(1, 2, 3) +f(1, b=2, c=3) +f(c=3, b=2, a=1) + +# vim: tabstop=4 expandtab shiftwidth=4