# HG changeset patch # User Paul Boddie # Date 1363043388 -3600 # Node ID fd005d0fb59c311063c890335844ae7f4e8f1d9f # Parent 114c62764785619ab6bdea1da9d4afae5682e205 Refined the purpose of the __main__ function, renamed __module__ to __static__ for statically referenced objects, added constraints on local and global storage, added temporary variable access function descriptions. Added an initial implementation of a syspython translator. diff -r 114c62764785 -r fd005d0fb59c docs/syspython.txt --- a/docs/syspython.txt Sat Feb 23 00:39:09 2013 +0100 +++ b/docs/syspython.txt Tue Mar 12 00:09:48 2013 +0100 @@ -36,11 +36,17 @@ Other than function definitions, no other code statements shall appear in class definitions; such statements will appear after classes have been -defined in a __main__ function collecting together all "loose" -(module-level) statements; class attribute assignments will occur in the -__main__ function, and where a name is associated with a function definition -and another object, the function will also be explicitly assigned in the -__main__ function using its full name. +defined. + +For classes in the module namespace or within other classes, the __main__ +function collects together all "loose" (module-level) statements; class +attribute assignments will occur in the __main__ function, and where a name +is associated with a function definition and another object, the function will +also be explicitly assigned in the __main__ function using its full name. + +For classes in function namespaces, the containing function could contain the +"loose" statements at the point at which the class appears. However, such +classes are not currently supported in micropython. Any class or function defined once in a namespace need not be assigned to that namespace in the __main__ function, but where multiple definitions exist and @@ -63,6 +69,7 @@ ... def __main__(): + __globalnames__(...) ... method = module.C.method if something: @@ -76,12 +83,12 @@ # import package package.__main__() - package = __module__(package) + package = __static__(package) # import package.module package.__main__() package.module.__main__() - package = __module__(package) + package = __static__(package) # from package.module import cls package.__main__() @@ -125,14 +132,22 @@ __localnames__(a, b, x, y) __globalnames__(f, g) - x = 1 # local - y = x # locals - a = b # locals - g = f # globals + __storelocal__(x, 1) + __storelocal__(y, x) + __storelocal__(a, b) + __storeaddress__(module, g, f) Names and Attributes -------------------- +Bare names refer to locals or globals according to the __localnames__ and +__globalnames__ declarations, or to constants such as None, True, False and +NotImplemented. Storage of local or global names is done using explicit +functions as follows: + + __storelocal__(name, value) + __storeaddress__(module, name, value) # see below + No operator usage: all operators are converted to invocations, including all attribute access except static references to modules or particular class or function definitions using the following notation: @@ -169,6 +184,11 @@ __storeattr__(obj, attrname, value) __storeattrindex__(obj, attrname, value) +Temporary variables could employ similar functions: + + __loadtemp__(0) + __storetemp__(0, value) + Operators and Invocations ------------------------- diff -r 114c62764785 -r fd005d0fb59c micropython/syspython.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micropython/syspython.py Tue Mar 12 00:09:48 2013 +0100 @@ -0,0 +1,938 @@ +#!/usr/bin/env python + +""" +Produce syspython code from an inspected program. + +Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from micropython.common import * +from micropython.data import * +from micropython.errors import * +from os.path import exists, extsep, join +import compiler.ast +import sys +import os + +try: + set +except NameError: + from sets import Set as set + +# Convenience definitions. + +module_attribute = compiler.ast.Getattr +special_name = compiler.ast.Name +quoted_name = compiler.ast.Const + +# Source code classes. + +class ConvertedSource(ASTVisitor): + + "A conversion of module source code to syspython." + + def __init__(self, module, program): + self.visitor = self + self.module = module + self.program = program + self.in_main = False + self.units = [] + + def get_unit(self): + return self.units[-1] + + def get_module(self): + return self.units[0] + + def to_stream(self, stream): + + "Write the converted code to the given 'stream'." + + module = self.dispatch(self.module.astnode) + stream.write(str(module)) + + def NOP(self, node): + return node + + def visitModule(self, node): + module = node.unit + self.units.append(module) + + definitions = self._findDefinitions(node) + + # __globalnames__(name, ...) + + globalnames = module.module_attribute_names() and [ + compiler.ast.CallFunc( + special_name("__globalnames__"), + [special_name(name) for name in module.module_attribute_names()] + ) + ] or [] + + # def __main__(): + # ... + + self.in_main = True + + main = compiler.ast.Function( + [], "__main__", [], [], 0, "Module initialisation.", + compiler.ast.Stmt(globalnames + self.dispatch(node.node).nodes) + ) + + self.in_main = False + self.units.pop() + + return compiler.ast.Module(node.doc, compiler.ast.Stmt(definitions + [main])) + + def _findDefinitions(self, node): + definitions = [] + for n in node.getChildNodes(): + if isinstance(n, (compiler.ast.Class, compiler.ast.Function)): + definitions.append(self.dispatch(n)) + else: + definitions += self._findDefinitions(n) + return definitions + + # Statements. + + def visitAssert(self, node): + return compiler.ast.Assert(self.dispatch(node.test), node.fail and self.dispatch(node.fail)) + + def visitAssign(self, node): + expr = self.dispatch(node.expr) + return compiler.ast.Stmt([self.dispatch(n, expr) for n in node.nodes]) + + def visitAugAssign(self, node): + + # lvalue = op(lvalue, expr) + # -> __fn__(lvalue, op(lvalue, expr)) + + op_name = operator_functions[node.op] + + return self.dispatch(node.node, compiler.ast.CallFunc( + module_attribute("operator", op_name), + [self.dispatch(node.node), self.dispatch(node.expr)] + )) + + visitBreak = NOP + + def visitClass(self, node): + if not used_by_unit(node): + return compiler.ast.Stmt([]) + + self.units.append(node.unit) + try: + # Incorporate class body code in the main function. + + if self.in_main: + return self.dispatch(node.code) + else: + return self._visitClassDefinition(node) + + finally: + self.units.pop() + + def _visitClassDefinition(self, node): + cls = node.unit + + # __instattrs__(name, ...) + # __clsattrs__(name, ...) + + instattrs = cls.instance_attribute_names() and [ + compiler.ast.CallFunc( + special_name("__instattrs__"), + [special_name(name) for name in cls.instance_attribute_names()] + ) + ] or [] + + clsattrs = cls.class_attribute_names() and [ + compiler.ast.CallFunc( + special_name("__clsattrs__"), + [special_name(name) for name in cls.class_attribute_names()] + ) + ] or [] + + # __inherited__(superclass, name, ...) + # ... + + attrs_by_cls = {} + for attrname, attr in cls.all_class_attributes().items(): + supercls = attr.parent + if supercls is cls: + continue + if not attrs_by_cls.has_key(supercls): + attrs_by_cls[supercls] = [] + attrs_by_cls[supercls].append(attrname) + + inherited = [] + + for supercls, attrnames in attrs_by_cls.items(): + inherited.append( + compiler.ast.CallFunc( + special_name("__inherited__"), + [special_name(supercls.full_name())] + [special_name(name) for name in attrnames] + )) + + # __descendants__(name, ...) + + descendants = cls.all_descendants() and [ + compiler.ast.CallFunc( + special_name("__descendants__"), + [special_name(name) for name in cls.all_descendants().keys()] + ) + ] or [] + + # Process all the definitions defined inside the class. + + definitions = self._findDefinitions(node) + + return compiler.ast.Class(node.name, [], node.doc, + compiler.ast.Stmt(instattrs + clsattrs + inherited + descendants + definitions) + ) + + visitContinue = NOP + + def visitDiscard(self, node): + return compiler.ast.Discard(self.dispatch(node.expr)) + + def visitFor(self, node): + + """ + Convert from... + + for in : + + [ else: + ] + + ...to... + + _it = iter() + while True: + try: + = _it.next() + except StopIteration: + [ ] + break + else: + + """ + + unit = self.get_unit() + temp = quoted_name(unit.temp_usage) + unit.temp_usage += 1 + + else_nodes = node.else_ and self.dispatch(node.else_).nodes or [] + + return compiler.ast.Stmt([ + # __storetemp__(_it, __loadaddress__(__builtins__, iter)()) + compiler.ast.CallFunc(special_name("__storetemp__"), [ + temp, + compiler.ast.CallFunc( + compiler.ast.CallFunc(special_name("__loadaddress__"), + [special_name("__builtins__"), special_name("iter")] + ), + [self.dispatch(node.list)] + ) + ]), + # while True: ... + compiler.ast.While( + special_name("True"), + # try: ... + compiler.ast.TryExcept( + compiler.ast.Stmt([ + # = ... + self.dispatch(node.assign, + # _it.next() + compiler.ast.CallFunc( + compiler.ast.CallFunc(special_name("__loadattr__"), [ + compiler.ast.CallFunc(special_name("__loadtemp__"), [temp]), + special_name("next") + ]), + [] + ) + ) + ]), + # except StopIteration: ... + [(special_name("StopIteration"), None, compiler.ast.Stmt(else_nodes + [compiler.ast.Break()]))], + # else: ... + self.dispatch(node.body) + ), + None + ) + ]) + + def visitFrom(self, node): + + # Generate __main__ function calls for each step in the imported module + # hierarchy. + + statements = [] + + for modname in self.module.get_module_paths(node.modname): + statements.append( + compiler.ast.CallFunc(special_name("%s.__main__" % modname ), []) + ) + + for name, alias in node.names: + statements.append( + compiler.ast.Assign( + [special_name(alias or name)], + compiler.ast.CallFunc( + special_name("__loadattribute__"), + [special_name(node.modname), special_name(name)] + ) + ) + ) + + return compiler.ast.Stmt(statements) + + def visitFunction(self, node): + if not used_by_unit(node): + return compiler.ast.Stmt([]) + + self.units.append(node.unit) + + try: + # Ignore functions when generating the main function. + + if self.in_main: + return compiler.ast.Stmt([]) + else: + return self._visitFunctionDefinition(node) + finally: + self.units.pop() + + def _visitFunctionDefinition(self, node): + fn = node.unit + + # __localnames__(name, ...) + # __globalnames__(name, ...) + + localnames = fn.locals() and [ + compiler.ast.CallFunc( + special_name("__localnames__"), + [special_name(name) for name in fn.locals().keys()] + ) + ] or [] + + globalnames = fn.globals and [ + compiler.ast.CallFunc( + special_name("__globalnames__"), + [special_name(name) for name in fn.globals] + ) + ] or [] + + defaults = [self.dispatch(n) for n in node.defaults] + + code = self.dispatch(node.code) + + return compiler.ast.Function(node.decorators, node.name, node.argnames, defaults, node.flags, node.doc, + compiler.ast.Stmt(localnames + globalnames + code.nodes)) + + visitGlobal = NOP + + def visitIf(self, node): + return compiler.ast.If( + [(self.dispatch(compare), self.dispatch(stmt)) for (compare, stmt) in node.tests], + node.else_ and self.dispatch(node.else_) + ) + + def visitImport(self, node): + + # Generate __main__ function calls for each step in the imported module + # hierarchy. + + statements = [] + + for name, alias in node.names: + for modname in self.module.get_module_paths(name): + statements.append( + compiler.ast.CallFunc(compiler.ast.Getattr(modname, "__main__"), []) + ) + + statements.append( + compiler.ast.Assign( + [special_name(alias or name.split(".")[0])], + compiler.ast.CallFunc( + special_name("__static__"), + [special_name(name)] + ) + ) + ) + + return compiler.ast.Stmt(statements) + + visitPass = NOP + + def visitPrint(self, node): + return compiler.ast.Print( + [self.dispatch(n) for n in node.nodes], + node.dest and self.dispatch(node.dest) + ) + + def visitPrintnl(self, node): + return compiler.ast.Print( + [self.dispatch(n) for n in node.nodes], + node.dest and self.dispatch(node.dest) + ) + + def visitRaise(self, node): + return compiler.ast.Raise( + node.expr1 and self.dispatch(node.expr1), + node.expr2 and self.dispatch(node.expr2), + node.expr3 and self.dispatch(node.expr3) + ) + + def visitReturn(self, node): + return compiler.ast.Return(self.dispatch(node.value)) + + def visitStmt(self, node): + return compiler.ast.Stmt([self.dispatch(n) for n in node.nodes]) + + def visitTryExcept(self, node): + # NOTE: Need to dispatch to the assignment with the exception. + return compiler.ast.TryExcept( + self.dispatch(node.body), + [(spec and self.dispatch(spec), assign and self.dispatch(assign), self.dispatch(statement)) + for spec, assign, statement in node.handlers], + node.else_ and self.dispatch(node.else_) + ) + + def visitTryFinally(self, node): + return compiler.ast.TryFinally( + self.dispatch(node.body), + self.dispatch(node.final) + ) + + def visitWhile(self, node): + return compiler.ast.While( + self.dispatch(node.test), + self.dispatch(node.body), + node.else_ and self.dispatch(node.else_) + ) + + def visitYield(self, node): + return compiler.ast.Yield(self.dispatch(node.value)) + + # Expression-related helper methods. + + def _visitBitBinary(self, node): + op_name = operator_functions[node.__class__.__name__] + last = self.dispatch(node.nodes[0]) + + for n in node.nodes[1:]: + last = compiler.ast.CallFunc( + module_attribute("operator", op_name), + [last, self.dispatch(n)] + ) + + return last + + def _visitBinary(self, node): + op_name = operator_functions[node.__class__.__name__] + + return compiler.ast.CallFunc( + module_attribute("operator", op_name), + [self.dispatch(node.left), self.dispatch(node.right)] + ) + + def _visitUnary(self, node): + op_name = operator_functions[node.__class__.__name__] + + return compiler.ast.CallFunc( + module_attribute("operator", op_name), + [self.dispatch(node.expr)] + ) + + # Expressions. + + def visitAdd(self, node): + return self._visitBinary(node) + + def visitAnd(self, node): + return compiler.ast.And([self.dispatch(n) for n in node.nodes]) + + def visitAssAttr(self, node, expr): + possible_types = self.possible_accessor_types(node, defining_users=0) + + # NOTE: Derived from Getattr support. + + accessor = self.dispatch(node.expr) + + # NOTE: Replicate the _generateAttr logic. + # NOTE: Should be able to store concrete value details on generated + # NOTE: nodes, such as whether an expression yields a constant. + + # NOTE: Known targets: + # NOTE: __storeaddress__ and __storeaddresscontext__ + + # NOTE: Attributes of self. + + # Usage observations. + + possible_types = self.possible_accessor_types(node, defining_users=0) + + # Record whether types were already deduced. If not, get types using + # only this attribute. + + if not possible_types: + possible_types = self.get_possible_types(node.attrname) + + attributes = self.get_attributes(possible_types, node.attrname) + + # Generate optimisations where only a single attribute applies. + + if len(attributes) == 1: + value, target, target_name = attributes[0] + + # Static attributes. + + if value is not None: + + # Static attributes may be accompanied by a different context + # depending on the accessor. + # NOTE: Should determine whether the context is always replaced. + + return compiler.ast.CallFunc( + special_name("__storeaddresscontextcond__"), + [accessor, special_name(node.attrname), expr] + ) + + # Non-static attributes. + + return compiler.ast.CallFunc( + special_name("__storeattr__"), + [accessor, special_name(node.attrname), expr] + ) + + # With no usable deductions, generate a table-based access. + + return compiler.ast.CallFunc( + special_name("__storeattrindex__"), + [accessor, special_name(node.attrname), expr] + ) + + def visitAssList(self, node, expr): + return compiler.ast.Stmt([ + self.dispatch(n, compiler.ast.CallFunc( + module_attribute("operator", "getitem"), + [expr, i] + )) + for (i, n) in enumerate(node.nodes) + ]) + + def visitAssName(self, node, expr): + unit = self.get_unit() + + # Generate appropriate name access operation. + + scope = getattr(node, "_scope", None) + if not scope: + attr, scope, from_name = self.get_unit()._get_with_scope(node.name) + + if scope == "constant": + return node + elif scope == "local": + + # Function locals are stored using a function. + + if isinstance(unit, Function): + return compiler.ast.CallFunc( + special_name("__storelocal__"), + [special_name(node.name), expr] + ) + + # Class locals are class attribute references. + + elif isinstance(unit, Class): + return compiler.ast.CallFunc( + special_name("__storeaddresscontext__"), + [quoted_name(unit.full_name()), special_name(node.name), expr] + ) + + # Module locals are module attribute references. + + elif isinstance(unit, Module): + return compiler.ast.CallFunc( + special_name("__storeaddress__"), + [quoted_name(unit.full_name()), special_name(node.name), expr] + ) + else: + raise TranslateError("Program unit has no local %r." % name) + + elif scope == "global": + + # Globals are references to module attributes. + + return compiler.ast.CallFunc( + special_name("__storeaddress__"), + [quoted_name(self.get_module().full_name()), special_name(node.name), expr] + ) + + elif scope == "builtin": + + # Builtins are accessed via the __builtins__ module. + + return compiler.ast.CallFunc( + special_name("__storeaddress__"), + [special_name("__builtins__"), special_name(node.name), expr] + ) + + else: + # NOTE: This may happen because a class attribute is optimised away. + return compiler.ast.CallFunc( + special_name("__storeunknown__"), + [special_name(node.name), expr] + ) + + visitAssTuple = visitAssList + + def visitBitand(self, node): + self._visitBitBinary(node) + + def visitBitor(self, node): + self._visitBitBinary(node) + + def visitBitxor(self, node): + self._visitBitBinary(node) + + def visitCallFunc(self, node): + return compiler.ast.CallFunc( + self.dispatch(node.node), + [self.dispatch(arg) for arg in node.args], + node.star_args and [self.dispatch(arg) for arg in node.star_args], + node.dstar_args and [self.dispatch(arg) for arg in node.dstar_args] + ) + + def visitCompare(self, node): + nodes = [] + left = node.expr + for op_name, right in node.ops: + nodes.append( + compiler.ast.CallFunc( + module_attribute("operator", operator_functions.get(op_name)), + [self.dispatch(left), self.dispatch(right)] + ) + ) + left = right + return compiler.ast.And(nodes) + + visitConst = NOP + + def visitDict(self, node): + return compiler.ast.Dict([(self.dispatch(key), self.dispatch(value)) for (key, value) in node.items]) + + def visitDiv(self, node): + return self._visitBinary(node) + + def visitFloorDiv(self, node): + return self._visitBinary(node) + + def visitGetattr(self, node, expr=None): + if expr: + return self.visitAssAttr(node, expr) + + accessor = self.dispatch(node.expr) + + # NOTE: Replicate the _generateAttr logic. + # NOTE: Should be able to store concrete value details on generated + # NOTE: nodes, such as whether an expression yields a constant. + + # NOTE: Known targets: + # NOTE: class.__class__ => __builtins__.type + # NOTE: __loadaddress__ and __loadaddresscontext__ + + # NOTE: Attributes of self. + + # Usage observations. + + possible_types = self.possible_accessor_types(node, defining_users=0) + + # Record whether types were already deduced. If not, get types using + # only this attribute. + + if not possible_types: + possible_types = self.get_possible_types(node.attrname) + + attributes = self.get_attributes(possible_types, node.attrname) + + # Generate optimisations where only a single attribute applies. + + if len(attributes) == 1: + value, target, target_name = attributes[0] + + # Static attributes. + + if value is not None: + + # class.__class__ => __builtins__.type + + if node.attrname == "__class__": + return compiler.ast.CallFunc( + special_name("__loadaddress__"), + [special_name("__builtins__"), special_name("type")] + ) + + # Static attributes may be accompanied by a different context + # depending on the accessor. + # NOTE: Should determine whether the context is always replaced. + + return compiler.ast.CallFunc( + special_name("__loadaddresscontextcond__"), + [accessor, special_name(node.attrname)] + ) + + # Non-static attributes. + + return compiler.ast.CallFunc( + special_name("__loadattr__"), + [accessor, special_name(node.attrname)] + ) + + # With no usable deductions, generate a table-based access. + + return compiler.ast.CallFunc( + special_name("__loadattrindex__"), + [accessor, special_name(node.attrname)] + ) + + def visitGenExpr(self, node): + return compiler.ast.GenExpr(self.dispatch(node.code)) + + def visitGenExprFor(self, node): + return compiler.ast.GenExprFor( + self.dispatch(node.assign), # NOTE: Needs to dispatch to AssName/AssTuple/AssList with an expression. + self.dispatch(node.iter), + [self.dispatch(n) for n in node.ifs] + ) + + def visitGenExprIf(self, node): + return compiler.ast.GenExprIf(self.dispatch(node.test)) + + def visitGenExprInner(self, node): + return compiler.ast.GenExprInner( + self.dispatch(node.expr), + [self.dispatch(n) for n in node.quals] + ) + + def visitIfExp(self, node): + return compiler.ast.IfExp( + self.dispatch(node.then), + self.dispatch(node.test), + self.dispatch(node.else_) + ) + + def visitInvert(self, node): + return self._visitUnary(node) + + def visitKeyword(self, node): + return compiler.ast.Keyword( + node.name, + self.dispatch(node.expr) + ) + + def visitLambda(self, node): + self.units.append(node.unit) + + try: + return compiler.ast.Lambda( + node.argnames, + [self.dispatch(n) for n in node.defaults], + node.flags, + self.dispatch(node.code) + ) + finally: + self.units.pop() + + def visitLeftShift(self, node): + return self._visitBinary(node) + + def visitList(self, node, expr=None): + if expr: + return self.visitAssList(node, expr) + return compiler.ast.List([self.dispatch(n) for n in node.nodes]) + + def visitListComp(self, node): + return compiler.ast.ListComp( + self.dispatch(node.expr), + [self.dispatch(n) for n in node.quals] + ) + + def visitListCompFor(self, node): + return compiler.ast.ListCompFor( + self.dispatch(node.assign), # NOTE: Needs to dispatch to AssName/AssTuple/AssList with an expression. + self.dispatch(node.list), + [self.dispatch(n) for n in node.ifs] + ) + + def visitListCompIf(self, node): + return compiler.ast.ListCompIf( + self.dispatch(node.test) + ) + + def visitMod(self, node): + return self._visitBinary(node) + + def visitMul(self, node): + return self._visitBinary(node) + + def visitName(self, node, expr=None): + if expr: + return self.visitAssName(node, expr) + + unit = self.get_unit() + + # Generate appropriate name access operation. + + scope = getattr(node, "_scope", None) + if not scope: + attr, scope, from_name = self.get_unit()._get_with_scope(node.name) + + if scope == "constant": + return node + elif scope == "local": + + # Function locals are referenced normally. + + if isinstance(unit, Function): + return node + + # Class locals are class attribute references. + # Module locals are module attribute references. + + elif isinstance(unit, (Class, Module)): + return compiler.ast.CallFunc( + special_name("__loadaddress__"), + [quoted_name(unit.full_name()), special_name(node.name)] + ) + else: + raise TranslateError("Program unit has no local %r." % name) + + elif scope == "global": + + # Globals are references to module attributes. + + return compiler.ast.CallFunc( + special_name("__loadaddress__"), + [quoted_name(self.get_module().full_name()), special_name(node.name)] + ) + + elif scope == "builtin": + + # Builtins are accessed via the __builtins__ module. + + return compiler.ast.CallFunc( + special_name("__loadaddress__"), + [special_name("__builtins__"), special_name(node.name)] + ) + + else: + # NOTE: This may happen because a class attribute is optimised away. + return compiler.ast.CallFunc( + special_name("__loadunknown__"), + [special_name(node.name)] + ) + + def visitNot(self, node): + return compiler.ast.Not(self.dispatch(node.expr)) + + def visitOr(self, node): + return compiler.ast.Or([self.dispatch(n) for n in node.nodes]) + + def visitPower(self, node): + return self._visitBinary(node) + + def visitRightShift(self, node): + return self._visitBinary(node) + + def visitSlice(self, node, expr=None): + return compiler.ast.CallFunc( + module_attribute("operator", expr and "setslice" or "getslice"), + [self.dispatch(node.expr), node.lower and self.dispatch(node.lower), node.upper and self.dispatch(node.upper)] + + (expr and [expr] or []) + ) + + def visitSliceobj(self, node): + return compiler.ast.Sliceobj([self.dispatch(n) for n in node.nodes]) + + def visitSub(self, node): + return self._visitBinary(node) + + def visitSubscript(self, node, expr=None): + return compiler.ast.CallFunc( + module_attribute("operator", expr and "setitem" or "getitem"), + [self.dispatch(node.expr), compiler.ast.Tuple([self.dispatch(sub) for sub in node.subs])] + + (expr and [expr] or []) + ) + + def visitTuple(self, node, expr=None): + if expr: + return self.visitAssTuple(node, expr) + return compiler.ast.Tuple([self.dispatch(n) for n in node.nodes]) + + def visitUnaryAdd(self, node): + return self._visitUnary(node) + + def visitUnarySub(self, node): + return self._visitUnary(node) + + # Type-related methods. + + def possible_accessor_types(self, node, defining_users=1): + return set([tn for (tn, st) in ASTVisitor.possible_accessor_types(self, node, defining_users)]) + + def get_possible_types(self, attrname): + objtable = self.program.get_object_table() + return objtable.any_possible_objects([attrname]) + + def get_attributes(self, possible_types, attrname): + objtable = self.program.get_object_table() + attributes = [] + for target_name in possible_types: + target = objtable.get_object(target_name) + try: + attr = objtable.access(target_name, attrname) + except TableError: + continue + if attr.is_static_attribute(): + for v in attr.get_values(): + attributes.append((v, target, target_name)) + else: + attributes.append((None, target, target_name)) + + return attributes + +# Convenience functions. + +def convert(module, program, filename): + stream = open(filename, "wb") + try: + source = ConvertedSource(module, program) + source.to_stream(stream) + finally: + stream.close() + +def translate(program, directory): + if not exists(directory): + os.mkdir(directory) + + # NOTE: Add constants here. + + for module in program.get_importer().get_modules(): + convert(module, program, join(directory, "%s%spy" % (module.full_name(), extsep))) + +# vim: tabstop=4 expandtab shiftwidth=4