# HG changeset patch # User Paul Boddie # Date 1210535430 -7200 # Node ID 5076b9976f4d432c0aed193a1edcd46520523b59 # Parent e7cd07e72de43f2d0da6b680026bade04291f686 Added a Constant superclass for Const, Class, Function and Module. Fixed binary operator code generation to drop unused right method invocation frames. Added unary operator support. Added usage of the unary and binary operator support for the remaining AST nodes. Added support for lambda inspection. diff -r e7cd07e72de4 -r 5076b9976f4d micropython/ast.py --- a/micropython/ast.py Sun May 11 18:43:58 2008 +0200 +++ b/micropython/ast.py Sun May 11 21:50:30 2008 +0200 @@ -699,6 +699,65 @@ def dispatch(self, node, *args): return ASTVisitor.dispatch(self, node, *args) + def _visitUnary(self, node, method): + + """ + _t = node.expr + try: + _result = _t.__pos__() + except AttributeError: + raise TypeError + """ + + end_call_label = self.new_label() + end_label = self.new_label() + + # Evaluate and store the operand in temporary storage. + + self.dispatch(node.expr) + temp = self._optimise_temp_storage() + + # Produce the invocation. + + self._startCallFunc() + self.new_ops(temp) + + # Get the method on temp. + + self._generateAttr(node, method, (LoadAddress, LoadAttr, LoadAttrIndex)) + + # Add exception handling to the method acquisition instructions where + # the attribute access cannot be resolved at compile-time. + + if not self._optimise_known_target(): + self.dispatch(compiler.ast.Name("AttributeError")) + self.new_op(CheckException()) + self.new_op(JumpIfTrue(end_call_label)) + + temp_method = self._optimise_temp_storage() + + # Add arguments. + + self.new_ops(temp) # Explicit context as first argument. + self._endCallFunc(temp_method) + self.new_op(Jump(end_label)) + + # End method attempt. + + self.set_label(end_call_label) + self.new_op(DropFrame()) # From the method call. + + # Raise a TypeError. + + self.dispatch(compiler.ast.Name("TypeError")) + self.new_op(RaiseException()) + + self.set_label(end_label) + + # Compilation duties... + + self.discard_temp(temp) + def _visitBinary(self, node, left_method, right_method): """ @@ -719,6 +778,7 @@ end_left_label = self.new_label() right_label = self.new_label() + end_right_label = self.new_label() type_error_label = self.new_label() end_label = self.new_label() @@ -787,7 +847,7 @@ if not self._optimise_known_target(): self.dispatch(compiler.ast.Name("AttributeError")) self.new_op(CheckException()) - self.new_op(JumpIfTrue(type_error_label)) + self.new_op(JumpIfTrue(end_right_label)) temp_method = self._optimise_temp_storage() @@ -806,6 +866,11 @@ self.new_op(JumpIfTrue(type_error_label)) self.new_op(Jump(end_label)) + # End right method attempt. + + self.set_label(end_right_label) + self.new_op(DropFrame()) # From the right method call. + # Raise a TypeError. self.set_label(type_error_label) @@ -845,11 +910,14 @@ def visitBackquote(self, node): pass - def visitBitand(self, node): pass + def visitBitand(self, node): + self._visitBinary(node, "__and__", "__rand__") - def visitBitor(self, node): pass + def visitBitor(self, node): + self._visitBinary(node, "__or__", "__ror__") - def visitBitxor(self, node): pass + def visitBitxor(self, node): + self._visitBinary(node, "__xor__", "__rxor__") def visitBreak(self, node): next_label, exit_label = self.get_loop_labels() @@ -1035,13 +1103,15 @@ def visitImport(self, node): pass - def visitInvert(self, node): pass + def visitInvert(self, node): + self._visitUnary(node, "__invert__") def visitKeyword(self, node): pass def visitLambda(self, node): pass - def visitLeftShift(self, node): pass + def visitLeftShift(self, node): + self._visitBinary(node, "__lshift__", "__rlshift__") def visitList(self, node): pass @@ -1073,7 +1143,8 @@ def visitPass(self, node): pass - def visitPower(self, node): pass + def visitPower(self, node): + self._visitBinary(node, "__pow__", "__rpow__") def visitPrint(self, node): pass @@ -1088,7 +1159,8 @@ self.dispatch(compiler.ast.Name("None")) self.new_op(Return()) - def visitRightShift(self, node): pass + def visitRightShift(self, node): + self._visitBinary(node, "__rshift__", "__rrshift__") def visitSlice(self, node): pass @@ -1102,13 +1174,6 @@ def visitSubscript(self, node): pass def visitTryExcept(self, node): - - """ - Enter try block. - Dispatch to code. - - """ - exit_label = self.new_label() handler_label = self.new_label() @@ -1164,9 +1229,11 @@ def visitTuple(self, node): pass - def visitUnaryAdd(self, node): pass + def visitUnaryAdd(self, node): + self._visitUnary(node, "__pos__") - def visitUnarySub(self, node): pass + def visitUnarySub(self, node): + self._visitUnary(node, "__neg__") def visitWhile(self, node): exit_label = self.new_label() diff -r e7cd07e72de4 -r 5076b9976f4d micropython/data.py --- a/micropython/data.py Sun May 11 18:43:58 2008 +0200 +++ b/micropython/data.py Sun May 11 21:50:30 2008 +0200 @@ -25,7 +25,6 @@ * Class * Function * Module - * InspectedModule (derived from Module) All of the above support the Naming interface either explicitly or through general conformance, meaning that all can be asked to provide their 'full_name' @@ -151,7 +150,11 @@ for i, attr in enumerate(self.values()): attr.position = i -# Program data structures. +# Program data structures. There are two separate kinds of structures: those +# with context, which are the values manipulated by programs, and those without +# context, which are typically constant things which are stored alongside the +# program but which are wrapped in context-dependent structures in the running +# program. class Attr: @@ -194,7 +197,13 @@ def __repr__(self): return "Attr(%r, %r, %r, %r, %r)" % (self.position, self.parent, self.name, self.value, self.assignments) -class Const: +class Constant: + + "A superclass for all constant or context-free structures." + + pass + +class Const(Constant): "A constant object with no context." @@ -222,7 +231,7 @@ def value_type_name(self): return "__builtins__." + self.value.__class__.__name__ -class Class(NamespaceDict, Naming): +class Class(NamespaceDict, Naming, Constant): "An inspected class." @@ -521,7 +530,7 @@ self.allattr[name] = attr return self.allattr -class Function(NamespaceDict, Naming): +class Function(NamespaceDict, Naming, Constant): "An inspected function." @@ -689,7 +698,7 @@ function.default_attrs = self.default_attrs return function -class UnresolvedName(NamespaceDict): +class UnresolvedName(NamespaceDict, Constant): "A module, class or function which was mentioned but could not be imported." @@ -713,14 +722,7 @@ else: return self.parent_name -class Instance: - - "A placeholder indicating the involvement of an instance." - - def __repr__(self): - return "Instance()" - -class Module(NamespaceDict): +class Module(NamespaceDict, Constant): "An inspected module's core details." @@ -772,4 +774,14 @@ return dict(self) +# Instances are special in that they need to be wrapped together with context in +# a running program, but they are not generally constant. + +class Instance: + + "A placeholder indicating the involvement of an instance." + + def __repr__(self): + return "Instance()" + # vim: tabstop=4 expandtab shiftwidth=4 diff -r e7cd07e72de4 -r 5076b9976f4d micropython/inspect.py --- a/micropython/inspect.py Sun May 11 18:43:58 2008 +0200 +++ b/micropython/inspect.py Sun May 11 21:50:30 2008 +0200 @@ -112,6 +112,9 @@ if not (self.namespaces and isinstance(self.namespaces[-1], Function)): self.all_objects.add(obj) + def store_lambda(self, obj): + self.all_objects.add(obj) + def store_instance_attr(self, name): "Record instance attribute 'name' in the current class." @@ -147,6 +150,59 @@ self.dispatch(n) return Instance() + def _visitFunction(self, node, name): + + """ + Return a function object for the function defined by 'node' with the + given 'name'. If a lambda expression is being visited, 'name' should be + None. + """ + + function = Function( + name, + self.get_parent(), + node.argnames, + node.defaults, + (node.flags & 4 != 0), + (node.flags & 8 != 0), + self, + node + ) + + # Make a back reference from the node for code generation. + + node.unit = function + + # Process the defaults. + + for n in node.defaults: + self.expr = self.dispatch(n) + if isinstance(self.expr, Attr): + function.store_default(self.expr.value) + else: + function.store_default(self.expr) + + # Enter the function. + + self.namespaces.append(function) + + # Current namespace is the function. + # Previous namespace is the class. + + if name == "__init__" and isinstance(self.namespaces[-2], Class): + self.in_init = 1 + + self.dispatch(node.code) + self.in_init = 0 + self.namespaces.pop() + + if name is not None: + self.store(name, function) + else: + self.store_lambda(function) + + return function + visitAdd = OP visitAnd = OP @@ -298,46 +354,7 @@ return None def visitFunction(self, node): - function = Function( - node.name, - self.get_parent(), - node.argnames, - node.defaults, - (node.flags & 4 != 0), - (node.flags & 8 != 0), - self, - node - ) - - # Make a back reference from the node for code generation. - - node.unit = function - - # Process the defaults. - - for n in node.defaults: - self.expr = self.dispatch(n) - if isinstance(self.expr, Attr): - function.store_default(self.expr.value) - else: - function.store_default(self.expr) - - # Enter the function. - - self.namespaces.append(function) - - # Current namespace is the function. - # Previous namespace is the class. - - if node.name == "__init__" and isinstance(self.namespaces[-2], Class): - self.in_init = 1 - - self.dispatch(node.code) - self.in_init = 0 - self.namespaces.pop() - - self.store(node.name, function) - return function + return self._visitFunction(node, node.name) visitGenExpr = OP @@ -397,7 +414,8 @@ self.keyword_names.add(node.name) return None - visitLambda = OP + def visitLambda(self, node): + return self._visitFunction(node, None) visitLeftShift = OP diff -r e7cd07e72de4 -r 5076b9976f4d tests/lambda.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/lambda.py Sun May 11 21:50:30 2008 +0200 @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +identity = lambda x: x +lambda a, b=2: a + b + +# vim: tabstop=4 expandtab shiftwidth=4