# HG changeset patch # User paulb@localhost.localdomain # Date 1180287948 -7200 # Node ID 52cdfcb4af3be089fbc952e9ab35685aeb94255f # Parent 1233b49abe978bea98e78dc63dd267ebbf357795 Renamed the simplify module to ast; added an empty package file. Fixed various imports which no longer functioned correctly. Fixed the configuration of Class, placing it at the top level of the simplified package. Added a new class to the viewer module. Improved the test of classes and methods. diff -r 1233b49abe97 -r 52cdfcb4af3b simplify/__init__.py --- a/simplify/__init__.py Sun May 27 18:30:34 2007 +0200 +++ b/simplify/__init__.py Sun May 27 19:45:48 2007 +0200 @@ -1,11 +1,9 @@ #!/usr/bin/env python """ -Simplify AST structures for easier type propagation and analysis. The code in -this module processes AST trees originating from the compiler module and -produces a result tree consisting of instruction-oriented program nodes. +The simplify package for processing Python source code. -Copyright (C) 2006, 2007 Paul Boddie +Copyright (C) 2007 Paul Boddie This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,1921 +19,6 @@ License along with this library; see the file LICENCE.txt If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - --------- - -To use this module, the easiest approach is to use the simplify function: - -simplify(filename) - -The more complicated approach involves first instantiating a Simplifier object: - -simplifier = Simplifier() - -Then, applying the simplifier to an AST tree: - -module = compiler.parseFile(filename) -simplifier.process(module) """ -from simplify.simplified import * -import compiler.ast -import os - -class Simplifier(Visitor): - - """ - A simplifying visitor for AST nodes. - - Covered: Add, And, Assert, AssAttr, AssList, AssName, AssTuple, Assign, - AugAssign, Bitand, Break, CallFunc, Class, Compare, Const, - Continue, Dict, Discard, Div, FloorDiv, For, From, Function, - Getattr, Global, If, Import, Invert, Keyword, Lambda, List, - ListComp, ListCompFor, ListCompIf, Mod, Module, Mul, Name, Not, Or, - Pass, Power, Print, Printnl, Raise, Return, Slice, Sliceobj, Stmt, - Sub, Subscript, TryExcept, TryFinally, Tuple, While, UnaryAdd, - UnarySub. - - Missing: Backquote, Bitor, Bitxor, Decorators, Ellipsis, Exec, LeftShift, - RightShift, Yield. - """ - - def __init__(self, builtins=0): - - """ - Initialise the simplifier with the optional 'builtins' parameter - indicating whether the module contains the built-in classes and - functions. - """ - - Visitor.__init__(self) - self.subprograms = [] # Subprograms outside the tree. - self.structures = [] # Structures/classes. - self.constants = {} # Constants. - self.current_subprograms = [] # Current subprograms being processed. - self.current_structures = [] # Current structures being processed. - self.within_class = 0 # Whether a class is being defined. - self.builtins = builtins # Whether the builtins are being processed. - - # Convenience attributes. - - self.subnames = {} - - # For compiler package mechanisms. - - self.visitor = self - - def process(self, node, name): - result = self.dispatch(node, name) - result.simplifier = self - return result - - def dispatch_or_none(self, node, *args): - if node is not None: - return self.dispatch(node, *args) - else: - return LoadName(node, name="None") - - # Top-level transformation. - - def visitModule(self, module, name=None): - - """ - Process the given 'module', producing a Module object which contains the - resulting program nodes. If the optional 'name' is provided, the 'name' - attribute is set on the Module object using a value other than None. - """ - - result = self.module = Module(module, 1, name=name) - result.code = self.dispatch(module.node) - return result - - # Node transformations. - - def visitAdd(self, add): - return self._visitBinary(add, "__add__", "__radd__") - - def visitAnd(self, and_): - - """ - Make a subprogram for the 'and_' node and record its contents inside the - subprogram. Convert... - - And (test) - (test) - ... - - ...to: - - Subprogram -> Conditional (test) -> ReturnFromBlock ... - (else) -> Conditional (test) -> ReturnFromBlock ... - (else) -> ... - """ - - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) - self.current_subprograms.append(subprogram) - - # In the subprogram, make instructions which store each operand, test - # for each operand's truth status, and if appropriate return from the - # subprogram with the value of the operand. - - last = and_.nodes[-1] - results = nodes = [] - - for node in and_.nodes: - expr = self.dispatch(node) - - # Return from the subprogram where the test is not satisfied. - - if node is not last: - nodes += [ - StoreTemp(expr=expr), - Conditional( - test=self._visitNot(LoadTemp()), - body=[ - ReturnFromBlock( - expr=LoadTemp() - ) - ], - else_=[ - ReleaseTemp() - # Subsequent operations go here! - ] - ) - ] - - # Put subsequent operations in the else section of this conditional. - - nodes = nodes[-1].else_ - - # For the last operation, return the result. - - else: - nodes.append(ReturnFromBlock(expr=expr)) - - # Finish the subprogram definition. - - subprogram.code = results - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Make an invocation of the subprogram. - - result = InvokeRef(and_, 1, produces_result=1, ref=subprogram) - return result - - def visitAssert(self, assert_): - if assert_.fail: - fail_args = [self.dispatch(assert_.fail)] - else: - fail_args = [] - - result = Conditional(assert_, 1, - test=self.dispatch(assert_.test), - body=[], - else_=[ - Raise(assert_, - expr=InvokeFunction(assert_, - expr=LoadName(name="AssertionError"), - args=fail_args, - star=None, - dstar=None - ) - ) - ] - ) - - # Make nice annotations for the viewer. - - assert_._raises = result.else_[0].expr - return result - - # Assignments. - - def visitAssAttr(self, assattr, in_sequence=0): - expr = self._visitAssNameOrAttr(assattr, in_sequence) - lvalue = self.dispatch(assattr.expr) - result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr) - return result - - def visitAssign(self, assign): - result = Assign(assign, 1) - store = StoreTemp(expr=self.dispatch(assign.expr)) - release = ReleaseTemp() - result.code = [store] + self.dispatches(assign.nodes, 0) + [release] - return result - - def visitAssList(self, asslist, in_sequence=0): - if not in_sequence: - expr = LoadTemp() - else: - expr = InvokeFunction(asslist, expr=LoadAttr(expr=LoadTemp(), name="next")) - result = Assign(asslist, 1) - store = StoreTemp(expr=InvokeFunction(asslist, expr=LoadAttr(name="__iter__", expr=expr))) - release = ReleaseTemp() - result.code = [store] + self.dispatches(asslist.nodes, 1) + [release] - return result - - visitAssTuple = visitAssList - - def _visitAssNameOrAttr(self, node, in_sequence): - if not in_sequence: - return LoadTemp() - else: - return InvokeFunction(node, expr=LoadAttr(expr=LoadTemp(), name="next")) - - def visitAssName(self, assname, in_sequence=0): - expr = self._visitAssNameOrAttr(assname, in_sequence) - result = StoreName(assname, 1, name=assname.name, expr=expr) - return result - - augassign_methods = { - "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__", - "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__", - "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__" - } - - def visitAugAssign(self, augassign): - - """ - Convert the augmented assignment... - - AugAssign (node) -> Name | Getattr | Slice | Subscript - (op) - (expr) - - ...to: - - Assign (code) -> StoreTemp (expr) -> InvokeFunction (expr) -> LoadAttr (expr) -> - (name) -> - StoreName (name) -> - (expr) -> LoadTemp - ReleaseTemp - """ - - result = Assign(augassign, 1) - expr = self.dispatch(augassign.expr) - - # Simple augmented assignment: name += expr - - if isinstance(augassign.node, compiler.ast.Name): - result.code = [ - StoreTemp( - expr=InvokeFunction( # referenced below - augassign, - args=[expr], - star=None, - dstar=None, - expr=LoadAttr( - expr=self.dispatch(augassign.node), - name=self.augassign_methods[augassign.op] - ) - ) - ), - StoreName( - expr=LoadTemp(), - name=augassign.node.name), - ReleaseTemp() - ] - - # Make nice annotations for the viewer. - - augassign._op_call = result.code[0].expr - - # Complicated augmented assignment: lvalue.attr += expr - - elif isinstance(augassign.node, compiler.ast.Getattr): - - # -> setattr(, getattr(, "attr").__xxx__(expr)) - - result.code = [ - StoreTemp( - index="expr", - expr=self.dispatch(augassign.node.expr) - ), - StoreTemp( - expr=InvokeFunction( # referenced below - augassign, - args=[expr], star=None, dstar=None, - expr=LoadAttr( - expr=LoadAttr(augassign.node, 1, - expr=LoadTemp(index="expr"), - name=augassign.node.attrname - ), - name=self.augassign_methods[augassign.op] - ) - ) - ), - StoreAttr( - expr=LoadTemp(), - lvalue=LoadTemp(index="expr"), - name=augassign.node.attrname - ), - ReleaseTemp(index="expr"), - ReleaseTemp() - ] - - # Make nice annotations for the viewer. - - augassign._op_call = result.code[1].expr - - # Complicated augassign using slices: lvalue[lower:upper] += expr - - elif isinstance(augassign.node, compiler.ast.Slice): - - # , , -> .__setslice__(, , .__getslice__(, ).__xxx__(expr)) - - result.code = [ - StoreTemp( - index="expr", - expr=self.dispatch(augassign.node.expr) - ), - StoreTemp( - index="lower", - expr=self.dispatch_or_none(augassign.node.lower) - ), - StoreTemp( - index="upper", - expr=self.dispatch_or_none(augassign.node.upper) - ), - StoreTemp( - expr=InvokeFunction( # referenced below - augassign, - args=[expr], star=None, dstar=None, - expr=LoadAttr( - expr=self._visitSlice( - augassign.node, - LoadTemp(index="expr"), - LoadTemp(index="lower"), - LoadTemp(index="upper"), - "OP_APPLY"), - name=self.augassign_methods[augassign.op] - ) - ) - ), - self._visitSlice( - augassign.node, - LoadTemp(index="expr"), - LoadTemp(index="lower"), - LoadTemp(index="upper"), - "OP_ASSIGN", - LoadTemp() - ), - ReleaseTemp(index="expr"), - ReleaseTemp(index="lower"), - ReleaseTemp(index="upper"), - ReleaseTemp() - ] - - # Make nice annotations for the viewer. - - augassign._op_call = result.code[3].expr - - # Complicated augassign using subscripts: lvalue[subs] += expr - - elif isinstance(augassign.node, compiler.ast.Subscript): - - # , -> .__setitem__(, .__getitem__().__xxx__(expr)) - - result.code = [ - StoreTemp(index="expr", expr=self.dispatch(augassign.node.expr)), - StoreTemp(index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)), - StoreTemp( - expr=InvokeFunction( # referenced below - augassign, - args=[expr], star=None, dstar=None, - expr=LoadAttr( - expr=self._visitSubscript( - augassign.node, - LoadTemp(index="expr"), - LoadTemp(index="subs"), - "OP_APPLY" - ), - name=self.augassign_methods[augassign.op] - ) - ) - ), - self._visitSubscript( - augassign.node, - LoadTemp(index="expr"), - LoadTemp(index="subs"), - "OP_ASSIGN", - LoadTemp() - ), - ReleaseTemp(index="expr"), - ReleaseTemp(index="subs"), - ReleaseTemp() - ] - - # Make nice annotations for the viewer. - - augassign._op_call = result.code[2].expr - - else: - raise NotImplementedError, augassign.node.__class__ - - return result - - def visitBitand(self, bitand): - - """ - Make a subprogram for the 'bitand' node and record its contents inside the - subprogram. Convert... - - Bitand (node) - (node) - ... - - ...to: - - Subprogram -> Conditional (test) -> ReturnFromBlock ... - (else) -> Conditional (test) -> ReturnFromBlock ... - (else) -> ... - """ - - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) - self.current_subprograms.append(subprogram) - - # In the subprogram, make instructions which store each operand, test - # for each operand's truth status, and if appropriate return from the - # subprogram with the value of the operand. - - last = bitand.nodes[-1] - results = nodes = [] - - # Start by storing the first operand. - - nodes += [ - StoreTemp(expr=self.dispatch(bitand.nodes[0])) - ] - - # For viewing purposes, record invocations on the AST node. - - bitand._ops = [] - - for node in bitand.nodes[1:]: - - # Make a new AST-style node to wrap the operation program nodes. - - new_op = Op("&", node) - bitand._ops.append(new_op) - - # Generate the operation involving the previous result and the - # current operand. - - expr = self._visitBinaryOp(new_op, LoadTemp(), self.dispatch(node), "__and__", "__rand__") - - # Return from the subprogram where the test is not satisfied. - - if node is not last: - nodes += [ - StoreTemp(expr=expr), - Conditional( - test=self._visitNot(LoadTemp()), - body=[ - ReturnFromBlock( - expr=LoadTemp() - ) - ], - else_=[ - # Subsequent operations go here! - ] - ) - ] - - # Put subsequent operations in the else section of this conditional. - - nodes = nodes[-1].else_ - - # For the last operation, return the result. - - else: - nodes.append(ReturnFromBlock(expr=expr)) - - # Finish the subprogram definition. - - subprogram.code = results - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Make an invocation of the subprogram. - - result = InvokeRef(bitand, 1, produces_result=1, ref=subprogram) - return result - - def visitBreak(self, break_): - result = ReturnFromBlock(break_, 1) - return result - - def visitCallFunc(self, callfunc): - result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args)) - if callfunc.star_args is not None: - result.star = self.dispatch(callfunc.star_args) - if callfunc.dstar_args is not None: - result.dstar = self.dispatch(callfunc.dstar_args) - result.expr = self.dispatch(callfunc.node) - return result - - def visitClass(self, class_): - - # Add "object" if the class is not "object" and has an empty bases list. - - if class_.name != "object" and not class_.bases: - bases = [compiler.ast.Name("object")] - else: - bases = class_.bases - - structure = Class(name=class_.name, module=self.module, bases=self.dispatches(bases)) - self.structures.append(structure) - within_class = self.within_class - self.within_class = 1 - - # Make a subprogram which initialises the class structure. - - subprogram = Subprogram(name=None, module=self.module, structure=structure, params=[], star=None, dstar=None) - self.current_subprograms.append(subprogram) - self.current_structures.append(structure) # mostly for name construction - - # The class is initialised using the code found inside. - - subprogram.code = self.dispatch(class_.code) + [ReturnFromBlock()] - - self.within_class = within_class - self.current_structures.pop() - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Make a definition of the class associating it with a name. - - result = Assign( - code=[ - StoreName(class_, 1, # defines the class - name=class_.name, - expr=LoadRef(ref=structure) - ), - InvokeRef( - class_, - share_locals=0, # override the local sharing usually in InvokeRef - ref=subprogram - ) - ] - ) - return result - - comparison_methods = { - "==" : ("__eq__", "__ne__"), - "!=" : ("__ne__", "__eq__"), - "<" : ("__lt__", "__gt__"), - "<=" : ("__le__", "__ge__"), - ">=" : ("__ge__", "__le__"), - ">" : ("__gt__", "__lt__"), - "is" : None, - "is not" : None, - "in" : None, - "not in" : None - } - - def visitCompare(self, compare): - - """ - Make a subprogram for the 'compare' node and record its contents inside - the subprogram. Convert... - - Compare (expr) - (name/node) - ... - - ...to: - - InvokeRef -> Subprogram -> Conditional (test) -> (body) - (else) -> Conditional (test) -> (body) - (else) -> ... - """ - - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) - self.current_subprograms.append(subprogram) - - # In the subprogram, make instructions which invoke a method on the - # first operand of each operand pair and, if appropriate, return with - # the value from that method. - - last = compare.ops[-1] - previous = self.dispatch(compare.expr) - results = nodes = [] - - # For viewing purposes, record invocations on the AST node. - - compare._ops = [] - - for op in compare.ops: - op_name, node = op - - # Make a new AST-style node to wrap the operation program nodes. - - new_op = Op(op_name, node) - compare._ops.append(new_op) - - expr = self.dispatch(node) - - # Identify the operation and produce the appropriate method call. - - method_names = self.comparison_methods[op_name] - if method_names: - first_name, alternative_name = method_names - invocation = self._visitBinaryCompareOp(new_op, previous, expr, first_name, alternative_name) - - elif op_name == "is": - invocation = InvokeFunction( - new_op, 1, - expr=LoadName(name="__is__"), - args=[previous, expr], - star=None, - dstar=None) - - elif op_name == "is not": - invocation = Not( - new_op, 1, - expr=InvokeFunction( - new_op, - expr=LoadName(name="__is__"), - args=[previous, expr], - star=None, - dstar=None) - ) - - elif op_name == "in": - invocation = InvokeFunction( - new_op, 1, - expr=LoadAttr( - expr=previous, - name="__contains__" - ), - args=[expr], - star=None, - dstar=None) - - elif op_name == "not in": - invocation = Not( - new_op, 1, - expr=InvokeFunction( - new_op, - expr=LoadAttr( - expr=previous, - name="__contains__" - ), - args=[expr], - star=None, - dstar=None) - ) - - else: - raise NotImplementedError, op_name - - nodes.append(StoreTemp(expr=invocation)) - - # Return from the subprogram where the test is not satisfied. - - if op is not last: - nodes.append( - Conditional( - test=self._visitNot(LoadTemp()), - body=[ - ReturnFromBlock(expr=LoadTemp()) - ], - else_=[ - ReleaseTemp() - # Subsequent operations go here! - ] - ) - ) - - # Put subsequent operations in the else section of this conditional. - - nodes = nodes[-1].else_ - - # For the last operation, return the result. - - else: - nodes.append( - ReturnFromBlock(expr=LoadTemp(release=1)) - ) - - previous = expr - - # Finish the subprogram definition. - - subprogram.code = results - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Make an invocation of the subprogram. - - result = InvokeRef(compare, 1, produces_result=1, ref=subprogram) - return result - - def visitConst(self, const): - key = "%s-%s" % (const.value.__class__.__name__, const.value) - if not self.constants.has_key(key): - self.constants[key] = Constant(name=repr(const.value), value=const.value) - result = InvokeFunction(const, 1, expr=LoadName(name=self.constants[key].typename)) - return result - - def visitContinue(self, continue_): - result = InvokeRef(continue_, 1, ref=self.current_subprograms[-1]) - return result - - def visitDict(self, dict): - result = InvokeFunction(dict, 1, expr=LoadName(name="dict")) - args = [] - for key, value in dict.items: - tuple = InvokeFunction(dict, expr=LoadName(name="tuple")) - tuple.set_args([self.dispatch(key), self.dispatch(value)]) - args.append(tuple) - result.set_args(args) - return result - - def visitDiscard(self, discard): - return self.dispatch(discard.expr) - - def visitDiv(self, div): - return self._visitBinary(div, "__div__", "__rdiv__") - - def visitFloorDiv(self, floordiv): - return self._visitBinary(floordiv, "__floordiv__", "__rfloordiv__") - - def visitFor(self, for_): - - """ - Make a subprogram for the 'for_' node and record its contents inside the - subprogram. Convert... - - For (assign) - (body) - (else) - - ...to: - - Assign (assign #1) - Invoke -> Subprogram -> Try (body) -> (assign #2) - (body) - Invoke subprogram - (handler) -> ... - (else) -> ... - """ - - return self._visitFor(for_, self.dispatches(for_.body), for_.else_) - - def _visitFor(self, node, body_stmt, else_=None): - - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[]) - self.current_subprograms.append(subprogram) - - # Always return from conditional sections/subprograms. - - if else_ is not None: - else_stmt = self.dispatch(else_) + [ReturnFromBlock()] - else: - else_stmt = [ReturnFromBlock()] - - # Wrap the assignment in a try...except statement. - # Inside the body, add a recursive invocation to the subprogram. - - subprogram.code = [ - Try( - body=[ - Assign( - code=[ - StoreTemp( - expr=InvokeFunction(node, - expr=LoadAttr( - expr=LoadTemp(), - name="next" - ) - ) - ), - self.dispatch(node.assign), - ReleaseTemp() - ]) - ] + body_stmt + [ - InvokeRef( - node, - ref=subprogram - ) - ], - handler=[ - Conditional( - test=InvokeFunction( - node, - expr=LoadName(name="isinstance"), - args=[LoadExc(), LoadName(name="StopIteration")], - star=None, - dstar=None), - body=else_stmt, - else_=[ - Raise( - expr=LoadExc() - ) - ] - ) - ], - else_=[], - finally_=[] - ), - ReturnFromBlock() - ] - - # Finish the subprogram definition. - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Obtain an iterator for the sequence involved. - # Then, make an invocation of the subprogram. - - result = Assign(node, 1, - code=[ - StoreTemp( - expr=InvokeFunction( - node, - expr=LoadAttr( - name="__iter__", - expr=self.dispatch(node.list) - ) - ) - ), - InvokeRef(node, ref=subprogram), - ReleaseTemp() - ] - ) - - # Make nice annotations for the viewer. - - node._iter_call = result.code[0].expr - node._next_call = subprogram.code[0].body[0].code[0].expr - - return result - - def visitFrom(self, from_): - result = Assign(from_, 1) - code = [] - _names = [] - code.append( - StoreTemp( - expr=Import(name=from_.modname, alias=1) - ) - ) - from_._modname = code[-1].expr - for name, alias in from_.names: - code.append( - StoreName( - expr=LoadAttr( - expr=LoadTemp(), - name=name), - name=(alias or name) - ) - ) - _names.append(code[-1].expr) - code.append(ReleaseTemp()) - result.code = code - from_._names = _names - return result - - def _visitFunction(self, function, subprogram): - - """ - A common function generator which transforms the given 'function' node - and initialises the given 'subprogram' appropriately. - """ - - # Discover star and dstar parameters. - - if function.flags & 4 != 0: - has_star = 1 - else: - has_star = 0 - if function.flags & 8 != 0: - has_dstar = 1 - else: - has_dstar = 0 - - # Discover the number of defaults and positional parameters. - - ndefaults = len(function.defaults) - npositional = len(function.argnames) - has_star - has_dstar - - # Produce star and dstar parameters with appropriate defaults. - - if has_star: - star = ( - function.argnames[npositional], - self.dispatch(compiler.ast.List([])) - ) - else: - star = None - if has_dstar: - dstar = ( - function.argnames[npositional + has_star], - self.dispatch(compiler.ast.Dict([])) - ) - else: - dstar = None - - params = [] - for i in range(0, npositional - ndefaults): - params.append((function.argnames[i], None)) - - # Process defaults. - - for i in range(0, ndefaults): - default = function.defaults[i] - if default is not None: - params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default))) - else: - params.append((function.argnames[npositional - ndefaults + i], None)) - - subprogram.params = params - subprogram.star = star - subprogram.dstar = dstar - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - def visitFunction(self, function): - - """ - Make a subprogram for the 'function' and record it outside the main - tree. Produce something like the following: - - StoreName (name) - (expr) -> LoadRef (ref) -> Subprogram (params) - (star) - (dstar) - """ - - subprogram = Subprogram(function, name=function.name, module=self.module, structures=self.current_structures[:], - internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class, original_def=function) - - # Make nice annotations for the viewer. - - function._subprogram = subprogram - - self.current_subprograms.append(subprogram) - within_class = self.within_class - self.within_class = 0 - - subprogram.code = self.dispatch(function.code) + [ReturnFromFunction()] - - self.within_class = within_class - self.current_subprograms.pop() - self._visitFunction(function, subprogram) - - # Make a definition of the function associating it with a name. - - result = StoreName(function, 1, name=function.name, expr=LoadRef(ref=subprogram)) - return result - - def visitGetattr(self, getattr): - result = LoadAttr(getattr, 1, - name=getattr.attrname, - expr=self.dispatch(getattr.expr) - ) - return result - - def visitGlobal(self, global_): - result = Global(global_, 1, - names=global_.names - ) - return result - - def visitIf(self, if_): - - """ - Make conditionals for each test from an 'if_' AST node, adding the body - and putting each subsequent test as part of the conditional's else - section. - - Convert... - - If (test/body) - (test/body) - ... - (else/body) - - ...to: - - Conditional (test) -> (body) - (else) -> Conditional (test) -> (body) - (else) -> ... - """ - - - results = nodes = [] - - # Produce something like... - # expr.__bool__() ? body - - first = 1 - for compare, stmt in if_.tests: - - # Set the first as the defining node. - - test = Conditional(if_, first, - test=InvokeFunction( - if_, - expr=LoadAttr( - expr=self.dispatch(compare), - name="__bool__" - ), - ) - ) - test.body = self.dispatch(stmt) - nodes.append(test) - nodes = test.else_ = [] - first = 0 - - # Add the compound statement from any else clause to the end. - - if if_.else_ is not None: - nodes += self.dispatch(if_.else_) - - result = results[0] - return result - - def visitImport(self, import_): - result = Assign(import_, 1) - code = [] - _names = [] - for path, alias in import_.names: - importer = Import(name=path, alias=alias) - top = alias or path.split(".")[0] - code.append(StoreName(expr=importer, name=top)) - _names.append(code[-1].expr) - result.code = code - import_._names = _names - return result - - def visitInvert(self, invert): - return self._visitUnary(invert, "__invert__") - - def visitKeyword(self, keyword): - result = Keyword(keyword, 1, - name=keyword.name, - expr=self.dispatch(keyword.expr) - ) - return result - - def visitLambda(self, lambda_): - - # Make a subprogram for the function and record it outside the main - # tree. - - subprogram = Subprogram(lambda_, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=lambda_) - - # Make nice annotations for the viewer. - - lambda_._subprogram = subprogram - - # Process the lambda contents. - - self.current_subprograms.append(subprogram) - subprogram.code = [ReturnFromFunction(expr=self.dispatch(lambda_.code))] - self.current_subprograms.pop() - self._visitFunction(lambda_, subprogram) - - # Get the subprogram reference to the lambda. - - return LoadRef(lambda_, 1, ref=subprogram) - - def visitList(self, list): - - # Make a subprogram for the list construction and record it outside the - # main tree. - - subprogram = Subprogram(list, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=list) - self.current_subprograms.append(subprogram) - - # Make nice annotations for the viewer. - - list._subprogram = subprogram - - subprogram.code=[ - StoreTemp( - expr=InvokeFunction( - list, - expr=LoadName( - name="list" - ), - args=[], - star=None, - dstar=None - ) - ) - ] - - for node in list.nodes: - subprogram.code.append( - InvokeFunction( - list, - expr=LoadAttr( - expr=LoadTemp(), - name="append" - ), - args=[self.dispatch(node)], - star=None, - dstar=None - ) - ) - - subprogram.code.append( - ReturnFromBlock( - expr=LoadTemp(release=1) - ) - ) - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Make an invocation of the subprogram. - - result = InvokeRef(list, 1, - produces_result=1, - ref=subprogram - ) - return result - - def visitListComp(self, listcomp): - - # Make a subprogram for the list comprehension and record it outside the - # main tree. - - subprogram = Subprogram(listcomp, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=listcomp) - self.current_subprograms.append(subprogram) - - # Make nice annotations for the viewer. - - listcomp._subprogram = subprogram - - # Add a temporary variable. - # Produce for loops within the subprogram. - # Return the result. - - subprogram.code = [ - StoreTemp( - index="listcomp", - expr=InvokeFunction( - expr=LoadName(name="list"), - args=[], - star=None, - dstar=None - ) - ) - ] + self._visitListCompFor(listcomp, listcomp.quals) + [ - ReturnFromBlock( - expr=LoadTemp( - index="listcomp", - release=1 - ) - ) - ] - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Make an invocation of the subprogram. - - result = InvokeRef(listcomp, 1, - produces_result=1, - ref=subprogram - ) - return result - - def _visitListCompFor(self, node, quals): - qual = quals[0] - if len(quals) > 1: - body = self._visitListCompFor(node, quals[1:]) - if qual.ifs: - body = self._visitListCompIf(node, qual.ifs, body) - elif qual.ifs: - body = self._visitListCompIf(node, qual.ifs) - else: - body = self._visitListCompBody(node) - return [self._visitFor(qual, body)] - - def _visitListCompIf(self, node, ifs, expr=None): - if_ = ifs[0] - if len(ifs) > 1: - body = self._visitListCompIf(node, ifs[1:], expr) - elif expr is None: - body = self._visitListCompBody(node) - else: - body = expr - return [ - Conditional(if_, 1, - test=InvokeFunction( - if_, - expr=LoadAttr( - expr=self.dispatch(if_.test), - name="__bool__" - ), - ), - body=body, - else_=[] - ) - ] - - def _visitListCompBody(self, node): - return [ - InvokeFunction( - expr=LoadAttr( - expr=LoadTemp(index="listcomp"), - name="append" - ), - args=[self.dispatch(node.expr)], - star=None, - dstar=None - ) - ] - - def visitMod(self, mod): - return self._visitBinary(mod, "__mod__", "__rmod__") - - def visitMul(self, mul): - return self._visitBinary(mul, "__mul__", "__rmul__") - - def visitName(self, name): - result = LoadName(name, 1, name=name.name) - return result - - def _visitNot(self, expr, not_=None): - invocation = InvokeFunction( - not_, # NOTE: May need a real original node. - expr=LoadAttr( - expr=expr, - name="__bool__" - ), - ) - if not_ is not None: - result = Not(not_, 1, expr=invocation) - else: - result = Not(expr=invocation) - return result - - def visitNot(self, not_): - return self._visitNot(self.dispatch(not_.expr), not_) - - def visitOr(self, or_): - - """ - Make a subprogram for the 'or_' node and record its contents inside the - subprogram. Convert... - - Or (test) - (test) - ... - - ...to: - - Subprogram -> Conditional (test) -> ReturnFromBlock ... - (else) -> Conditional (test) -> ReturnFromBlock ... - (else) -> ... - """ - - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) - self.current_subprograms.append(subprogram) - - # In the subprogram, make instructions which store each operand, test - # for each operand's truth status, and if appropriate return from the - # subprogram with the value of the operand. - - last = or_.nodes[-1] - results = nodes = [] - - for node in or_.nodes: - expr = self.dispatch(node) - - # Return from the subprogram where the test is satisfied. - - if node is not last: - nodes.append(StoreTemp(expr=expr)) - invocation = InvokeFunction(or_, expr=LoadAttr(expr=LoadTemp(), name="__bool__")) - test = Conditional(test=invocation, body=[ReturnFromBlock(expr=LoadTemp())]) - nodes.append(test) - - # Put subsequent operations in the else section of this conditional. - - nodes = test.else_ = [ReleaseTemp()] - - # For the last operation, return the result. - - else: - nodes.append( - ReturnFromBlock(expr=expr) - ) - - # Finish the subprogram definition. - - subprogram.code = results - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Make an invocation of the subprogram. - - result = InvokeRef(or_, 1, - produces_result=1, - ref=subprogram - ) - return result - - def visitPass(self, pass_): - return Pass(pass_, 1) - - def visitPower(self, power): - return self._visitBinary(power, "__pow__", "__rpow__") - - def visitPrint(self, print_): - - """ - Convert... - - Print (dest) -> - (nodes) - - ...to: - - StoreTemp (index) -> "print" - (expr) -> LoadAttr (expr) -> (dest) - (name) -> "write" - InvokeFunction (expr) -> LoadTemp (index) -> "print" - (args) -> [(node)] - ReleaseTemp (index) -> "print" - """ - - if print_.dest is not None: - dest = self.dispatch(print_.dest) - else: - dest = self.dispatch(compiler.ast.Name("stdout")) - - result = Assign(print_, 1, - code=[ - StoreTemp( - index="print", - expr=LoadAttr( - expr=dest, - name="write" - ) - ) - ] - ) - - for node in print_.nodes: - result.code.append( - InvokeFunction( - print_, - expr=LoadTemp(index="print"), - args=[self.dispatch(node)], - star=None, - dstar=None - ) - ) - - result.code.append( - ReleaseTemp(index="print") - ) - - return result - - def visitPrintnl(self, printnl): - result = self.visitPrint(printnl) - result.code.insert( - len(result.code) - 1, - InvokeFunction( - printnl, - expr=LoadTemp(index="print"), - args=[self.dispatch(compiler.ast.Const("\n"))], - star=None, - dstar=None - ) - ) - return result - - def visitRaise(self, raise_): - result = Raise(raise_, 1) - if raise_.expr2 is None: - result.expr = self.dispatch(raise_.expr1) - else: - result.expr = InvokeFunction( - raise_, - expr=self.dispatch(raise_.expr1), - args=[self.dispatch(raise_.expr2)], - star=None, - dstar=None - ) - if raise_.expr3 is not None: - result.traceback = self.dispatch(raise_.expr3) - else: - result.traceback = None - return result - - def visitReturn(self, return_): - result = ReturnFromFunction(return_, 1, - expr=self.dispatch(return_.value) - ) - return result - - def _visitSlice(self, slice, expr, lower, upper, flags, value=None): - if flags == "OP_ASSIGN": - result = InvokeFunction(slice, 1, - expr=LoadAttr( - expr=expr, - name="__setslice__" - ), - star=None, - dstar=None, - args=[lower, upper, value] - ) - elif flags == "OP_APPLY": - args = [] - result = InvokeFunction(slice, 1, - expr=LoadAttr( - expr=expr, - name="__getslice__" - ), - star=None, - dstar=None, - args=[lower, upper] - ) - elif flags == "OP_DELETE": - args = [] - result = InvokeFunction(slice, 1, - expr=LoadAttr( - expr=expr, - name="__delslice__" - ), - star=None, - dstar=None, - args=[lower, upper] - ) - else: - raise NotImplementedError, flags - - return result - - def visitSlice(self, slice, in_sequence=0): - return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower), - self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, in_sequence)) - - def visitSliceobj(self, sliceobj): - return InvokeFunction(sliceobj, 1, - expr=LoadName(name="slice"), - args=self.dispatches(sliceobj.nodes), - star=None, - dstar=None - ) - - def visitStmt(self, stmt): - return self.dispatches(stmt.nodes) - - def visitSub(self, sub): - return self._visitBinary(sub, "__sub__", "__rsub__") - - def _visitSubscript(self, subscript, expr, subs, flags, value=None): - if flags == "OP_ASSIGN": - result = InvokeFunction(subscript, 1, - expr=LoadAttr( - expr=expr, - name="__setitem__" - ), - star=None, - dstar=None, - args=[subs, value] - ) - elif flags == "OP_APPLY": - args = [] - result = InvokeFunction(subscript, 1, - expr=LoadAttr( - expr=expr, - name="__getitem__" - ), - star=None, - dstar=None, - args=[subs] - ) - elif flags == "OP_DELETE": - args = [] - result = InvokeFunction(subscript, 1, - expr=LoadAttr( - expr=expr, - name="__delitem__" - ), - star=None, - dstar=None, - args=[subs] - ) - else: - raise NotImplementedError, flags - - return result - - def _visitSubscriptSubs(self, node, subs): - if len(subs) == 1: - return self.dispatch(subs[0]) - else: - return InvokeFunction(node, 1, - expr=LoadName(name="tuple"), - args=self.dispatches(subs), - star=None, - dstar=None - ) - - def visitSubscript(self, subscript, in_sequence=0): - return self._visitSubscript( - subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags, - self._visitAssNameOrAttr(subscript, in_sequence) - ) - - def visitTryExcept(self, tryexcept): - - """ - Make conditionals for each handler associated with a 'tryexcept' node. - - Convert... - - TryExcept (body) - (else) - (spec/assign/stmt) - ... - - ...to: - - Try (body) - (else) - (handler) -> Conditional (test) -> (stmt) - (body) -> ResetExc ... - (else) -> Conditional (test) -> (stmt) - (body) -> ResetExc ... - (else) -> ... - """ - - result = Try(tryexcept, 1, body=[], else_=[], finally_=[]) - - if tryexcept.body is not None: - result.body = self.dispatch(tryexcept.body) - if tryexcept.else_ is not None: - result.else_ = self.dispatch(tryexcept.else_) - - results = nodes = [] - catch_all = 0 - - for spec, assign, stmt in tryexcept.handlers: - - # If no specification exists, produce an unconditional block. - - if spec is None: - nodes += self.dispatch(stmt) - catch_all = 1 - - # Produce an exception value check. - - else: - test = Conditional( - isolate_test=1, - test=CheckType(expr=LoadExc(), choices=self._visitTryExcept(spec)) - ) - test.body = [] - - if assign is not None: - test.body.append( - Assign( - code=[ - StoreTemp(expr=LoadExc()), - self.dispatch(assign), - ReleaseTemp() - ] - ) - ) - - test.body += [ResetExc()] + self.dispatch(stmt) - nodes.append(test) - nodes = test.else_ = [] - - # Add a raise operation to deal with unhandled exceptions. - - if not catch_all: - nodes.append( - Raise( - expr=LoadExc()) - ) - - result.handler = results - return result - - def _visitTryExcept(self, spec): - - "Return a list of nodes for the given exception type 'spec'." - - if isinstance(spec, compiler.ast.Tuple): - nodes = [] - for node in spec.nodes: - nodes += self._visitTryExcept(node) - else: - nodes = [self.dispatch(spec)] - return nodes - - def visitTryFinally(self, tryfinally): - result = Try(tryfinally, 1, body=[], else_=[], finally_=[]) - if tryfinally.body is not None: - result.body = self.dispatch(tryfinally.body) - if tryfinally.final is not None: - result.finally_ = self.dispatch(tryfinally.final) - return result - - def visitTuple(self, tuple): - - "Make a MakeTuple node containing the original 'tuple' contents." - - result = MakeTuple(tuple, 1, - nodes=self.dispatches(tuple.nodes) - ) - return result - - def visitUnaryAdd(self, unaryadd): - return self._visitUnary(unaryadd, "__pos__") - - def visitUnarySub(self, unarysub): - return self._visitUnary(unarysub, "__neg__") - - def visitWhile(self, while_): - - """ - Make a subprogram for the 'while' node and record its contents inside the - subprogram. Convert... - - While (test) -> (body) - (else) - - ...to: - - Subprogram -> Conditional (test) -> (body) -> Invoke subprogram - (else) -> Conditional (test) -> ReturnFromBlock ... - (else) -> ... - """ - - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None) - self.current_subprograms.append(subprogram) - - # Include a conditional statement in the subprogram. - # Inside the conditional, add a recursive invocation to the subprogram - # if the test condition was satisfied. - # Return within the main section of the loop. - - test = Conditional( - test=InvokeFunction( - while_, - expr=LoadAttr( - expr=self.dispatch(while_.test), - name="__bool__"), - ), - body=self.dispatch(while_.body) + [ - InvokeRef( - while_, - ref=subprogram - ), - ReturnFromBlock() - ], - else_=[] - ) - - # Provide the else section, if present, along with an explicit return. - - if while_.else_ is not None: - test.else_ = self.dispatch(while_.else_) + [ReturnFromBlock()] - - # Finish the subprogram definition. - - subprogram.code = [test] - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - # Make an invocation of the subprogram. - - result = InvokeRef(while_, 1, ref=subprogram) - - # Make nice annotations for the viewer. - - while_._test_call = subprogram.code[0].test - - return result - - # NOTE: Not actually supported. - # NOTE: Virtually the same as visitReturn... - - def visitYield(self, yield_): - result = Yield(yield_, 1, - expr=self.dispatch(yield_.value) - ) - return result - - # Convenience methods. - - def _visitBinary(self, binary, left_name, right_name): - return self._visitBinaryOp(binary, self.dispatch(binary.left), self.dispatch(binary.right), left_name, right_name) - - def _visitBinaryCompareOp(self, binary, left, right, left_name, right_name): - - """ - Emulate the current mechanisms by producing nodes as follows: - - InvokeRef -> Subprogram -> StoreTemp (expr) -> x.__lt__(y) - Conditional (test) -> __is__(LoadTemp, NotImplemented) - (body) -> ReleaseTemp - StoreTemp (expr) -> y.__gt__(x) - Conditional (test) -> __is__(LoadTemp, NotImplemented) - (body) -> ReturnFromBlock (expr) -> False - (else) -> ReturnFromBlock (expr) -> LoadTemp - (else) -> ReturnFromBlock (expr) -> LoadTemp - """ - - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) - self.current_subprograms.append(subprogram) - - subprogram.code = [ - StoreTemp( - expr=InvokeFunction( - binary, - expr=LoadAttr(expr=left, name=left_name), - args=[right], - star=None, - dstar=None) - ), - Conditional( - isolate_test=1, - test=CheckType( - expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] - ), - body=[ - ReleaseTemp(), - StoreTemp( - expr=InvokeFunction( - binary, - expr=LoadAttr(expr=right, name=right_name), - args=[left], - star=None, - dstar=None) - ), - Conditional( - isolate_test=1, - test=CheckType( - expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] - ), - body=[ - ReturnFromBlock( - expr=LoadName(name="False") - ) - ], - else_=[ - CheckType( - inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] - ), - ReturnFromBlock( - expr=LoadTemp() - ) - ] - ) - ], - else_=[ - CheckType( - inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] - ), - ReturnFromBlock( - expr=LoadTemp() - ) - ] - ) - ] - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - result = InvokeRef( - binary, - produces_result=1, - ref=subprogram - ) - - # Make nice annotations for the viewer. - - binary._left_call = subprogram.code[0].expr - binary._right_call = subprogram.code[1].body[1].expr - - return result - - def _visitBinaryOp(self, binary, left, right, left_name, right_name): - - """ - Emulate the current mechanisms by producing nodes as follows: - - InvokeRef -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y) - (else) - (handler) -> Conditional (test) -> CheckType (expr) -> LoadExc - (choices) -> LoadName TypeError - (body) -> ReturnFromBlock (expr) -> y.__radd__(x) - (else) - """ - - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) - self.current_subprograms.append(subprogram) - - subprogram.code = [ - Try(binary, 1, - body=[ - ReturnFromBlock( - expr=InvokeFunction( - binary, - expr=LoadAttr(expr=left, name=left_name), - args=[right], - star=None, - dstar=None) - ) - ], - else_=[], - finally_=[], - handler=[ - Conditional( - test=CheckType(expr=LoadExc(), choices=[LoadName(name="TypeError")]), - body=[ - ReturnFromBlock( - expr=InvokeFunction( - binary, - expr=LoadAttr(expr=right, name=right_name), - args=[left], - star=None, - dstar=None) - ) - ], - else_=[] - ) - ] - ) - ] - - self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - - result = InvokeRef( - binary, - produces_result=1, - ref=subprogram - ) - - # Make nice annotations for the viewer. - - binary._left_call = subprogram.code[0].body[0].expr - binary._right_call = subprogram.code[0].handler[0].body[0].expr - - return result - - def _visitBuiltin(self, builtin, name): - result = InvokeFunction(builtin, 1, expr=LoadName(name=name), args=self.dispatches(builtin.nodes)) - return result - - def _visitUnary(self, unary, name): - result = InvokeFunction(unary, 1, - expr=LoadAttr( - expr=self.dispatch(unary.expr), - name=name - ) - ) - - # Make nice annotations for the viewer. - - unary._unary_call = result - - return result - -# Convenience functions. - -def simplify(filename, builtins=0, module_name=None): - - """ - Simplify the module stored in the file with the given 'filename'. - - If the optional 'builtins' parameter is set to a true value (the default - being a false value), then the module is considered as the builtins module. - """ - - simplifier = Simplifier(builtins) - module = compiler.parseFile(filename) - compiler.misc.set_filename(filename, module) - if builtins: - name = module_name or "__builtins__" - else: - path, ext = os.path.splitext(filename) - path, name = os.path.split(path) - name = module_name or name - simplified = simplifier.process(module, name) - return simplified - # vim: tabstop=4 expandtab shiftwidth=4 diff -r 1233b49abe97 -r 52cdfcb4af3b simplify/annotate.py --- a/simplify/annotate.py Sun May 27 18:30:34 2007 +0200 +++ b/simplify/annotate.py Sun May 27 19:45:48 2007 +0200 @@ -55,7 +55,7 @@ """ from simplify.simplified import * -import simplify, simplify.fixnames # for the load function +import simplify.ast, simplify.fixnames # for the load function import compiler import os @@ -1766,7 +1766,7 @@ optional 'importer' to provide a means of finding and loading modules. """ - module = simplify.simplify(name, builtins is None, module_name) + module = simplify.ast.simplify(name, builtins is None, module_name) simplify.fixnames.fix(module, builtins) if not no_annotate: annotate(module, builtins, importer) diff -r 1233b49abe97 -r 52cdfcb4af3b simplify/ast.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simplify/ast.py Sun May 27 19:45:48 2007 +0200 @@ -0,0 +1,1941 @@ +#!/usr/bin/env python + +""" +Simplify AST structures for easier type propagation and analysis. The code in +this module processes AST trees originating from the compiler module and +produces a result tree consisting of instruction-oriented program nodes. + +Copyright (C) 2006, 2007 Paul Boddie + +This software 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 2 of +the License, or (at your option) any later version. + +This software 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 library; see the file LICENCE.txt +If not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +-------- + +To use this module, the easiest approach is to use the simplify function: + +simplify(filename) + +The more complicated approach involves first instantiating a Simplifier object: + +simplifier = Simplifier() + +Then, applying the simplifier to an AST tree: + +module = compiler.parseFile(filename) +simplifier.process(module) +""" + +from simplify.simplified import * +import compiler.ast +import os + +class Simplifier(Visitor): + + """ + A simplifying visitor for AST nodes. + + Covered: Add, And, Assert, AssAttr, AssList, AssName, AssTuple, Assign, + AugAssign, Bitand, Break, CallFunc, Class, Compare, Const, + Continue, Dict, Discard, Div, FloorDiv, For, From, Function, + Getattr, Global, If, Import, Invert, Keyword, Lambda, List, + ListComp, ListCompFor, ListCompIf, Mod, Module, Mul, Name, Not, Or, + Pass, Power, Print, Printnl, Raise, Return, Slice, Sliceobj, Stmt, + Sub, Subscript, TryExcept, TryFinally, Tuple, While, UnaryAdd, + UnarySub. + + Missing: Backquote, Bitor, Bitxor, Decorators, Ellipsis, Exec, LeftShift, + RightShift, Yield. + """ + + def __init__(self, builtins=0): + + """ + Initialise the simplifier with the optional 'builtins' parameter + indicating whether the module contains the built-in classes and + functions. + """ + + Visitor.__init__(self) + self.subprograms = [] # Subprograms outside the tree. + self.structures = [] # Structures/classes. + self.constants = {} # Constants. + self.current_subprograms = [] # Current subprograms being processed. + self.current_structures = [] # Current structures being processed. + self.within_class = 0 # Whether a class is being defined. + self.builtins = builtins # Whether the builtins are being processed. + + # Convenience attributes. + + self.subnames = {} + + # For compiler package mechanisms. + + self.visitor = self + + def process(self, node, name): + result = self.dispatch(node, name) + result.simplifier = self + return result + + def dispatch_or_none(self, node, *args): + if node is not None: + return self.dispatch(node, *args) + else: + return LoadName(node, name="None") + + # Top-level transformation. + + def visitModule(self, module, name=None): + + """ + Process the given 'module', producing a Module object which contains the + resulting program nodes. If the optional 'name' is provided, the 'name' + attribute is set on the Module object using a value other than None. + """ + + result = self.module = Module(module, 1, name=name) + result.code = self.dispatch(module.node) + return result + + # Node transformations. + + def visitAdd(self, add): + return self._visitBinary(add, "__add__", "__radd__") + + def visitAnd(self, and_): + + """ + Make a subprogram for the 'and_' node and record its contents inside the + subprogram. Convert... + + And (test) + (test) + ... + + ...to: + + Subprogram -> Conditional (test) -> ReturnFromBlock ... + (else) -> Conditional (test) -> ReturnFromBlock ... + (else) -> ... + """ + + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) + self.current_subprograms.append(subprogram) + + # In the subprogram, make instructions which store each operand, test + # for each operand's truth status, and if appropriate return from the + # subprogram with the value of the operand. + + last = and_.nodes[-1] + results = nodes = [] + + for node in and_.nodes: + expr = self.dispatch(node) + + # Return from the subprogram where the test is not satisfied. + + if node is not last: + nodes += [ + StoreTemp(expr=expr), + Conditional( + test=self._visitNot(LoadTemp()), + body=[ + ReturnFromBlock( + expr=LoadTemp() + ) + ], + else_=[ + ReleaseTemp() + # Subsequent operations go here! + ] + ) + ] + + # Put subsequent operations in the else section of this conditional. + + nodes = nodes[-1].else_ + + # For the last operation, return the result. + + else: + nodes.append(ReturnFromBlock(expr=expr)) + + # Finish the subprogram definition. + + subprogram.code = results + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make an invocation of the subprogram. + + result = InvokeRef(and_, 1, produces_result=1, ref=subprogram) + return result + + def visitAssert(self, assert_): + if assert_.fail: + fail_args = [self.dispatch(assert_.fail)] + else: + fail_args = [] + + result = Conditional(assert_, 1, + test=self.dispatch(assert_.test), + body=[], + else_=[ + Raise(assert_, + expr=InvokeFunction(assert_, + expr=LoadName(name="AssertionError"), + args=fail_args, + star=None, + dstar=None + ) + ) + ] + ) + + # Make nice annotations for the viewer. + + assert_._raises = result.else_[0].expr + return result + + # Assignments. + + def visitAssAttr(self, assattr, in_sequence=0): + expr = self._visitAssNameOrAttr(assattr, in_sequence) + lvalue = self.dispatch(assattr.expr) + result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr) + return result + + def visitAssign(self, assign): + result = Assign(assign, 1) + store = StoreTemp(expr=self.dispatch(assign.expr)) + release = ReleaseTemp() + result.code = [store] + self.dispatches(assign.nodes, 0) + [release] + return result + + def visitAssList(self, asslist, in_sequence=0): + if not in_sequence: + expr = LoadTemp() + else: + expr = InvokeFunction(asslist, expr=LoadAttr(expr=LoadTemp(), name="next")) + result = Assign(asslist, 1) + store = StoreTemp(expr=InvokeFunction(asslist, expr=LoadAttr(name="__iter__", expr=expr))) + release = ReleaseTemp() + result.code = [store] + self.dispatches(asslist.nodes, 1) + [release] + return result + + visitAssTuple = visitAssList + + def _visitAssNameOrAttr(self, node, in_sequence): + if not in_sequence: + return LoadTemp() + else: + return InvokeFunction(node, expr=LoadAttr(expr=LoadTemp(), name="next")) + + def visitAssName(self, assname, in_sequence=0): + expr = self._visitAssNameOrAttr(assname, in_sequence) + result = StoreName(assname, 1, name=assname.name, expr=expr) + return result + + augassign_methods = { + "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__", + "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__", + "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__" + } + + def visitAugAssign(self, augassign): + + """ + Convert the augmented assignment... + + AugAssign (node) -> Name | Getattr | Slice | Subscript + (op) + (expr) + + ...to: + + Assign (code) -> StoreTemp (expr) -> InvokeFunction (expr) -> LoadAttr (expr) -> + (name) -> + StoreName (name) -> + (expr) -> LoadTemp + ReleaseTemp + """ + + result = Assign(augassign, 1) + expr = self.dispatch(augassign.expr) + + # Simple augmented assignment: name += expr + + if isinstance(augassign.node, compiler.ast.Name): + result.code = [ + StoreTemp( + expr=InvokeFunction( # referenced below + augassign, + args=[expr], + star=None, + dstar=None, + expr=LoadAttr( + expr=self.dispatch(augassign.node), + name=self.augassign_methods[augassign.op] + ) + ) + ), + StoreName( + expr=LoadTemp(), + name=augassign.node.name), + ReleaseTemp() + ] + + # Make nice annotations for the viewer. + + augassign._op_call = result.code[0].expr + + # Complicated augmented assignment: lvalue.attr += expr + + elif isinstance(augassign.node, compiler.ast.Getattr): + + # -> setattr(, getattr(, "attr").__xxx__(expr)) + + result.code = [ + StoreTemp( + index="expr", + expr=self.dispatch(augassign.node.expr) + ), + StoreTemp( + expr=InvokeFunction( # referenced below + augassign, + args=[expr], star=None, dstar=None, + expr=LoadAttr( + expr=LoadAttr(augassign.node, 1, + expr=LoadTemp(index="expr"), + name=augassign.node.attrname + ), + name=self.augassign_methods[augassign.op] + ) + ) + ), + StoreAttr( + expr=LoadTemp(), + lvalue=LoadTemp(index="expr"), + name=augassign.node.attrname + ), + ReleaseTemp(index="expr"), + ReleaseTemp() + ] + + # Make nice annotations for the viewer. + + augassign._op_call = result.code[1].expr + + # Complicated augassign using slices: lvalue[lower:upper] += expr + + elif isinstance(augassign.node, compiler.ast.Slice): + + # , , -> .__setslice__(, , .__getslice__(, ).__xxx__(expr)) + + result.code = [ + StoreTemp( + index="expr", + expr=self.dispatch(augassign.node.expr) + ), + StoreTemp( + index="lower", + expr=self.dispatch_or_none(augassign.node.lower) + ), + StoreTemp( + index="upper", + expr=self.dispatch_or_none(augassign.node.upper) + ), + StoreTemp( + expr=InvokeFunction( # referenced below + augassign, + args=[expr], star=None, dstar=None, + expr=LoadAttr( + expr=self._visitSlice( + augassign.node, + LoadTemp(index="expr"), + LoadTemp(index="lower"), + LoadTemp(index="upper"), + "OP_APPLY"), + name=self.augassign_methods[augassign.op] + ) + ) + ), + self._visitSlice( + augassign.node, + LoadTemp(index="expr"), + LoadTemp(index="lower"), + LoadTemp(index="upper"), + "OP_ASSIGN", + LoadTemp() + ), + ReleaseTemp(index="expr"), + ReleaseTemp(index="lower"), + ReleaseTemp(index="upper"), + ReleaseTemp() + ] + + # Make nice annotations for the viewer. + + augassign._op_call = result.code[3].expr + + # Complicated augassign using subscripts: lvalue[subs] += expr + + elif isinstance(augassign.node, compiler.ast.Subscript): + + # , -> .__setitem__(, .__getitem__().__xxx__(expr)) + + result.code = [ + StoreTemp(index="expr", expr=self.dispatch(augassign.node.expr)), + StoreTemp(index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)), + StoreTemp( + expr=InvokeFunction( # referenced below + augassign, + args=[expr], star=None, dstar=None, + expr=LoadAttr( + expr=self._visitSubscript( + augassign.node, + LoadTemp(index="expr"), + LoadTemp(index="subs"), + "OP_APPLY" + ), + name=self.augassign_methods[augassign.op] + ) + ) + ), + self._visitSubscript( + augassign.node, + LoadTemp(index="expr"), + LoadTemp(index="subs"), + "OP_ASSIGN", + LoadTemp() + ), + ReleaseTemp(index="expr"), + ReleaseTemp(index="subs"), + ReleaseTemp() + ] + + # Make nice annotations for the viewer. + + augassign._op_call = result.code[2].expr + + else: + raise NotImplementedError, augassign.node.__class__ + + return result + + def visitBitand(self, bitand): + + """ + Make a subprogram for the 'bitand' node and record its contents inside the + subprogram. Convert... + + Bitand (node) + (node) + ... + + ...to: + + Subprogram -> Conditional (test) -> ReturnFromBlock ... + (else) -> Conditional (test) -> ReturnFromBlock ... + (else) -> ... + """ + + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) + self.current_subprograms.append(subprogram) + + # In the subprogram, make instructions which store each operand, test + # for each operand's truth status, and if appropriate return from the + # subprogram with the value of the operand. + + last = bitand.nodes[-1] + results = nodes = [] + + # Start by storing the first operand. + + nodes += [ + StoreTemp(expr=self.dispatch(bitand.nodes[0])) + ] + + # For viewing purposes, record invocations on the AST node. + + bitand._ops = [] + + for node in bitand.nodes[1:]: + + # Make a new AST-style node to wrap the operation program nodes. + + new_op = Op("&", node) + bitand._ops.append(new_op) + + # Generate the operation involving the previous result and the + # current operand. + + expr = self._visitBinaryOp(new_op, LoadTemp(), self.dispatch(node), "__and__", "__rand__") + + # Return from the subprogram where the test is not satisfied. + + if node is not last: + nodes += [ + StoreTemp(expr=expr), + Conditional( + test=self._visitNot(LoadTemp()), + body=[ + ReturnFromBlock( + expr=LoadTemp() + ) + ], + else_=[ + # Subsequent operations go here! + ] + ) + ] + + # Put subsequent operations in the else section of this conditional. + + nodes = nodes[-1].else_ + + # For the last operation, return the result. + + else: + nodes.append(ReturnFromBlock(expr=expr)) + + # Finish the subprogram definition. + + subprogram.code = results + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make an invocation of the subprogram. + + result = InvokeRef(bitand, 1, produces_result=1, ref=subprogram) + return result + + def visitBreak(self, break_): + result = ReturnFromBlock(break_, 1) + return result + + def visitCallFunc(self, callfunc): + result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args)) + if callfunc.star_args is not None: + result.star = self.dispatch(callfunc.star_args) + if callfunc.dstar_args is not None: + result.dstar = self.dispatch(callfunc.dstar_args) + result.expr = self.dispatch(callfunc.node) + return result + + def visitClass(self, class_): + + # Add "object" if the class is not "object" and has an empty bases list. + + if class_.name != "object" and not class_.bases: + bases = [compiler.ast.Name("object")] + else: + bases = class_.bases + + structure = Class(name=class_.name, module=self.module, bases=self.dispatches(bases)) + self.structures.append(structure) + within_class = self.within_class + self.within_class = 1 + + # Make a subprogram which initialises the class structure. + + subprogram = Subprogram(name=None, module=self.module, structure=structure, params=[], star=None, dstar=None) + self.current_subprograms.append(subprogram) + self.current_structures.append(structure) # mostly for name construction + + # The class is initialised using the code found inside. + + subprogram.code = self.dispatch(class_.code) + [ReturnFromBlock()] + + self.within_class = within_class + self.current_structures.pop() + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make a definition of the class associating it with a name. + + result = Assign( + code=[ + StoreName(class_, 1, # defines the class + name=class_.name, + expr=LoadRef(ref=structure) + ), + InvokeRef( + class_, + share_locals=0, # override the local sharing usually in InvokeRef + ref=subprogram + ) + ] + ) + return result + + comparison_methods = { + "==" : ("__eq__", "__ne__"), + "!=" : ("__ne__", "__eq__"), + "<" : ("__lt__", "__gt__"), + "<=" : ("__le__", "__ge__"), + ">=" : ("__ge__", "__le__"), + ">" : ("__gt__", "__lt__"), + "is" : None, + "is not" : None, + "in" : None, + "not in" : None + } + + def visitCompare(self, compare): + + """ + Make a subprogram for the 'compare' node and record its contents inside + the subprogram. Convert... + + Compare (expr) + (name/node) + ... + + ...to: + + InvokeRef -> Subprogram -> Conditional (test) -> (body) + (else) -> Conditional (test) -> (body) + (else) -> ... + """ + + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) + self.current_subprograms.append(subprogram) + + # In the subprogram, make instructions which invoke a method on the + # first operand of each operand pair and, if appropriate, return with + # the value from that method. + + last = compare.ops[-1] + previous = self.dispatch(compare.expr) + results = nodes = [] + + # For viewing purposes, record invocations on the AST node. + + compare._ops = [] + + for op in compare.ops: + op_name, node = op + + # Make a new AST-style node to wrap the operation program nodes. + + new_op = Op(op_name, node) + compare._ops.append(new_op) + + expr = self.dispatch(node) + + # Identify the operation and produce the appropriate method call. + + method_names = self.comparison_methods[op_name] + if method_names: + first_name, alternative_name = method_names + invocation = self._visitBinaryCompareOp(new_op, previous, expr, first_name, alternative_name) + + elif op_name == "is": + invocation = InvokeFunction( + new_op, 1, + expr=LoadName(name="__is__"), + args=[previous, expr], + star=None, + dstar=None) + + elif op_name == "is not": + invocation = Not( + new_op, 1, + expr=InvokeFunction( + new_op, + expr=LoadName(name="__is__"), + args=[previous, expr], + star=None, + dstar=None) + ) + + elif op_name == "in": + invocation = InvokeFunction( + new_op, 1, + expr=LoadAttr( + expr=previous, + name="__contains__" + ), + args=[expr], + star=None, + dstar=None) + + elif op_name == "not in": + invocation = Not( + new_op, 1, + expr=InvokeFunction( + new_op, + expr=LoadAttr( + expr=previous, + name="__contains__" + ), + args=[expr], + star=None, + dstar=None) + ) + + else: + raise NotImplementedError, op_name + + nodes.append(StoreTemp(expr=invocation)) + + # Return from the subprogram where the test is not satisfied. + + if op is not last: + nodes.append( + Conditional( + test=self._visitNot(LoadTemp()), + body=[ + ReturnFromBlock(expr=LoadTemp()) + ], + else_=[ + ReleaseTemp() + # Subsequent operations go here! + ] + ) + ) + + # Put subsequent operations in the else section of this conditional. + + nodes = nodes[-1].else_ + + # For the last operation, return the result. + + else: + nodes.append( + ReturnFromBlock(expr=LoadTemp(release=1)) + ) + + previous = expr + + # Finish the subprogram definition. + + subprogram.code = results + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make an invocation of the subprogram. + + result = InvokeRef(compare, 1, produces_result=1, ref=subprogram) + return result + + def visitConst(self, const): + key = "%s-%s" % (const.value.__class__.__name__, const.value) + if not self.constants.has_key(key): + self.constants[key] = Constant(name=repr(const.value), value=const.value) + result = InvokeFunction(const, 1, expr=LoadName(name=self.constants[key].typename)) + return result + + def visitContinue(self, continue_): + result = InvokeRef(continue_, 1, ref=self.current_subprograms[-1]) + return result + + def visitDict(self, dict): + result = InvokeFunction(dict, 1, expr=LoadName(name="dict")) + args = [] + for key, value in dict.items: + tuple = InvokeFunction(dict, expr=LoadName(name="tuple")) + tuple.set_args([self.dispatch(key), self.dispatch(value)]) + args.append(tuple) + result.set_args(args) + return result + + def visitDiscard(self, discard): + return self.dispatch(discard.expr) + + def visitDiv(self, div): + return self._visitBinary(div, "__div__", "__rdiv__") + + def visitFloorDiv(self, floordiv): + return self._visitBinary(floordiv, "__floordiv__", "__rfloordiv__") + + def visitFor(self, for_): + + """ + Make a subprogram for the 'for_' node and record its contents inside the + subprogram. Convert... + + For (assign) + (body) + (else) + + ...to: + + Assign (assign #1) + Invoke -> Subprogram -> Try (body) -> (assign #2) + (body) + Invoke subprogram + (handler) -> ... + (else) -> ... + """ + + return self._visitFor(for_, self.dispatches(for_.body), for_.else_) + + def _visitFor(self, node, body_stmt, else_=None): + + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[]) + self.current_subprograms.append(subprogram) + + # Always return from conditional sections/subprograms. + + if else_ is not None: + else_stmt = self.dispatch(else_) + [ReturnFromBlock()] + else: + else_stmt = [ReturnFromBlock()] + + # Wrap the assignment in a try...except statement. + # Inside the body, add a recursive invocation to the subprogram. + + subprogram.code = [ + Try( + body=[ + Assign( + code=[ + StoreTemp( + expr=InvokeFunction(node, + expr=LoadAttr( + expr=LoadTemp(), + name="next" + ) + ) + ), + self.dispatch(node.assign), + ReleaseTemp() + ]) + ] + body_stmt + [ + InvokeRef( + node, + ref=subprogram + ) + ], + handler=[ + Conditional( + test=InvokeFunction( + node, + expr=LoadName(name="isinstance"), + args=[LoadExc(), LoadName(name="StopIteration")], + star=None, + dstar=None), + body=else_stmt, + else_=[ + Raise( + expr=LoadExc() + ) + ] + ) + ], + else_=[], + finally_=[] + ), + ReturnFromBlock() + ] + + # Finish the subprogram definition. + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Obtain an iterator for the sequence involved. + # Then, make an invocation of the subprogram. + + result = Assign(node, 1, + code=[ + StoreTemp( + expr=InvokeFunction( + node, + expr=LoadAttr( + name="__iter__", + expr=self.dispatch(node.list) + ) + ) + ), + InvokeRef(node, ref=subprogram), + ReleaseTemp() + ] + ) + + # Make nice annotations for the viewer. + + node._iter_call = result.code[0].expr + node._next_call = subprogram.code[0].body[0].code[0].expr + + return result + + def visitFrom(self, from_): + result = Assign(from_, 1) + code = [] + _names = [] + code.append( + StoreTemp( + expr=Import(name=from_.modname, alias=1) + ) + ) + from_._modname = code[-1].expr + for name, alias in from_.names: + code.append( + StoreName( + expr=LoadAttr( + expr=LoadTemp(), + name=name), + name=(alias or name) + ) + ) + _names.append(code[-1].expr) + code.append(ReleaseTemp()) + result.code = code + from_._names = _names + return result + + def _visitFunction(self, function, subprogram): + + """ + A common function generator which transforms the given 'function' node + and initialises the given 'subprogram' appropriately. + """ + + # Discover star and dstar parameters. + + if function.flags & 4 != 0: + has_star = 1 + else: + has_star = 0 + if function.flags & 8 != 0: + has_dstar = 1 + else: + has_dstar = 0 + + # Discover the number of defaults and positional parameters. + + ndefaults = len(function.defaults) + npositional = len(function.argnames) - has_star - has_dstar + + # Produce star and dstar parameters with appropriate defaults. + + if has_star: + star = ( + function.argnames[npositional], + self.dispatch(compiler.ast.List([])) + ) + else: + star = None + if has_dstar: + dstar = ( + function.argnames[npositional + has_star], + self.dispatch(compiler.ast.Dict([])) + ) + else: + dstar = None + + params = [] + for i in range(0, npositional - ndefaults): + params.append((function.argnames[i], None)) + + # Process defaults. + + for i in range(0, ndefaults): + default = function.defaults[i] + if default is not None: + params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default))) + else: + params.append((function.argnames[npositional - ndefaults + i], None)) + + subprogram.params = params + subprogram.star = star + subprogram.dstar = dstar + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + def visitFunction(self, function): + + """ + Make a subprogram for the 'function' and record it outside the main + tree. Produce something like the following: + + StoreName (name) + (expr) -> LoadRef (ref) -> Subprogram (params) + (star) + (dstar) + """ + + subprogram = Subprogram(function, name=function.name, module=self.module, structures=self.current_structures[:], + internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class, original_def=function) + + # Make nice annotations for the viewer. + + function._subprogram = subprogram + + self.current_subprograms.append(subprogram) + within_class = self.within_class + self.within_class = 0 + + subprogram.code = self.dispatch(function.code) + [ReturnFromFunction()] + + self.within_class = within_class + self.current_subprograms.pop() + self._visitFunction(function, subprogram) + + # Make a definition of the function associating it with a name. + + result = StoreName(function, 1, name=function.name, expr=LoadRef(ref=subprogram)) + return result + + def visitGetattr(self, getattr): + result = LoadAttr(getattr, 1, + name=getattr.attrname, + expr=self.dispatch(getattr.expr) + ) + return result + + def visitGlobal(self, global_): + result = Global(global_, 1, + names=global_.names + ) + return result + + def visitIf(self, if_): + + """ + Make conditionals for each test from an 'if_' AST node, adding the body + and putting each subsequent test as part of the conditional's else + section. + + Convert... + + If (test/body) + (test/body) + ... + (else/body) + + ...to: + + Conditional (test) -> (body) + (else) -> Conditional (test) -> (body) + (else) -> ... + """ + + + results = nodes = [] + + # Produce something like... + # expr.__bool__() ? body + + first = 1 + for compare, stmt in if_.tests: + + # Set the first as the defining node. + + test = Conditional(if_, first, + test=InvokeFunction( + if_, + expr=LoadAttr( + expr=self.dispatch(compare), + name="__bool__" + ), + ) + ) + test.body = self.dispatch(stmt) + nodes.append(test) + nodes = test.else_ = [] + first = 0 + + # Add the compound statement from any else clause to the end. + + if if_.else_ is not None: + nodes += self.dispatch(if_.else_) + + result = results[0] + return result + + def visitImport(self, import_): + result = Assign(import_, 1) + code = [] + _names = [] + for path, alias in import_.names: + importer = Import(name=path, alias=alias) + top = alias or path.split(".")[0] + code.append(StoreName(expr=importer, name=top)) + _names.append(code[-1].expr) + result.code = code + import_._names = _names + return result + + def visitInvert(self, invert): + return self._visitUnary(invert, "__invert__") + + def visitKeyword(self, keyword): + result = Keyword(keyword, 1, + name=keyword.name, + expr=self.dispatch(keyword.expr) + ) + return result + + def visitLambda(self, lambda_): + + # Make a subprogram for the function and record it outside the main + # tree. + + subprogram = Subprogram(lambda_, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=lambda_) + + # Make nice annotations for the viewer. + + lambda_._subprogram = subprogram + + # Process the lambda contents. + + self.current_subprograms.append(subprogram) + subprogram.code = [ReturnFromFunction(expr=self.dispatch(lambda_.code))] + self.current_subprograms.pop() + self._visitFunction(lambda_, subprogram) + + # Get the subprogram reference to the lambda. + + return LoadRef(lambda_, 1, ref=subprogram) + + def visitList(self, list): + + # Make a subprogram for the list construction and record it outside the + # main tree. + + subprogram = Subprogram(list, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=list) + self.current_subprograms.append(subprogram) + + # Make nice annotations for the viewer. + + list._subprogram = subprogram + + subprogram.code=[ + StoreTemp( + expr=InvokeFunction( + list, + expr=LoadName( + name="list" + ), + args=[], + star=None, + dstar=None + ) + ) + ] + + for node in list.nodes: + subprogram.code.append( + InvokeFunction( + list, + expr=LoadAttr( + expr=LoadTemp(), + name="append" + ), + args=[self.dispatch(node)], + star=None, + dstar=None + ) + ) + + subprogram.code.append( + ReturnFromBlock( + expr=LoadTemp(release=1) + ) + ) + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make an invocation of the subprogram. + + result = InvokeRef(list, 1, + produces_result=1, + ref=subprogram + ) + return result + + def visitListComp(self, listcomp): + + # Make a subprogram for the list comprehension and record it outside the + # main tree. + + subprogram = Subprogram(listcomp, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=listcomp) + self.current_subprograms.append(subprogram) + + # Make nice annotations for the viewer. + + listcomp._subprogram = subprogram + + # Add a temporary variable. + # Produce for loops within the subprogram. + # Return the result. + + subprogram.code = [ + StoreTemp( + index="listcomp", + expr=InvokeFunction( + expr=LoadName(name="list"), + args=[], + star=None, + dstar=None + ) + ) + ] + self._visitListCompFor(listcomp, listcomp.quals) + [ + ReturnFromBlock( + expr=LoadTemp( + index="listcomp", + release=1 + ) + ) + ] + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make an invocation of the subprogram. + + result = InvokeRef(listcomp, 1, + produces_result=1, + ref=subprogram + ) + return result + + def _visitListCompFor(self, node, quals): + qual = quals[0] + if len(quals) > 1: + body = self._visitListCompFor(node, quals[1:]) + if qual.ifs: + body = self._visitListCompIf(node, qual.ifs, body) + elif qual.ifs: + body = self._visitListCompIf(node, qual.ifs) + else: + body = self._visitListCompBody(node) + return [self._visitFor(qual, body)] + + def _visitListCompIf(self, node, ifs, expr=None): + if_ = ifs[0] + if len(ifs) > 1: + body = self._visitListCompIf(node, ifs[1:], expr) + elif expr is None: + body = self._visitListCompBody(node) + else: + body = expr + return [ + Conditional(if_, 1, + test=InvokeFunction( + if_, + expr=LoadAttr( + expr=self.dispatch(if_.test), + name="__bool__" + ), + ), + body=body, + else_=[] + ) + ] + + def _visitListCompBody(self, node): + return [ + InvokeFunction( + expr=LoadAttr( + expr=LoadTemp(index="listcomp"), + name="append" + ), + args=[self.dispatch(node.expr)], + star=None, + dstar=None + ) + ] + + def visitMod(self, mod): + return self._visitBinary(mod, "__mod__", "__rmod__") + + def visitMul(self, mul): + return self._visitBinary(mul, "__mul__", "__rmul__") + + def visitName(self, name): + result = LoadName(name, 1, name=name.name) + return result + + def _visitNot(self, expr, not_=None): + invocation = InvokeFunction( + not_, # NOTE: May need a real original node. + expr=LoadAttr( + expr=expr, + name="__bool__" + ), + ) + if not_ is not None: + result = Not(not_, 1, expr=invocation) + else: + result = Not(expr=invocation) + return result + + def visitNot(self, not_): + return self._visitNot(self.dispatch(not_.expr), not_) + + def visitOr(self, or_): + + """ + Make a subprogram for the 'or_' node and record its contents inside the + subprogram. Convert... + + Or (test) + (test) + ... + + ...to: + + Subprogram -> Conditional (test) -> ReturnFromBlock ... + (else) -> Conditional (test) -> ReturnFromBlock ... + (else) -> ... + """ + + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) + self.current_subprograms.append(subprogram) + + # In the subprogram, make instructions which store each operand, test + # for each operand's truth status, and if appropriate return from the + # subprogram with the value of the operand. + + last = or_.nodes[-1] + results = nodes = [] + + for node in or_.nodes: + expr = self.dispatch(node) + + # Return from the subprogram where the test is satisfied. + + if node is not last: + nodes.append(StoreTemp(expr=expr)) + invocation = InvokeFunction(or_, expr=LoadAttr(expr=LoadTemp(), name="__bool__")) + test = Conditional(test=invocation, body=[ReturnFromBlock(expr=LoadTemp())]) + nodes.append(test) + + # Put subsequent operations in the else section of this conditional. + + nodes = test.else_ = [ReleaseTemp()] + + # For the last operation, return the result. + + else: + nodes.append( + ReturnFromBlock(expr=expr) + ) + + # Finish the subprogram definition. + + subprogram.code = results + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make an invocation of the subprogram. + + result = InvokeRef(or_, 1, + produces_result=1, + ref=subprogram + ) + return result + + def visitPass(self, pass_): + return Pass(pass_, 1) + + def visitPower(self, power): + return self._visitBinary(power, "__pow__", "__rpow__") + + def visitPrint(self, print_): + + """ + Convert... + + Print (dest) -> + (nodes) + + ...to: + + StoreTemp (index) -> "print" + (expr) -> LoadAttr (expr) -> (dest) + (name) -> "write" + InvokeFunction (expr) -> LoadTemp (index) -> "print" + (args) -> [(node)] + ReleaseTemp (index) -> "print" + """ + + if print_.dest is not None: + dest = self.dispatch(print_.dest) + else: + dest = self.dispatch(compiler.ast.Name("stdout")) + + result = Assign(print_, 1, + code=[ + StoreTemp( + index="print", + expr=LoadAttr( + expr=dest, + name="write" + ) + ) + ] + ) + + for node in print_.nodes: + result.code.append( + InvokeFunction( + print_, + expr=LoadTemp(index="print"), + args=[self.dispatch(node)], + star=None, + dstar=None + ) + ) + + result.code.append( + ReleaseTemp(index="print") + ) + + return result + + def visitPrintnl(self, printnl): + result = self.visitPrint(printnl) + result.code.insert( + len(result.code) - 1, + InvokeFunction( + printnl, + expr=LoadTemp(index="print"), + args=[self.dispatch(compiler.ast.Const("\n"))], + star=None, + dstar=None + ) + ) + return result + + def visitRaise(self, raise_): + result = Raise(raise_, 1) + if raise_.expr2 is None: + result.expr = self.dispatch(raise_.expr1) + else: + result.expr = InvokeFunction( + raise_, + expr=self.dispatch(raise_.expr1), + args=[self.dispatch(raise_.expr2)], + star=None, + dstar=None + ) + if raise_.expr3 is not None: + result.traceback = self.dispatch(raise_.expr3) + else: + result.traceback = None + return result + + def visitReturn(self, return_): + result = ReturnFromFunction(return_, 1, + expr=self.dispatch(return_.value) + ) + return result + + def _visitSlice(self, slice, expr, lower, upper, flags, value=None): + if flags == "OP_ASSIGN": + result = InvokeFunction(slice, 1, + expr=LoadAttr( + expr=expr, + name="__setslice__" + ), + star=None, + dstar=None, + args=[lower, upper, value] + ) + elif flags == "OP_APPLY": + args = [] + result = InvokeFunction(slice, 1, + expr=LoadAttr( + expr=expr, + name="__getslice__" + ), + star=None, + dstar=None, + args=[lower, upper] + ) + elif flags == "OP_DELETE": + args = [] + result = InvokeFunction(slice, 1, + expr=LoadAttr( + expr=expr, + name="__delslice__" + ), + star=None, + dstar=None, + args=[lower, upper] + ) + else: + raise NotImplementedError, flags + + return result + + def visitSlice(self, slice, in_sequence=0): + return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower), + self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, in_sequence)) + + def visitSliceobj(self, sliceobj): + return InvokeFunction(sliceobj, 1, + expr=LoadName(name="slice"), + args=self.dispatches(sliceobj.nodes), + star=None, + dstar=None + ) + + def visitStmt(self, stmt): + return self.dispatches(stmt.nodes) + + def visitSub(self, sub): + return self._visitBinary(sub, "__sub__", "__rsub__") + + def _visitSubscript(self, subscript, expr, subs, flags, value=None): + if flags == "OP_ASSIGN": + result = InvokeFunction(subscript, 1, + expr=LoadAttr( + expr=expr, + name="__setitem__" + ), + star=None, + dstar=None, + args=[subs, value] + ) + elif flags == "OP_APPLY": + args = [] + result = InvokeFunction(subscript, 1, + expr=LoadAttr( + expr=expr, + name="__getitem__" + ), + star=None, + dstar=None, + args=[subs] + ) + elif flags == "OP_DELETE": + args = [] + result = InvokeFunction(subscript, 1, + expr=LoadAttr( + expr=expr, + name="__delitem__" + ), + star=None, + dstar=None, + args=[subs] + ) + else: + raise NotImplementedError, flags + + return result + + def _visitSubscriptSubs(self, node, subs): + if len(subs) == 1: + return self.dispatch(subs[0]) + else: + return InvokeFunction(node, 1, + expr=LoadName(name="tuple"), + args=self.dispatches(subs), + star=None, + dstar=None + ) + + def visitSubscript(self, subscript, in_sequence=0): + return self._visitSubscript( + subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags, + self._visitAssNameOrAttr(subscript, in_sequence) + ) + + def visitTryExcept(self, tryexcept): + + """ + Make conditionals for each handler associated with a 'tryexcept' node. + + Convert... + + TryExcept (body) + (else) + (spec/assign/stmt) + ... + + ...to: + + Try (body) + (else) + (handler) -> Conditional (test) -> (stmt) + (body) -> ResetExc ... + (else) -> Conditional (test) -> (stmt) + (body) -> ResetExc ... + (else) -> ... + """ + + result = Try(tryexcept, 1, body=[], else_=[], finally_=[]) + + if tryexcept.body is not None: + result.body = self.dispatch(tryexcept.body) + if tryexcept.else_ is not None: + result.else_ = self.dispatch(tryexcept.else_) + + results = nodes = [] + catch_all = 0 + + for spec, assign, stmt in tryexcept.handlers: + + # If no specification exists, produce an unconditional block. + + if spec is None: + nodes += self.dispatch(stmt) + catch_all = 1 + + # Produce an exception value check. + + else: + test = Conditional( + isolate_test=1, + test=CheckType(expr=LoadExc(), choices=self._visitTryExcept(spec)) + ) + test.body = [] + + if assign is not None: + test.body.append( + Assign( + code=[ + StoreTemp(expr=LoadExc()), + self.dispatch(assign), + ReleaseTemp() + ] + ) + ) + + test.body += [ResetExc()] + self.dispatch(stmt) + nodes.append(test) + nodes = test.else_ = [] + + # Add a raise operation to deal with unhandled exceptions. + + if not catch_all: + nodes.append( + Raise( + expr=LoadExc()) + ) + + result.handler = results + return result + + def _visitTryExcept(self, spec): + + "Return a list of nodes for the given exception type 'spec'." + + if isinstance(spec, compiler.ast.Tuple): + nodes = [] + for node in spec.nodes: + nodes += self._visitTryExcept(node) + else: + nodes = [self.dispatch(spec)] + return nodes + + def visitTryFinally(self, tryfinally): + result = Try(tryfinally, 1, body=[], else_=[], finally_=[]) + if tryfinally.body is not None: + result.body = self.dispatch(tryfinally.body) + if tryfinally.final is not None: + result.finally_ = self.dispatch(tryfinally.final) + return result + + def visitTuple(self, tuple): + + "Make a MakeTuple node containing the original 'tuple' contents." + + result = MakeTuple(tuple, 1, + nodes=self.dispatches(tuple.nodes) + ) + return result + + def visitUnaryAdd(self, unaryadd): + return self._visitUnary(unaryadd, "__pos__") + + def visitUnarySub(self, unarysub): + return self._visitUnary(unarysub, "__neg__") + + def visitWhile(self, while_): + + """ + Make a subprogram for the 'while' node and record its contents inside the + subprogram. Convert... + + While (test) -> (body) + (else) + + ...to: + + Subprogram -> Conditional (test) -> (body) -> Invoke subprogram + (else) -> Conditional (test) -> ReturnFromBlock ... + (else) -> ... + """ + + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None) + self.current_subprograms.append(subprogram) + + # Include a conditional statement in the subprogram. + # Inside the conditional, add a recursive invocation to the subprogram + # if the test condition was satisfied. + # Return within the main section of the loop. + + test = Conditional( + test=InvokeFunction( + while_, + expr=LoadAttr( + expr=self.dispatch(while_.test), + name="__bool__"), + ), + body=self.dispatch(while_.body) + [ + InvokeRef( + while_, + ref=subprogram + ), + ReturnFromBlock() + ], + else_=[] + ) + + # Provide the else section, if present, along with an explicit return. + + if while_.else_ is not None: + test.else_ = self.dispatch(while_.else_) + [ReturnFromBlock()] + + # Finish the subprogram definition. + + subprogram.code = [test] + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make an invocation of the subprogram. + + result = InvokeRef(while_, 1, ref=subprogram) + + # Make nice annotations for the viewer. + + while_._test_call = subprogram.code[0].test + + return result + + # NOTE: Not actually supported. + # NOTE: Virtually the same as visitReturn... + + def visitYield(self, yield_): + result = Yield(yield_, 1, + expr=self.dispatch(yield_.value) + ) + return result + + # Convenience methods. + + def _visitBinary(self, binary, left_name, right_name): + return self._visitBinaryOp(binary, self.dispatch(binary.left), self.dispatch(binary.right), left_name, right_name) + + def _visitBinaryCompareOp(self, binary, left, right, left_name, right_name): + + """ + Emulate the current mechanisms by producing nodes as follows: + + InvokeRef -> Subprogram -> StoreTemp (expr) -> x.__lt__(y) + Conditional (test) -> __is__(LoadTemp, NotImplemented) + (body) -> ReleaseTemp + StoreTemp (expr) -> y.__gt__(x) + Conditional (test) -> __is__(LoadTemp, NotImplemented) + (body) -> ReturnFromBlock (expr) -> False + (else) -> ReturnFromBlock (expr) -> LoadTemp + (else) -> ReturnFromBlock (expr) -> LoadTemp + """ + + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) + self.current_subprograms.append(subprogram) + + subprogram.code = [ + StoreTemp( + expr=InvokeFunction( + binary, + expr=LoadAttr(expr=left, name=left_name), + args=[right], + star=None, + dstar=None) + ), + Conditional( + isolate_test=1, + test=CheckType( + expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] + ), + body=[ + ReleaseTemp(), + StoreTemp( + expr=InvokeFunction( + binary, + expr=LoadAttr(expr=right, name=right_name), + args=[left], + star=None, + dstar=None) + ), + Conditional( + isolate_test=1, + test=CheckType( + expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] + ), + body=[ + ReturnFromBlock( + expr=LoadName(name="False") + ) + ], + else_=[ + CheckType( + inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] + ), + ReturnFromBlock( + expr=LoadTemp() + ) + ] + ) + ], + else_=[ + CheckType( + inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] + ), + ReturnFromBlock( + expr=LoadTemp() + ) + ] + ) + ] + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + result = InvokeRef( + binary, + produces_result=1, + ref=subprogram + ) + + # Make nice annotations for the viewer. + + binary._left_call = subprogram.code[0].expr + binary._right_call = subprogram.code[1].body[1].expr + + return result + + def _visitBinaryOp(self, binary, left, right, left_name, right_name): + + """ + Emulate the current mechanisms by producing nodes as follows: + + InvokeRef -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y) + (else) + (handler) -> Conditional (test) -> CheckType (expr) -> LoadExc + (choices) -> LoadName TypeError + (body) -> ReturnFromBlock (expr) -> y.__radd__(x) + (else) + """ + + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) + self.current_subprograms.append(subprogram) + + subprogram.code = [ + Try(binary, 1, + body=[ + ReturnFromBlock( + expr=InvokeFunction( + binary, + expr=LoadAttr(expr=left, name=left_name), + args=[right], + star=None, + dstar=None) + ) + ], + else_=[], + finally_=[], + handler=[ + Conditional( + test=CheckType(expr=LoadExc(), choices=[LoadName(name="TypeError")]), + body=[ + ReturnFromBlock( + expr=InvokeFunction( + binary, + expr=LoadAttr(expr=right, name=right_name), + args=[left], + star=None, + dstar=None) + ) + ], + else_=[] + ) + ] + ) + ] + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + result = InvokeRef( + binary, + produces_result=1, + ref=subprogram + ) + + # Make nice annotations for the viewer. + + binary._left_call = subprogram.code[0].body[0].expr + binary._right_call = subprogram.code[0].handler[0].body[0].expr + + return result + + def _visitBuiltin(self, builtin, name): + result = InvokeFunction(builtin, 1, expr=LoadName(name=name), args=self.dispatches(builtin.nodes)) + return result + + def _visitUnary(self, unary, name): + result = InvokeFunction(unary, 1, + expr=LoadAttr( + expr=self.dispatch(unary.expr), + name=name + ) + ) + + # Make nice annotations for the viewer. + + unary._unary_call = result + + return result + +# Convenience functions. + +def simplify(filename, builtins=0, module_name=None): + + """ + Simplify the module stored in the file with the given 'filename'. + + If the optional 'builtins' parameter is set to a true value (the default + being a false value), then the module is considered as the builtins module. + """ + + simplifier = Simplifier(builtins) + module = compiler.parseFile(filename) + compiler.misc.set_filename(filename, module) + if builtins: + name = module_name or "__builtins__" + else: + path, ext = os.path.splitext(filename) + path, name = os.path.split(path) + name = module_name or name + simplified = simplifier.process(module, name) + return simplified + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 1233b49abe97 -r 52cdfcb4af3b simplify/simplified/__init__.py --- a/simplify/simplified/__init__.py Sun May 27 18:30:34 2007 +0200 +++ b/simplify/simplified/__init__.py Sun May 27 19:45:48 2007 +0200 @@ -34,4 +34,25 @@ libdir = os.path.join(os.path.split(os.path.split(__file__)[0])[0], "lib") +# Configuration setting. + +Class = SingleInstanceClass +#Class = MultipleInstanceClass + +def set_single_instance_mode(): + global Class + Class = SingleInstanceClass + +def set_multiple_instance_mode(): + global Class + Class = MultipleInstanceClass + +def set_selective_multiple_instance_mode(): + global Class + Class = SelectiveMultipleInstanceClass + +def set_prolific_multiple_instance_mode(): + global Class + Class = ProlificMultipleInstanceClass + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 1233b49abe97 -r 52cdfcb4af3b simplify/simplified/data.py --- a/simplify/simplified/data.py Sun May 27 18:30:34 2007 +0200 +++ b/simplify/simplified/data.py Sun May 27 19:45:48 2007 +0200 @@ -22,6 +22,7 @@ """ from simplify.simplified.utils import Structure, WithName, name +from simplify.simplified.program import Subprogram # Special non-program nodes. @@ -213,25 +214,4 @@ def __hash__(self): return id(self.type) -# Configuration setting. - -Class = SingleInstanceClass -#Class = MultipleInstanceClass - -def set_single_instance_mode(): - global Class - Class = SingleInstanceClass - -def set_multiple_instance_mode(): - global Class - Class = MultipleInstanceClass - -def set_selective_multiple_instance_mode(): - global Class - Class = SelectiveMultipleInstanceClass - -def set_prolific_multiple_instance_mode(): - global Class - Class = ProlificMultipleInstanceClass - # vim: tabstop=4 expandtab shiftwidth=4 diff -r 1233b49abe97 -r 52cdfcb4af3b simplify/viewer.py --- a/simplify/viewer.py Sun May 27 18:30:34 2007 +0200 +++ b/simplify/viewer.py Sun May 27 19:45:48 2007 +0200 @@ -125,9 +125,39 @@ """ +# Utility classes. + +class Writer: + + "A utility class providing useful HTML output methods." + + def _text(self, text): + return text.replace("&", "&").replace("<", "<").replace(">", ">") + + def _attr(self, attr): + return self._text(attr).replace("'", "'").replace('"', """) + + def _url(self, url): + return self._attr(url).replace("#", "%23").replace("-", "%2d") + +# Summary classes. + +class Summariser(Writer): + + def __init__(self, stream): + self.stream = stream + + def process(self, module): + self.stream.write(html_header) + self._write_classes(module) + self.stream.write(html_footer) + + def _write_classes(self, module): + pass + # Browser classes. -class Browser(ASTVisitor): +class Browser(ASTVisitor, Writer): """ A browsing visitor for AST nodes. @@ -872,15 +902,6 @@ # Output preparation methods. - def _text(self, text): - return text.replace("&", "&").replace("<", "<").replace(">", ">") - - def _attr(self, attr): - return self._text(attr).replace("'", "'").replace('"', """) - - def _url(self, url): - return self._attr(url).replace("#", "%23").replace("-", "%2d") - def _comment(self, comment): self.stream.write("# %s\n" % comment) diff -r 1233b49abe97 -r 52cdfcb4af3b tests/class.py --- a/tests/class.py Sun May 27 18:30:34 2007 +0200 +++ b/tests/class.py Sun May 27 19:45:48 2007 +0200 @@ -1,6 +1,6 @@ class A: "A class with attribute and method." - x = 123 + x = 123.456 def m(self, x): self.x = x def n(self, y=x):