micropython

Changeset

638:fd005d0fb59c
2013-03-12 Paul Boddie raw files shortlog changelog graph 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. syspython-as-target
docs/syspython.txt (file) micropython/syspython.py (file)
     1.1 --- a/docs/syspython.txt	Sat Feb 23 00:39:09 2013 +0100
     1.2 +++ b/docs/syspython.txt	Tue Mar 12 00:09:48 2013 +0100
     1.3 @@ -36,11 +36,17 @@
     1.4  
     1.5  Other than function definitions, no other code statements shall appear in
     1.6  class definitions; such statements will appear after classes have been
     1.7 -defined in a __main__ function collecting together all "loose"
     1.8 -(module-level) statements; class attribute assignments will occur in the
     1.9 -__main__ function, and where a name is associated with a function definition
    1.10 -and another object, the function will also be explicitly assigned in the
    1.11 -__main__ function using its full name.
    1.12 +defined.
    1.13 +
    1.14 +For classes in the module namespace or within other classes, the __main__
    1.15 +function collects together all "loose" (module-level) statements; class
    1.16 +attribute assignments will occur in the __main__ function, and where a name
    1.17 +is associated with a function definition and another object, the function will
    1.18 +also be explicitly assigned in the __main__ function using its full name.
    1.19 +
    1.20 +For classes in function namespaces, the containing function could contain the
    1.21 +"loose" statements at the point at which the class appears. However, such
    1.22 +classes are not currently supported in micropython.
    1.23  
    1.24  Any class or function defined once in a namespace need not be assigned to that
    1.25  namespace in the __main__ function, but where multiple definitions exist and
    1.26 @@ -63,6 +69,7 @@
    1.27              ...
    1.28  
    1.29      def __main__():
    1.30 +        __globalnames__(...)
    1.31          ...
    1.32          method = module.C.method
    1.33          if something:
    1.34 @@ -76,12 +83,12 @@
    1.35  
    1.36      # import package
    1.37      package.__main__()
    1.38 -    package = __module__(package)
    1.39 +    package = __static__(package)
    1.40  
    1.41      # import package.module
    1.42      package.__main__()
    1.43      package.module.__main__()
    1.44 -    package = __module__(package)
    1.45 +    package = __static__(package)
    1.46  
    1.47      # from package.module import cls
    1.48      package.__main__()
    1.49 @@ -125,14 +132,22 @@
    1.50          __localnames__(a, b, x, y)
    1.51          __globalnames__(f, g)
    1.52  
    1.53 -        x = 1       # local
    1.54 -        y = x       # locals
    1.55 -        a = b       # locals
    1.56 -        g = f       # globals
    1.57 +        __storelocal__(x, 1)
    1.58 +        __storelocal__(y, x)
    1.59 +        __storelocal__(a, b)
    1.60 +        __storeaddress__(module, g, f)
    1.61  
    1.62  Names and Attributes
    1.63  --------------------
    1.64  
    1.65 +Bare names refer to locals or globals according to the __localnames__ and
    1.66 +__globalnames__ declarations, or to constants such as None, True, False and
    1.67 +NotImplemented. Storage of local or global names is done using explicit
    1.68 +functions as follows:
    1.69 +
    1.70 +    __storelocal__(name, value)
    1.71 +    __storeaddress__(module, name, value) # see below
    1.72 +
    1.73  No operator usage: all operators are converted to invocations, including
    1.74  all attribute access except static references to modules or particular class
    1.75  or function definitions using the following notation:
    1.76 @@ -169,6 +184,11 @@
    1.77      __storeattr__(obj, attrname, value)
    1.78      __storeattrindex__(obj, attrname, value)
    1.79  
    1.80 +Temporary variables could employ similar functions:
    1.81 +
    1.82 +    __loadtemp__(0)
    1.83 +    __storetemp__(0, value)
    1.84 +
    1.85  Operators and Invocations
    1.86  -------------------------
    1.87  
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/micropython/syspython.py	Tue Mar 12 00:09:48 2013 +0100
     2.3 @@ -0,0 +1,938 @@
     2.4 +#!/usr/bin/env python
     2.5 +
     2.6 +"""
     2.7 +Produce syspython code from an inspected program.
     2.8 +
     2.9 +Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Paul Boddie <paul@boddie.org.uk>
    2.10 +
    2.11 +This program is free software; you can redistribute it and/or modify it under
    2.12 +the terms of the GNU General Public License as published by the Free Software
    2.13 +Foundation; either version 3 of the License, or (at your option) any later
    2.14 +version.
    2.15 +
    2.16 +This program is distributed in the hope that it will be useful, but WITHOUT
    2.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    2.18 +FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    2.19 +details.
    2.20 +
    2.21 +You should have received a copy of the GNU General Public License along with
    2.22 +this program.  If not, see <http://www.gnu.org/licenses/>.
    2.23 +"""
    2.24 +
    2.25 +from micropython.common import *
    2.26 +from micropython.data import *
    2.27 +from micropython.errors import *
    2.28 +from os.path import exists, extsep, join
    2.29 +import compiler.ast
    2.30 +import sys
    2.31 +import os
    2.32 +
    2.33 +try:
    2.34 +    set
    2.35 +except NameError:
    2.36 +    from sets import Set as set
    2.37 +
    2.38 +# Convenience definitions.
    2.39 +
    2.40 +module_attribute = compiler.ast.Getattr
    2.41 +special_name = compiler.ast.Name
    2.42 +quoted_name = compiler.ast.Const
    2.43 +
    2.44 +# Source code classes.
    2.45 +
    2.46 +class ConvertedSource(ASTVisitor):
    2.47 +
    2.48 +    "A conversion of module source code to syspython."
    2.49 +
    2.50 +    def __init__(self, module, program):
    2.51 +        self.visitor = self
    2.52 +        self.module = module
    2.53 +        self.program = program
    2.54 +        self.in_main = False
    2.55 +        self.units = []
    2.56 +
    2.57 +    def get_unit(self):
    2.58 +        return self.units[-1]
    2.59 +
    2.60 +    def get_module(self):
    2.61 +        return self.units[0]
    2.62 +
    2.63 +    def to_stream(self, stream):
    2.64 +
    2.65 +        "Write the converted code to the given 'stream'."
    2.66 +
    2.67 +        module = self.dispatch(self.module.astnode)
    2.68 +        stream.write(str(module))
    2.69 +
    2.70 +    def NOP(self, node):
    2.71 +        return node
    2.72 +
    2.73 +    def visitModule(self, node):
    2.74 +        module = node.unit
    2.75 +        self.units.append(module)
    2.76 +
    2.77 +        definitions = self._findDefinitions(node)
    2.78 +
    2.79 +        # __globalnames__(name, ...)
    2.80 +
    2.81 +        globalnames = module.module_attribute_names() and [
    2.82 +            compiler.ast.CallFunc(
    2.83 +                special_name("__globalnames__"),
    2.84 +                [special_name(name) for name in module.module_attribute_names()]
    2.85 +                )
    2.86 +            ] or []
    2.87 +
    2.88 +        # def __main__():
    2.89 +        #     ...
    2.90 +
    2.91 +        self.in_main = True
    2.92 +
    2.93 +        main = compiler.ast.Function(
    2.94 +            [], "__main__", [], [], 0, "Module initialisation.",
    2.95 +            compiler.ast.Stmt(globalnames + self.dispatch(node.node).nodes)
    2.96 +            )
    2.97 +
    2.98 +        self.in_main = False
    2.99 +        self.units.pop()
   2.100 +
   2.101 +        return compiler.ast.Module(node.doc, compiler.ast.Stmt(definitions + [main]))
   2.102 +
   2.103 +    def _findDefinitions(self, node):
   2.104 +        definitions = []
   2.105 +        for n in node.getChildNodes():
   2.106 +            if isinstance(n, (compiler.ast.Class, compiler.ast.Function)):
   2.107 +                definitions.append(self.dispatch(n))
   2.108 +            else:
   2.109 +                definitions += self._findDefinitions(n)
   2.110 +        return definitions
   2.111 +
   2.112 +    # Statements.
   2.113 +
   2.114 +    def visitAssert(self, node):
   2.115 +        return compiler.ast.Assert(self.dispatch(node.test), node.fail and self.dispatch(node.fail))
   2.116 +
   2.117 +    def visitAssign(self, node):
   2.118 +        expr = self.dispatch(node.expr)
   2.119 +        return compiler.ast.Stmt([self.dispatch(n, expr) for n in node.nodes])
   2.120 +
   2.121 +    def visitAugAssign(self, node):
   2.122 +
   2.123 +        #    lvalue = op(lvalue, expr)
   2.124 +        # -> __fn__(lvalue, op(lvalue, expr))
   2.125 +
   2.126 +        op_name = operator_functions[node.op]
   2.127 +
   2.128 +        return self.dispatch(node.node, compiler.ast.CallFunc(
   2.129 +            module_attribute("operator", op_name),
   2.130 +            [self.dispatch(node.node), self.dispatch(node.expr)]
   2.131 +            ))
   2.132 +
   2.133 +    visitBreak = NOP
   2.134 +
   2.135 +    def visitClass(self, node):
   2.136 +        if not used_by_unit(node):
   2.137 +            return compiler.ast.Stmt([])
   2.138 +
   2.139 +        self.units.append(node.unit)
   2.140 +        try:
   2.141 +            # Incorporate class body code in the main function.
   2.142 +
   2.143 +            if self.in_main:
   2.144 +                return self.dispatch(node.code)
   2.145 +            else:
   2.146 +                return self._visitClassDefinition(node)
   2.147 +
   2.148 +        finally:
   2.149 +            self.units.pop()
   2.150 +
   2.151 +    def _visitClassDefinition(self, node):
   2.152 +        cls = node.unit
   2.153 +
   2.154 +        # __instattrs__(name, ...)
   2.155 +        # __clsattrs__(name, ...)
   2.156 +
   2.157 +        instattrs = cls.instance_attribute_names() and [
   2.158 +            compiler.ast.CallFunc(
   2.159 +                special_name("__instattrs__"),
   2.160 +                [special_name(name) for name in cls.instance_attribute_names()]
   2.161 +                )
   2.162 +            ] or []
   2.163 +
   2.164 +        clsattrs = cls.class_attribute_names() and [
   2.165 +            compiler.ast.CallFunc(
   2.166 +                special_name("__clsattrs__"),
   2.167 +                [special_name(name) for name in cls.class_attribute_names()]
   2.168 +                )
   2.169 +            ] or []
   2.170 +
   2.171 +        # __inherited__(superclass, name, ...)
   2.172 +        # ...
   2.173 +
   2.174 +        attrs_by_cls = {}
   2.175 +        for attrname, attr in cls.all_class_attributes().items():
   2.176 +            supercls = attr.parent
   2.177 +            if supercls is cls:
   2.178 +                continue
   2.179 +            if not attrs_by_cls.has_key(supercls):
   2.180 +                attrs_by_cls[supercls] = []
   2.181 +            attrs_by_cls[supercls].append(attrname)
   2.182 +
   2.183 +        inherited = []
   2.184 +
   2.185 +        for supercls, attrnames in attrs_by_cls.items():
   2.186 +            inherited.append(
   2.187 +                compiler.ast.CallFunc(
   2.188 +                    special_name("__inherited__"),
   2.189 +                    [special_name(supercls.full_name())] + [special_name(name) for name in attrnames]
   2.190 +                    ))
   2.191 +
   2.192 +        # __descendants__(name, ...)
   2.193 +
   2.194 +        descendants = cls.all_descendants() and [
   2.195 +            compiler.ast.CallFunc(
   2.196 +                special_name("__descendants__"),
   2.197 +                [special_name(name) for name in cls.all_descendants().keys()]
   2.198 +                )
   2.199 +            ] or []
   2.200 +
   2.201 +        # Process all the definitions defined inside the class.
   2.202 +
   2.203 +        definitions = self._findDefinitions(node)
   2.204 +
   2.205 +        return compiler.ast.Class(node.name, [], node.doc,
   2.206 +            compiler.ast.Stmt(instattrs + clsattrs + inherited + descendants + definitions)
   2.207 +            )
   2.208 +
   2.209 +    visitContinue = NOP
   2.210 +
   2.211 +    def visitDiscard(self, node):
   2.212 +        return compiler.ast.Discard(self.dispatch(node.expr))
   2.213 +
   2.214 +    def visitFor(self, node):
   2.215 +
   2.216 +        """
   2.217 +        Convert from...
   2.218 +
   2.219 +        for <assign> in <list>:
   2.220 +            <body>
   2.221 +        [ else:
   2.222 +            <else_> ]
   2.223 +
   2.224 +        ...to...
   2.225 +
   2.226 +        _it = iter(<list>)
   2.227 +        while True:
   2.228 +            try:
   2.229 +                <assign> = _it.next()
   2.230 +            except StopIteration:
   2.231 +                [ <else_> ]
   2.232 +                break
   2.233 +            else:
   2.234 +                <body>
   2.235 +        """
   2.236 +
   2.237 +        unit = self.get_unit()
   2.238 +        temp = quoted_name(unit.temp_usage)
   2.239 +        unit.temp_usage += 1
   2.240 +
   2.241 +        else_nodes = node.else_ and self.dispatch(node.else_).nodes or []
   2.242 +
   2.243 +        return compiler.ast.Stmt([
   2.244 +            # __storetemp__(_it, __loadaddress__(__builtins__, iter)(<list>))
   2.245 +            compiler.ast.CallFunc(special_name("__storetemp__"), [
   2.246 +                temp,
   2.247 +                compiler.ast.CallFunc(
   2.248 +                    compiler.ast.CallFunc(special_name("__loadaddress__"),
   2.249 +                        [special_name("__builtins__"), special_name("iter")]
   2.250 +                        ),
   2.251 +                    [self.dispatch(node.list)]
   2.252 +                    )
   2.253 +                ]),
   2.254 +            # while True: ...
   2.255 +            compiler.ast.While(
   2.256 +                special_name("True"),
   2.257 +                # try: ...
   2.258 +                compiler.ast.TryExcept(
   2.259 +                    compiler.ast.Stmt([
   2.260 +                        # <assign> = ...
   2.261 +                        self.dispatch(node.assign,
   2.262 +                            # _it.next()
   2.263 +                            compiler.ast.CallFunc(
   2.264 +                                compiler.ast.CallFunc(special_name("__loadattr__"), [
   2.265 +                                    compiler.ast.CallFunc(special_name("__loadtemp__"), [temp]),
   2.266 +                                    special_name("next")
   2.267 +                                    ]),
   2.268 +                                []
   2.269 +                                )
   2.270 +                            )
   2.271 +                        ]),
   2.272 +                    # except StopIteration: ...
   2.273 +                    [(special_name("StopIteration"), None, compiler.ast.Stmt(else_nodes + [compiler.ast.Break()]))],
   2.274 +                    # else: ...
   2.275 +                    self.dispatch(node.body)
   2.276 +                    ),
   2.277 +                None
   2.278 +                )
   2.279 +            ])
   2.280 +
   2.281 +    def visitFrom(self, node):
   2.282 +
   2.283 +        # Generate __main__ function calls for each step in the imported module
   2.284 +        # hierarchy.
   2.285 +
   2.286 +        statements = []
   2.287 +
   2.288 +        for modname in self.module.get_module_paths(node.modname):
   2.289 +            statements.append(
   2.290 +                compiler.ast.CallFunc(special_name("%s.__main__" % modname ), [])
   2.291 +                )
   2.292 +
   2.293 +        for name, alias in node.names:
   2.294 +            statements.append(
   2.295 +                compiler.ast.Assign(
   2.296 +                    [special_name(alias or name)],
   2.297 +                    compiler.ast.CallFunc(
   2.298 +                        special_name("__loadattribute__"),
   2.299 +                        [special_name(node.modname), special_name(name)]
   2.300 +                        )
   2.301 +                    )
   2.302 +                )
   2.303 +
   2.304 +        return compiler.ast.Stmt(statements)
   2.305 +
   2.306 +    def visitFunction(self, node):
   2.307 +        if not used_by_unit(node):
   2.308 +            return compiler.ast.Stmt([])
   2.309 +
   2.310 +        self.units.append(node.unit)
   2.311 +
   2.312 +        try:
   2.313 +            # Ignore functions when generating the main function.
   2.314 +
   2.315 +            if self.in_main:
   2.316 +                return compiler.ast.Stmt([])
   2.317 +            else:
   2.318 +                return self._visitFunctionDefinition(node)
   2.319 +        finally:
   2.320 +            self.units.pop()
   2.321 +
   2.322 +    def _visitFunctionDefinition(self, node):
   2.323 +        fn = node.unit
   2.324 +
   2.325 +        # __localnames__(name, ...)
   2.326 +        # __globalnames__(name, ...)
   2.327 +
   2.328 +        localnames = fn.locals() and [
   2.329 +            compiler.ast.CallFunc(
   2.330 +                special_name("__localnames__"),
   2.331 +                [special_name(name) for name in fn.locals().keys()]
   2.332 +                )
   2.333 +            ] or []
   2.334 +
   2.335 +        globalnames = fn.globals and [
   2.336 +            compiler.ast.CallFunc(
   2.337 +                special_name("__globalnames__"),
   2.338 +                [special_name(name) for name in fn.globals]
   2.339 +                )
   2.340 +            ] or []
   2.341 +
   2.342 +        defaults = [self.dispatch(n) for n in node.defaults]
   2.343 +
   2.344 +        code = self.dispatch(node.code)
   2.345 +
   2.346 +        return compiler.ast.Function(node.decorators, node.name, node.argnames, defaults, node.flags, node.doc,
   2.347 +            compiler.ast.Stmt(localnames + globalnames + code.nodes))
   2.348 +
   2.349 +    visitGlobal = NOP
   2.350 +
   2.351 +    def visitIf(self, node):
   2.352 +        return compiler.ast.If(
   2.353 +            [(self.dispatch(compare), self.dispatch(stmt)) for (compare, stmt) in node.tests],
   2.354 +            node.else_ and self.dispatch(node.else_)
   2.355 +            )
   2.356 +
   2.357 +    def visitImport(self, node):
   2.358 +
   2.359 +        # Generate __main__ function calls for each step in the imported module
   2.360 +        # hierarchy.
   2.361 +
   2.362 +        statements = []
   2.363 +
   2.364 +        for name, alias in node.names:
   2.365 +            for modname in self.module.get_module_paths(name):
   2.366 +                statements.append(
   2.367 +                    compiler.ast.CallFunc(compiler.ast.Getattr(modname, "__main__"), [])
   2.368 +                    )
   2.369 +
   2.370 +            statements.append(
   2.371 +                compiler.ast.Assign(
   2.372 +                    [special_name(alias or name.split(".")[0])],
   2.373 +                    compiler.ast.CallFunc(
   2.374 +                        special_name("__static__"),
   2.375 +                        [special_name(name)]
   2.376 +                        )
   2.377 +                    )
   2.378 +                )
   2.379 +
   2.380 +        return compiler.ast.Stmt(statements)
   2.381 +
   2.382 +    visitPass = NOP
   2.383 +
   2.384 +    def visitPrint(self, node):
   2.385 +        return compiler.ast.Print(
   2.386 +            [self.dispatch(n) for n in node.nodes],
   2.387 +            node.dest and self.dispatch(node.dest)
   2.388 +            )
   2.389 +
   2.390 +    def visitPrintnl(self, node):
   2.391 +        return compiler.ast.Print(
   2.392 +            [self.dispatch(n) for n in node.nodes],
   2.393 +            node.dest and self.dispatch(node.dest)
   2.394 +            )
   2.395 +
   2.396 +    def visitRaise(self, node):
   2.397 +        return compiler.ast.Raise(
   2.398 +            node.expr1 and self.dispatch(node.expr1),
   2.399 +            node.expr2 and self.dispatch(node.expr2),
   2.400 +            node.expr3 and self.dispatch(node.expr3)
   2.401 +            )
   2.402 +
   2.403 +    def visitReturn(self, node):
   2.404 +        return compiler.ast.Return(self.dispatch(node.value))
   2.405 +
   2.406 +    def visitStmt(self, node):
   2.407 +        return compiler.ast.Stmt([self.dispatch(n) for n in node.nodes])
   2.408 +
   2.409 +    def visitTryExcept(self, node):
   2.410 +        # NOTE: Need to dispatch to the assignment with the exception.
   2.411 +        return compiler.ast.TryExcept(
   2.412 +            self.dispatch(node.body),
   2.413 +            [(spec and self.dispatch(spec), assign and self.dispatch(assign), self.dispatch(statement))
   2.414 +                for spec, assign, statement in node.handlers],
   2.415 +            node.else_ and self.dispatch(node.else_)
   2.416 +            )
   2.417 +
   2.418 +    def visitTryFinally(self, node):
   2.419 +        return compiler.ast.TryFinally(
   2.420 +            self.dispatch(node.body),
   2.421 +            self.dispatch(node.final)
   2.422 +            )
   2.423 +
   2.424 +    def visitWhile(self, node):
   2.425 +        return compiler.ast.While(
   2.426 +            self.dispatch(node.test),
   2.427 +            self.dispatch(node.body),
   2.428 +            node.else_ and self.dispatch(node.else_)
   2.429 +            )
   2.430 +
   2.431 +    def visitYield(self, node):
   2.432 +        return compiler.ast.Yield(self.dispatch(node.value))
   2.433 +
   2.434 +    # Expression-related helper methods.
   2.435 +
   2.436 +    def _visitBitBinary(self, node):
   2.437 +        op_name = operator_functions[node.__class__.__name__]
   2.438 +        last = self.dispatch(node.nodes[0])
   2.439 +
   2.440 +        for n in node.nodes[1:]:
   2.441 +            last = compiler.ast.CallFunc(
   2.442 +                module_attribute("operator", op_name),
   2.443 +                [last, self.dispatch(n)]
   2.444 +                )
   2.445 +
   2.446 +        return last
   2.447 +
   2.448 +    def _visitBinary(self, node):
   2.449 +        op_name = operator_functions[node.__class__.__name__]
   2.450 +
   2.451 +        return compiler.ast.CallFunc(
   2.452 +            module_attribute("operator", op_name),
   2.453 +            [self.dispatch(node.left), self.dispatch(node.right)]
   2.454 +            )
   2.455 +
   2.456 +    def _visitUnary(self, node):
   2.457 +        op_name = operator_functions[node.__class__.__name__]
   2.458 +
   2.459 +        return compiler.ast.CallFunc(
   2.460 +            module_attribute("operator", op_name),
   2.461 +            [self.dispatch(node.expr)]
   2.462 +            )
   2.463 +
   2.464 +    # Expressions.
   2.465 +
   2.466 +    def visitAdd(self, node):
   2.467 +        return self._visitBinary(node)
   2.468 +
   2.469 +    def visitAnd(self, node):
   2.470 +        return compiler.ast.And([self.dispatch(n) for n in node.nodes])
   2.471 +
   2.472 +    def visitAssAttr(self, node, expr):
   2.473 +        possible_types = self.possible_accessor_types(node, defining_users=0)
   2.474 +
   2.475 +        # NOTE: Derived from Getattr support.
   2.476 +
   2.477 +        accessor = self.dispatch(node.expr)
   2.478 +
   2.479 +        # NOTE: Replicate the _generateAttr logic.
   2.480 +        # NOTE: Should be able to store concrete value details on generated
   2.481 +        # NOTE: nodes, such as whether an expression yields a constant.
   2.482 +
   2.483 +        # NOTE: Known targets:
   2.484 +        # NOTE: __storeaddress__ and __storeaddresscontext__
   2.485 +
   2.486 +        # NOTE: Attributes of self.
   2.487 +
   2.488 +        # Usage observations.
   2.489 +
   2.490 +        possible_types = self.possible_accessor_types(node, defining_users=0)
   2.491 +
   2.492 +        # Record whether types were already deduced. If not, get types using
   2.493 +        # only this attribute.
   2.494 +
   2.495 +        if not possible_types:
   2.496 +            possible_types = self.get_possible_types(node.attrname)
   2.497 +
   2.498 +        attributes = self.get_attributes(possible_types, node.attrname)
   2.499 +
   2.500 +        # Generate optimisations where only a single attribute applies.
   2.501 +
   2.502 +        if len(attributes) == 1:
   2.503 +            value, target, target_name = attributes[0]
   2.504 +
   2.505 +            # Static attributes.
   2.506 +
   2.507 +            if value is not None:
   2.508 +
   2.509 +                # Static attributes may be accompanied by a different context
   2.510 +                # depending on the accessor.
   2.511 +                # NOTE: Should determine whether the context is always replaced.
   2.512 +
   2.513 +                return compiler.ast.CallFunc(
   2.514 +                    special_name("__storeaddresscontextcond__"),
   2.515 +                    [accessor, special_name(node.attrname), expr]
   2.516 +                    )
   2.517 +
   2.518 +            # Non-static attributes.
   2.519 +
   2.520 +            return compiler.ast.CallFunc(
   2.521 +                special_name("__storeattr__"),
   2.522 +                [accessor, special_name(node.attrname), expr]
   2.523 +                )
   2.524 +
   2.525 +        # With no usable deductions, generate a table-based access.
   2.526 +
   2.527 +        return compiler.ast.CallFunc(
   2.528 +            special_name("__storeattrindex__"),
   2.529 +            [accessor, special_name(node.attrname), expr]
   2.530 +            )
   2.531 +
   2.532 +    def visitAssList(self, node, expr):
   2.533 +        return compiler.ast.Stmt([
   2.534 +            self.dispatch(n, compiler.ast.CallFunc(
   2.535 +                module_attribute("operator", "getitem"),
   2.536 +                [expr, i]
   2.537 +                ))
   2.538 +            for (i, n) in enumerate(node.nodes)
   2.539 +            ])
   2.540 +
   2.541 +    def visitAssName(self, node, expr):
   2.542 +        unit = self.get_unit()
   2.543 +
   2.544 +        # Generate appropriate name access operation.
   2.545 +
   2.546 +        scope = getattr(node, "_scope", None)
   2.547 +        if not scope:
   2.548 +            attr, scope, from_name = self.get_unit()._get_with_scope(node.name)
   2.549 +
   2.550 +        if scope == "constant":
   2.551 +            return node
   2.552 +        elif scope == "local":
   2.553 +
   2.554 +            # Function locals are stored using a function.
   2.555 +
   2.556 +            if isinstance(unit, Function):
   2.557 +                return compiler.ast.CallFunc(
   2.558 +                    special_name("__storelocal__"),
   2.559 +                    [special_name(node.name), expr]
   2.560 +                    )
   2.561 +
   2.562 +            # Class locals are class attribute references.
   2.563 +
   2.564 +            elif isinstance(unit, Class):
   2.565 +                return compiler.ast.CallFunc(
   2.566 +                    special_name("__storeaddresscontext__"),
   2.567 +                    [quoted_name(unit.full_name()), special_name(node.name), expr]
   2.568 +                    )
   2.569 +
   2.570 +            # Module locals are module attribute references.
   2.571 +
   2.572 +            elif isinstance(unit, Module):
   2.573 +                return compiler.ast.CallFunc(
   2.574 +                    special_name("__storeaddress__"),
   2.575 +                    [quoted_name(unit.full_name()), special_name(node.name), expr]
   2.576 +                    )
   2.577 +            else:
   2.578 +                raise TranslateError("Program unit has no local %r." % name)
   2.579 +
   2.580 +        elif scope == "global":
   2.581 +
   2.582 +            # Globals are references to module attributes.
   2.583 +
   2.584 +            return compiler.ast.CallFunc(
   2.585 +                special_name("__storeaddress__"),
   2.586 +                [quoted_name(self.get_module().full_name()), special_name(node.name), expr]
   2.587 +                )
   2.588 +
   2.589 +        elif scope == "builtin":
   2.590 +
   2.591 +            # Builtins are accessed via the __builtins__ module.
   2.592 +
   2.593 +            return compiler.ast.CallFunc(
   2.594 +                special_name("__storeaddress__"),
   2.595 +                [special_name("__builtins__"), special_name(node.name), expr]
   2.596 +                )
   2.597 +
   2.598 +        else:
   2.599 +            # NOTE: This may happen because a class attribute is optimised away.
   2.600 +            return compiler.ast.CallFunc(
   2.601 +                special_name("__storeunknown__"),
   2.602 +                [special_name(node.name), expr]
   2.603 +                )
   2.604 +
   2.605 +    visitAssTuple = visitAssList
   2.606 +
   2.607 +    def visitBitand(self, node):
   2.608 +        self._visitBitBinary(node)
   2.609 +
   2.610 +    def visitBitor(self, node):
   2.611 +        self._visitBitBinary(node)
   2.612 +
   2.613 +    def visitBitxor(self, node):
   2.614 +        self._visitBitBinary(node)
   2.615 +
   2.616 +    def visitCallFunc(self, node):
   2.617 +        return compiler.ast.CallFunc(
   2.618 +            self.dispatch(node.node),
   2.619 +            [self.dispatch(arg) for arg in node.args],
   2.620 +            node.star_args and [self.dispatch(arg) for arg in node.star_args],
   2.621 +            node.dstar_args and [self.dispatch(arg) for arg in node.dstar_args]
   2.622 +            )
   2.623 +
   2.624 +    def visitCompare(self, node):
   2.625 +        nodes = []
   2.626 +        left = node.expr
   2.627 +        for op_name, right in node.ops:
   2.628 +            nodes.append(
   2.629 +                compiler.ast.CallFunc(
   2.630 +                    module_attribute("operator", operator_functions.get(op_name)),
   2.631 +                    [self.dispatch(left), self.dispatch(right)]
   2.632 +                    )
   2.633 +                )
   2.634 +            left = right
   2.635 +        return compiler.ast.And(nodes)
   2.636 +
   2.637 +    visitConst = NOP
   2.638 +
   2.639 +    def visitDict(self, node):
   2.640 +        return compiler.ast.Dict([(self.dispatch(key), self.dispatch(value)) for (key, value) in node.items])
   2.641 +
   2.642 +    def visitDiv(self, node):
   2.643 +        return self._visitBinary(node)
   2.644 +
   2.645 +    def visitFloorDiv(self, node):
   2.646 +        return self._visitBinary(node)
   2.647 +
   2.648 +    def visitGetattr(self, node, expr=None):
   2.649 +        if expr:
   2.650 +            return self.visitAssAttr(node, expr)
   2.651 +
   2.652 +        accessor = self.dispatch(node.expr)
   2.653 +
   2.654 +        # NOTE: Replicate the _generateAttr logic.
   2.655 +        # NOTE: Should be able to store concrete value details on generated
   2.656 +        # NOTE: nodes, such as whether an expression yields a constant.
   2.657 +
   2.658 +        # NOTE: Known targets:
   2.659 +        # NOTE: class.__class__ => __builtins__.type
   2.660 +        # NOTE: __loadaddress__ and __loadaddresscontext__
   2.661 +
   2.662 +        # NOTE: Attributes of self.
   2.663 +
   2.664 +        # Usage observations.
   2.665 +
   2.666 +        possible_types = self.possible_accessor_types(node, defining_users=0)
   2.667 +
   2.668 +        # Record whether types were already deduced. If not, get types using
   2.669 +        # only this attribute.
   2.670 +
   2.671 +        if not possible_types:
   2.672 +            possible_types = self.get_possible_types(node.attrname)
   2.673 +
   2.674 +        attributes = self.get_attributes(possible_types, node.attrname)
   2.675 +
   2.676 +        # Generate optimisations where only a single attribute applies.
   2.677 +
   2.678 +        if len(attributes) == 1:
   2.679 +            value, target, target_name = attributes[0]
   2.680 +
   2.681 +            # Static attributes.
   2.682 +
   2.683 +            if value is not None:
   2.684 +
   2.685 +                # class.__class__ => __builtins__.type
   2.686 +
   2.687 +                if node.attrname == "__class__":
   2.688 +                    return compiler.ast.CallFunc(
   2.689 +                        special_name("__loadaddress__"),
   2.690 +                        [special_name("__builtins__"), special_name("type")]
   2.691 +                        )
   2.692 +
   2.693 +                # Static attributes may be accompanied by a different context
   2.694 +                # depending on the accessor.
   2.695 +                # NOTE: Should determine whether the context is always replaced.
   2.696 +
   2.697 +                return compiler.ast.CallFunc(
   2.698 +                    special_name("__loadaddresscontextcond__"),
   2.699 +                    [accessor, special_name(node.attrname)]
   2.700 +                    )
   2.701 +
   2.702 +            # Non-static attributes.
   2.703 +
   2.704 +            return compiler.ast.CallFunc(
   2.705 +                special_name("__loadattr__"),
   2.706 +                [accessor, special_name(node.attrname)]
   2.707 +                )
   2.708 +
   2.709 +        # With no usable deductions, generate a table-based access.
   2.710 +
   2.711 +        return compiler.ast.CallFunc(
   2.712 +            special_name("__loadattrindex__"),
   2.713 +            [accessor, special_name(node.attrname)]
   2.714 +            )
   2.715 +
   2.716 +    def visitGenExpr(self, node):
   2.717 +        return compiler.ast.GenExpr(self.dispatch(node.code))
   2.718 +
   2.719 +    def visitGenExprFor(self, node):
   2.720 +        return compiler.ast.GenExprFor(
   2.721 +            self.dispatch(node.assign), # NOTE: Needs to dispatch to AssName/AssTuple/AssList with an expression.
   2.722 +            self.dispatch(node.iter),
   2.723 +            [self.dispatch(n) for n in node.ifs]
   2.724 +            )
   2.725 +
   2.726 +    def visitGenExprIf(self, node):
   2.727 +        return compiler.ast.GenExprIf(self.dispatch(node.test))
   2.728 +
   2.729 +    def visitGenExprInner(self, node):
   2.730 +        return compiler.ast.GenExprInner(
   2.731 +            self.dispatch(node.expr),
   2.732 +            [self.dispatch(n) for n in node.quals]
   2.733 +            )
   2.734 +
   2.735 +    def visitIfExp(self, node):
   2.736 +        return compiler.ast.IfExp(
   2.737 +            self.dispatch(node.then),
   2.738 +            self.dispatch(node.test),
   2.739 +            self.dispatch(node.else_)
   2.740 +            )
   2.741 +
   2.742 +    def visitInvert(self, node):
   2.743 +        return self._visitUnary(node)
   2.744 +
   2.745 +    def visitKeyword(self, node):
   2.746 +        return compiler.ast.Keyword(
   2.747 +            node.name,
   2.748 +            self.dispatch(node.expr)
   2.749 +            )
   2.750 +
   2.751 +    def visitLambda(self, node):
   2.752 +        self.units.append(node.unit)
   2.753 +
   2.754 +        try:
   2.755 +            return compiler.ast.Lambda(
   2.756 +                node.argnames,
   2.757 +                [self.dispatch(n) for n in node.defaults],
   2.758 +                node.flags,
   2.759 +                self.dispatch(node.code)
   2.760 +                )
   2.761 +        finally:
   2.762 +            self.units.pop()
   2.763 +
   2.764 +    def visitLeftShift(self, node):
   2.765 +        return self._visitBinary(node)
   2.766 +
   2.767 +    def visitList(self, node, expr=None):
   2.768 +        if expr:
   2.769 +            return self.visitAssList(node, expr)
   2.770 +        return compiler.ast.List([self.dispatch(n) for n in node.nodes])
   2.771 +
   2.772 +    def visitListComp(self, node):
   2.773 +        return compiler.ast.ListComp(
   2.774 +            self.dispatch(node.expr),
   2.775 +            [self.dispatch(n) for n in node.quals]
   2.776 +            )
   2.777 +
   2.778 +    def visitListCompFor(self, node):
   2.779 +        return compiler.ast.ListCompFor(
   2.780 +            self.dispatch(node.assign), # NOTE: Needs to dispatch to AssName/AssTuple/AssList with an expression.
   2.781 +            self.dispatch(node.list),
   2.782 +            [self.dispatch(n) for n in node.ifs]
   2.783 +            )
   2.784 +
   2.785 +    def visitListCompIf(self, node):
   2.786 +        return compiler.ast.ListCompIf(
   2.787 +            self.dispatch(node.test)
   2.788 +            )
   2.789 +
   2.790 +    def visitMod(self, node):
   2.791 +        return self._visitBinary(node)
   2.792 +
   2.793 +    def visitMul(self, node):
   2.794 +        return self._visitBinary(node)
   2.795 +
   2.796 +    def visitName(self, node, expr=None):
   2.797 +        if expr:
   2.798 +            return self.visitAssName(node, expr)
   2.799 +
   2.800 +        unit = self.get_unit()
   2.801 +
   2.802 +        # Generate appropriate name access operation.
   2.803 +
   2.804 +        scope = getattr(node, "_scope", None)
   2.805 +        if not scope:
   2.806 +            attr, scope, from_name = self.get_unit()._get_with_scope(node.name)
   2.807 +
   2.808 +        if scope == "constant":
   2.809 +            return node
   2.810 +        elif scope == "local":
   2.811 +
   2.812 +            # Function locals are referenced normally.
   2.813 +
   2.814 +            if isinstance(unit, Function):
   2.815 +                return node
   2.816 +
   2.817 +            # Class locals are class attribute references.
   2.818 +            # Module locals are module attribute references.
   2.819 +
   2.820 +            elif isinstance(unit, (Class, Module)):
   2.821 +                return compiler.ast.CallFunc(
   2.822 +                    special_name("__loadaddress__"),
   2.823 +                    [quoted_name(unit.full_name()), special_name(node.name)]
   2.824 +                    )
   2.825 +            else:
   2.826 +                raise TranslateError("Program unit has no local %r." % name)
   2.827 +
   2.828 +        elif scope == "global":
   2.829 +
   2.830 +            # Globals are references to module attributes.
   2.831 +
   2.832 +            return compiler.ast.CallFunc(
   2.833 +                special_name("__loadaddress__"),
   2.834 +                [quoted_name(self.get_module().full_name()), special_name(node.name)]
   2.835 +                )
   2.836 +
   2.837 +        elif scope == "builtin":
   2.838 +
   2.839 +            # Builtins are accessed via the __builtins__ module.
   2.840 +
   2.841 +            return compiler.ast.CallFunc(
   2.842 +                special_name("__loadaddress__"),
   2.843 +                [special_name("__builtins__"), special_name(node.name)]
   2.844 +                )
   2.845 +
   2.846 +        else:
   2.847 +            # NOTE: This may happen because a class attribute is optimised away.
   2.848 +            return compiler.ast.CallFunc(
   2.849 +                special_name("__loadunknown__"),
   2.850 +                [special_name(node.name)]
   2.851 +                )
   2.852 +
   2.853 +    def visitNot(self, node):
   2.854 +        return compiler.ast.Not(self.dispatch(node.expr))
   2.855 +
   2.856 +    def visitOr(self, node):
   2.857 +        return compiler.ast.Or([self.dispatch(n) for n in node.nodes])
   2.858 +
   2.859 +    def visitPower(self, node):
   2.860 +        return self._visitBinary(node)
   2.861 +
   2.862 +    def visitRightShift(self, node):
   2.863 +        return self._visitBinary(node)
   2.864 +
   2.865 +    def visitSlice(self, node, expr=None):
   2.866 +        return compiler.ast.CallFunc(
   2.867 +            module_attribute("operator", expr and "setslice" or "getslice"),
   2.868 +            [self.dispatch(node.expr), node.lower and self.dispatch(node.lower), node.upper and self.dispatch(node.upper)]
   2.869 +            + (expr and [expr] or [])
   2.870 +            )
   2.871 +
   2.872 +    def visitSliceobj(self, node):
   2.873 +        return compiler.ast.Sliceobj([self.dispatch(n) for n in node.nodes])
   2.874 +
   2.875 +    def visitSub(self, node):
   2.876 +        return self._visitBinary(node)
   2.877 +
   2.878 +    def visitSubscript(self, node, expr=None):
   2.879 +        return compiler.ast.CallFunc(
   2.880 +            module_attribute("operator", expr and "setitem" or "getitem"),
   2.881 +            [self.dispatch(node.expr), compiler.ast.Tuple([self.dispatch(sub) for sub in node.subs])]
   2.882 +            + (expr and [expr] or [])
   2.883 +            )
   2.884 +
   2.885 +    def visitTuple(self, node, expr=None):
   2.886 +        if expr:
   2.887 +            return self.visitAssTuple(node, expr)
   2.888 +        return compiler.ast.Tuple([self.dispatch(n) for n in node.nodes])
   2.889 +
   2.890 +    def visitUnaryAdd(self, node):
   2.891 +        return self._visitUnary(node)
   2.892 +
   2.893 +    def visitUnarySub(self, node):
   2.894 +        return self._visitUnary(node)
   2.895 +
   2.896 +    # Type-related methods.
   2.897 +
   2.898 +    def possible_accessor_types(self, node, defining_users=1):
   2.899 +        return set([tn for (tn, st) in ASTVisitor.possible_accessor_types(self, node, defining_users)])
   2.900 +
   2.901 +    def get_possible_types(self, attrname):
   2.902 +        objtable = self.program.get_object_table()
   2.903 +        return objtable.any_possible_objects([attrname])
   2.904 +
   2.905 +    def get_attributes(self, possible_types, attrname):
   2.906 +        objtable = self.program.get_object_table()
   2.907 +        attributes = []
   2.908 +        for target_name in possible_types:
   2.909 +            target = objtable.get_object(target_name)
   2.910 +            try:
   2.911 +                attr = objtable.access(target_name, attrname)
   2.912 +            except TableError:
   2.913 +                continue
   2.914 +            if attr.is_static_attribute():
   2.915 +                for v in attr.get_values():
   2.916 +                    attributes.append((v, target, target_name))
   2.917 +            else:
   2.918 +                attributes.append((None, target, target_name))
   2.919 +
   2.920 +        return attributes
   2.921 +
   2.922 +# Convenience functions.
   2.923 +
   2.924 +def convert(module, program, filename):
   2.925 +    stream = open(filename, "wb")
   2.926 +    try:
   2.927 +        source = ConvertedSource(module, program)
   2.928 +        source.to_stream(stream)
   2.929 +    finally:
   2.930 +        stream.close()
   2.931 +
   2.932 +def translate(program, directory):
   2.933 +    if not exists(directory):
   2.934 +        os.mkdir(directory)
   2.935 +
   2.936 +    # NOTE: Add constants here.
   2.937 +
   2.938 +    for module in program.get_importer().get_modules():
   2.939 +        convert(module, program, join(directory, "%s%spy" % (module.full_name(), extsep)))
   2.940 +
   2.941 +# vim: tabstop=4 expandtab shiftwidth=4