mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	The compiler package is now part of the standard library.
Remove all these files. All except astgen.py are moved to Lib/compiler.
This commit is contained in:
		
							parent
							
								
									f6cc07cffe
								
							
						
					
					
						commit
						b3c569ce82
					
				
					 12 changed files with 0 additions and 5811 deletions
				
			
		| 
						 | 
					@ -1,27 +0,0 @@
 | 
				
			||||||
"""Package for parsing and compiling Python source code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There are several functions defined at the top level that are imported
 | 
					 | 
				
			||||||
from modules contained in the package.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
parse(buf, mode="exec") -> AST
 | 
					 | 
				
			||||||
    Converts a string containing Python source code to an abstract
 | 
					 | 
				
			||||||
    syntax tree (AST).  The AST is defined in compiler.ast.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
parseFile(path) -> AST
 | 
					 | 
				
			||||||
    The same as parse(open(path))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
walk(ast, visitor, verbose=None)
 | 
					 | 
				
			||||||
    Does a pre-order walk over the ast using the visitor instance.
 | 
					 | 
				
			||||||
    See compiler.visitor for details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
compile(source, filename, mode, flags=None, dont_inherit=None)
 | 
					 | 
				
			||||||
    Returns a code object.  A replacement for the builtin compile() function. 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
compileFile(filename)
 | 
					 | 
				
			||||||
    Generates a .pyc file by compilining filename.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from transformer import parse, parseFile
 | 
					 | 
				
			||||||
from visitor import walk
 | 
					 | 
				
			||||||
from pycodegen import compile, compileFile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -1,280 +0,0 @@
 | 
				
			||||||
"""Generate ast module from specification
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This script generates the ast module from a simple specification,
 | 
					 | 
				
			||||||
which makes it easy to accomodate changes in the grammar.  This
 | 
					 | 
				
			||||||
approach would be quite reasonable if the grammar changed often.
 | 
					 | 
				
			||||||
Instead, it is rather complex to generate the appropriate code.  And
 | 
					 | 
				
			||||||
the Node interface has changed more often than the grammar.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import fileinput
 | 
					 | 
				
			||||||
import getopt
 | 
					 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
from StringIO import StringIO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SPEC = "ast.txt"
 | 
					 | 
				
			||||||
COMMA = ", "
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def load_boilerplate(file):
 | 
					 | 
				
			||||||
    f = open(file)
 | 
					 | 
				
			||||||
    buf = f.read()
 | 
					 | 
				
			||||||
    f.close()
 | 
					 | 
				
			||||||
    i = buf.find('### ''PROLOGUE')
 | 
					 | 
				
			||||||
    j = buf.find('### ''EPILOGUE')
 | 
					 | 
				
			||||||
    pro = buf[i+12:j].strip()
 | 
					 | 
				
			||||||
    epi = buf[j+12:].strip()
 | 
					 | 
				
			||||||
    return pro, epi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def strip_default(arg):
 | 
					 | 
				
			||||||
    """Return the argname from an 'arg = default' string"""
 | 
					 | 
				
			||||||
    i = arg.find('=')
 | 
					 | 
				
			||||||
    if i == -1:
 | 
					 | 
				
			||||||
        return arg
 | 
					 | 
				
			||||||
    t = arg[:i].strip()
 | 
					 | 
				
			||||||
    return t
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
P_NODE = 1
 | 
					 | 
				
			||||||
P_OTHER = 2
 | 
					 | 
				
			||||||
P_NESTED = 3
 | 
					 | 
				
			||||||
P_NONE = 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class NodeInfo:
 | 
					 | 
				
			||||||
    """Each instance describes a specific AST node"""
 | 
					 | 
				
			||||||
    def __init__(self, name, args):
 | 
					 | 
				
			||||||
        self.name = name
 | 
					 | 
				
			||||||
        self.args = args.strip()
 | 
					 | 
				
			||||||
        self.argnames = self.get_argnames()
 | 
					 | 
				
			||||||
        self.argprops = self.get_argprops()
 | 
					 | 
				
			||||||
        self.nargs = len(self.argnames)
 | 
					 | 
				
			||||||
        self.init = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_argnames(self):
 | 
					 | 
				
			||||||
        if '(' in self.args:
 | 
					 | 
				
			||||||
            i = self.args.find('(')
 | 
					 | 
				
			||||||
            j = self.args.rfind(')')
 | 
					 | 
				
			||||||
            args = self.args[i+1:j]
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            args = self.args
 | 
					 | 
				
			||||||
        return [strip_default(arg.strip())
 | 
					 | 
				
			||||||
                for arg in args.split(',') if arg]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_argprops(self):
 | 
					 | 
				
			||||||
        """Each argument can have a property like '*' or '!'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        XXX This method modifies the argnames in place!
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        d = {}
 | 
					 | 
				
			||||||
        hardest_arg = P_NODE
 | 
					 | 
				
			||||||
        for i in range(len(self.argnames)):
 | 
					 | 
				
			||||||
            arg = self.argnames[i]
 | 
					 | 
				
			||||||
            if arg.endswith('*'):
 | 
					 | 
				
			||||||
                arg = self.argnames[i] = arg[:-1]
 | 
					 | 
				
			||||||
                d[arg] = P_OTHER
 | 
					 | 
				
			||||||
                hardest_arg = max(hardest_arg, P_OTHER)
 | 
					 | 
				
			||||||
            elif arg.endswith('!'):
 | 
					 | 
				
			||||||
                arg = self.argnames[i] = arg[:-1]
 | 
					 | 
				
			||||||
                d[arg] = P_NESTED
 | 
					 | 
				
			||||||
                hardest_arg = max(hardest_arg, P_NESTED)
 | 
					 | 
				
			||||||
            elif arg.endswith('&'):
 | 
					 | 
				
			||||||
                arg = self.argnames[i] = arg[:-1]
 | 
					 | 
				
			||||||
                d[arg] = P_NONE
 | 
					 | 
				
			||||||
                hardest_arg = max(hardest_arg, P_NONE)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                d[arg] = P_NODE
 | 
					 | 
				
			||||||
        self.hardest_arg = hardest_arg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if hardest_arg > P_NODE:
 | 
					 | 
				
			||||||
            self.args = self.args.replace('*', '')
 | 
					 | 
				
			||||||
            self.args = self.args.replace('!', '')
 | 
					 | 
				
			||||||
            self.args = self.args.replace('&', '')
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        return d
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def gen_source(self):
 | 
					 | 
				
			||||||
        buf = StringIO()
 | 
					 | 
				
			||||||
        print >> buf, "class %s(Node):" % self.name
 | 
					 | 
				
			||||||
        print >> buf, '    nodes["%s"] = "%s"' % (self.name.lower(), self.name)
 | 
					 | 
				
			||||||
        self._gen_init(buf)
 | 
					 | 
				
			||||||
        print >> buf
 | 
					 | 
				
			||||||
        self._gen_getChildren(buf)
 | 
					 | 
				
			||||||
        print >> buf
 | 
					 | 
				
			||||||
        self._gen_getChildNodes(buf)
 | 
					 | 
				
			||||||
        print >> buf
 | 
					 | 
				
			||||||
        self._gen_repr(buf)
 | 
					 | 
				
			||||||
        buf.seek(0, 0)
 | 
					 | 
				
			||||||
        return buf.read()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _gen_init(self, buf):
 | 
					 | 
				
			||||||
        print >> buf, "    def __init__(self, %s):" % self.args
 | 
					 | 
				
			||||||
        if self.argnames:
 | 
					 | 
				
			||||||
            for name in self.argnames:
 | 
					 | 
				
			||||||
                print >> buf, "        self.%s = %s" % (name, name)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            print >> buf, "        pass"
 | 
					 | 
				
			||||||
        if self.init:
 | 
					 | 
				
			||||||
            print >> buf, "".join(["    " + line for line in self.init])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _gen_getChildren(self, buf):
 | 
					 | 
				
			||||||
        print >> buf, "    def getChildren(self):"
 | 
					 | 
				
			||||||
        if len(self.argnames) == 0:
 | 
					 | 
				
			||||||
            print >> buf, "        return ()"
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            if self.hardest_arg < P_NESTED:
 | 
					 | 
				
			||||||
                clist = COMMA.join(["self.%s" % c
 | 
					 | 
				
			||||||
                                    for c in self.argnames])
 | 
					 | 
				
			||||||
                if self.nargs == 1:
 | 
					 | 
				
			||||||
                    print >> buf, "        return %s," % clist
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    print >> buf, "        return %s" % clist
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                print >> buf, "        children = []"
 | 
					 | 
				
			||||||
                template = "        children.%s(%sself.%s%s)"
 | 
					 | 
				
			||||||
                for name in self.argnames:
 | 
					 | 
				
			||||||
                    if self.argprops[name] == P_NESTED:
 | 
					 | 
				
			||||||
                        print >> buf, template % ("extend", "flatten(",
 | 
					 | 
				
			||||||
                                                  name, ")")
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        print >> buf, template % ("append", "", name, "")
 | 
					 | 
				
			||||||
                print >> buf, "        return tuple(children)"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _gen_getChildNodes(self, buf):
 | 
					 | 
				
			||||||
        print >> buf, "    def getChildNodes(self):"
 | 
					 | 
				
			||||||
        if len(self.argnames) == 0:
 | 
					 | 
				
			||||||
            print >> buf, "        return ()"
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            if self.hardest_arg < P_NESTED:
 | 
					 | 
				
			||||||
                clist = ["self.%s" % c
 | 
					 | 
				
			||||||
                         for c in self.argnames
 | 
					 | 
				
			||||||
                         if self.argprops[c] == P_NODE]
 | 
					 | 
				
			||||||
                if len(clist) == 0:
 | 
					 | 
				
			||||||
                    print >> buf, "        return ()"
 | 
					 | 
				
			||||||
                elif len(clist) == 1:
 | 
					 | 
				
			||||||
                    print >> buf, "        return %s," % clist[0]
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    print >> buf, "        return %s" % COMMA.join(clist)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                print >> buf, "        nodes = []"
 | 
					 | 
				
			||||||
                template = "        nodes.%s(%sself.%s%s)"
 | 
					 | 
				
			||||||
                for name in self.argnames:
 | 
					 | 
				
			||||||
                    if self.argprops[name] == P_NONE:
 | 
					 | 
				
			||||||
                        tmp = ("        if self.%s is not None:" 
 | 
					 | 
				
			||||||
                               "            nodes.append(self.%s)")
 | 
					 | 
				
			||||||
                        print >> buf, tmp % (name, name)
 | 
					 | 
				
			||||||
                    elif self.argprops[name] == P_NESTED:
 | 
					 | 
				
			||||||
                        print >> buf, template % ("extend", "flatten_nodes(",
 | 
					 | 
				
			||||||
                                                  name, ")")
 | 
					 | 
				
			||||||
                    elif self.argprops[name] == P_NODE:
 | 
					 | 
				
			||||||
                        print >> buf, template % ("append", "", name, "")
 | 
					 | 
				
			||||||
                print >> buf, "        return tuple(nodes)"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _gen_repr(self, buf):
 | 
					 | 
				
			||||||
        print >> buf, "    def __repr__(self):"
 | 
					 | 
				
			||||||
        if self.argnames:
 | 
					 | 
				
			||||||
            fmt = COMMA.join(["%s"] * self.nargs)
 | 
					 | 
				
			||||||
            if '(' in self.args:
 | 
					 | 
				
			||||||
                fmt = '(%s)' % fmt
 | 
					 | 
				
			||||||
            vals = ["repr(self.%s)" % name for name in self.argnames]
 | 
					 | 
				
			||||||
            vals = COMMA.join(vals)
 | 
					 | 
				
			||||||
            if self.nargs == 1:
 | 
					 | 
				
			||||||
                vals = vals + ","
 | 
					 | 
				
			||||||
            print >> buf, '        return "%s(%s)" %% (%s)' % \
 | 
					 | 
				
			||||||
                  (self.name, fmt, vals)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            print >> buf, '        return "%s()"' % self.name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rx_init = re.compile('init\((.*)\):')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def parse_spec(file):
 | 
					 | 
				
			||||||
    classes = {}
 | 
					 | 
				
			||||||
    cur = None
 | 
					 | 
				
			||||||
    for line in fileinput.input(file):
 | 
					 | 
				
			||||||
        if line.strip().startswith('#'):
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        mo = rx_init.search(line)
 | 
					 | 
				
			||||||
        if mo is None:
 | 
					 | 
				
			||||||
            if cur is None:
 | 
					 | 
				
			||||||
                # a normal entry
 | 
					 | 
				
			||||||
                try:
 | 
					 | 
				
			||||||
                    name, args = line.split(':')
 | 
					 | 
				
			||||||
                except ValueError:
 | 
					 | 
				
			||||||
                    continue
 | 
					 | 
				
			||||||
                classes[name] = NodeInfo(name, args)
 | 
					 | 
				
			||||||
                cur = None
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                # some code for the __init__ method
 | 
					 | 
				
			||||||
                cur.init.append(line)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            # some extra code for a Node's __init__ method
 | 
					 | 
				
			||||||
            name = mo.group(1)
 | 
					 | 
				
			||||||
            cur = classes[name]
 | 
					 | 
				
			||||||
    return classes.values()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def main():
 | 
					 | 
				
			||||||
    prologue, epilogue = load_boilerplate(sys.argv[-1])
 | 
					 | 
				
			||||||
    print prologue
 | 
					 | 
				
			||||||
    print
 | 
					 | 
				
			||||||
    classes = parse_spec(SPEC)
 | 
					 | 
				
			||||||
    for info in classes:
 | 
					 | 
				
			||||||
        print info.gen_source()
 | 
					 | 
				
			||||||
    print epilogue
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == "__main__":
 | 
					 | 
				
			||||||
    main()
 | 
					 | 
				
			||||||
    sys.exit(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### PROLOGUE
 | 
					 | 
				
			||||||
"""Python abstract syntax node definitions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This file is automatically generated.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
from types import TupleType, ListType
 | 
					 | 
				
			||||||
from consts import CO_VARARGS, CO_VARKEYWORDS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def flatten(list):
 | 
					 | 
				
			||||||
    l = []
 | 
					 | 
				
			||||||
    for elt in list:
 | 
					 | 
				
			||||||
        t = type(elt)
 | 
					 | 
				
			||||||
        if t is TupleType or t is ListType:
 | 
					 | 
				
			||||||
            for elt2 in flatten(elt):
 | 
					 | 
				
			||||||
                l.append(elt2)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            l.append(elt)
 | 
					 | 
				
			||||||
    return l
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def flatten_nodes(list):
 | 
					 | 
				
			||||||
    return [n for n in flatten(list) if isinstance(n, Node)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def asList(nodes):
 | 
					 | 
				
			||||||
    l = []
 | 
					 | 
				
			||||||
    for item in nodes:
 | 
					 | 
				
			||||||
        if hasattr(item, "asList"):
 | 
					 | 
				
			||||||
            l.append(item.asList())
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            t = type(item)
 | 
					 | 
				
			||||||
            if t is TupleType or t is ListType:
 | 
					 | 
				
			||||||
                l.append(tuple(asList(item)))
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                l.append(item)
 | 
					 | 
				
			||||||
    return l
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
nodes = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Node: # an abstract base class
 | 
					 | 
				
			||||||
    lineno = None # provide a lineno for nodes that don't have one
 | 
					 | 
				
			||||||
    def getType(self):
 | 
					 | 
				
			||||||
        pass # implemented by subclass
 | 
					 | 
				
			||||||
    def getChildren(self):
 | 
					 | 
				
			||||||
        pass # implemented by subclasses
 | 
					 | 
				
			||||||
    def asList(self):
 | 
					 | 
				
			||||||
        return tuple(asList(self.getChildren()))
 | 
					 | 
				
			||||||
    def getChildNodes(self):
 | 
					 | 
				
			||||||
        pass # implemented by subclasses
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class EmptyNode(Node):
 | 
					 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### EPILOGUE
 | 
					 | 
				
			||||||
klasses = globals()
 | 
					 | 
				
			||||||
for k in nodes.keys():
 | 
					 | 
				
			||||||
    nodes[k] = klasses[nodes[k]]
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,19 +0,0 @@
 | 
				
			||||||
# operation flags
 | 
					 | 
				
			||||||
OP_ASSIGN = 'OP_ASSIGN'
 | 
					 | 
				
			||||||
OP_DELETE = 'OP_DELETE'
 | 
					 | 
				
			||||||
OP_APPLY = 'OP_APPLY'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SC_LOCAL = 1
 | 
					 | 
				
			||||||
SC_GLOBAL = 2
 | 
					 | 
				
			||||||
SC_FREE = 3
 | 
					 | 
				
			||||||
SC_CELL = 4
 | 
					 | 
				
			||||||
SC_UNKNOWN = 5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CO_OPTIMIZED = 0x0001
 | 
					 | 
				
			||||||
CO_NEWLOCALS = 0x0002
 | 
					 | 
				
			||||||
CO_VARARGS = 0x0004
 | 
					 | 
				
			||||||
CO_VARKEYWORDS = 0x0008
 | 
					 | 
				
			||||||
CO_NESTED = 0x0010
 | 
					 | 
				
			||||||
CO_GENERATOR = 0x0020
 | 
					 | 
				
			||||||
CO_GENERATOR_ALLOWED = 0x1000
 | 
					 | 
				
			||||||
CO_FUTURE_DIVISION = 0x2000
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,73 +0,0 @@
 | 
				
			||||||
"""Parser for future statements
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from compiler import ast, walk
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def is_future(stmt):
 | 
					 | 
				
			||||||
    """Return true if statement is a well-formed future statement"""
 | 
					 | 
				
			||||||
    if not isinstance(stmt, ast.From):
 | 
					 | 
				
			||||||
        return 0
 | 
					 | 
				
			||||||
    if stmt.modname == "__future__":
 | 
					 | 
				
			||||||
        return 1
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
        return 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FutureParser:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    features = ("nested_scopes", "generators", "division")
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.found = {} # set
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitModule(self, node):
 | 
					 | 
				
			||||||
        stmt = node.node
 | 
					 | 
				
			||||||
        for s in stmt.nodes:
 | 
					 | 
				
			||||||
            if not self.check_stmt(s):
 | 
					 | 
				
			||||||
                break
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_stmt(self, stmt):
 | 
					 | 
				
			||||||
        if is_future(stmt):
 | 
					 | 
				
			||||||
            for name, asname in stmt.names:
 | 
					 | 
				
			||||||
                if name in self.features:
 | 
					 | 
				
			||||||
                    self.found[name] = 1
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    raise SyntaxError, \
 | 
					 | 
				
			||||||
                          "future feature %s is not defined" % name
 | 
					 | 
				
			||||||
            stmt.valid_future = 1
 | 
					 | 
				
			||||||
            return 1
 | 
					 | 
				
			||||||
        return 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_features(self):
 | 
					 | 
				
			||||||
        """Return list of features enabled by future statements"""
 | 
					 | 
				
			||||||
        return self.found.keys()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class BadFutureParser:
 | 
					 | 
				
			||||||
    """Check for invalid future statements"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitFrom(self, node):
 | 
					 | 
				
			||||||
        if hasattr(node, 'valid_future'):
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        if node.modname != "__future__":
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        raise SyntaxError, "invalid future statement"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def find_futures(node):
 | 
					 | 
				
			||||||
    p1 = FutureParser()
 | 
					 | 
				
			||||||
    p2 = BadFutureParser()
 | 
					 | 
				
			||||||
    walk(node, p1)
 | 
					 | 
				
			||||||
    walk(node, p2)
 | 
					 | 
				
			||||||
    return p1.get_features()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == "__main__":
 | 
					 | 
				
			||||||
    import sys
 | 
					 | 
				
			||||||
    from compiler import parseFile, walk
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for file in sys.argv[1:]:
 | 
					 | 
				
			||||||
        print file
 | 
					 | 
				
			||||||
        tree = parseFile(file)
 | 
					 | 
				
			||||||
        v = FutureParser()
 | 
					 | 
				
			||||||
        walk(tree, v)
 | 
					 | 
				
			||||||
        print v.found
 | 
					 | 
				
			||||||
        print
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,75 +0,0 @@
 | 
				
			||||||
import types
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def flatten(tup):
 | 
					 | 
				
			||||||
    elts = []
 | 
					 | 
				
			||||||
    for elt in tup:
 | 
					 | 
				
			||||||
        if type(elt) == types.TupleType:
 | 
					 | 
				
			||||||
            elts = elts + flatten(elt)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            elts.append(elt)
 | 
					 | 
				
			||||||
    return elts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Set:
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.elts = {}
 | 
					 | 
				
			||||||
    def __len__(self):
 | 
					 | 
				
			||||||
        return len(self.elts)
 | 
					 | 
				
			||||||
    def __contains__(self, elt):
 | 
					 | 
				
			||||||
        return self.elts.has_key(elt)
 | 
					 | 
				
			||||||
    def add(self, elt):
 | 
					 | 
				
			||||||
        self.elts[elt] = elt
 | 
					 | 
				
			||||||
    def elements(self):
 | 
					 | 
				
			||||||
        return self.elts.keys()
 | 
					 | 
				
			||||||
    def has_elt(self, elt):
 | 
					 | 
				
			||||||
        return self.elts.has_key(elt)
 | 
					 | 
				
			||||||
    def remove(self, elt):
 | 
					 | 
				
			||||||
        del self.elts[elt]
 | 
					 | 
				
			||||||
    def copy(self):
 | 
					 | 
				
			||||||
        c = Set()
 | 
					 | 
				
			||||||
        c.elts.update(self.elts)
 | 
					 | 
				
			||||||
        return c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Stack:
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.stack = []
 | 
					 | 
				
			||||||
        self.pop = self.stack.pop
 | 
					 | 
				
			||||||
    def __len__(self):
 | 
					 | 
				
			||||||
        return len(self.stack)
 | 
					 | 
				
			||||||
    def push(self, elt):
 | 
					 | 
				
			||||||
        self.stack.append(elt)
 | 
					 | 
				
			||||||
    def top(self):
 | 
					 | 
				
			||||||
        return self.stack[-1]
 | 
					 | 
				
			||||||
    def __getitem__(self, index): # needed by visitContinue()
 | 
					 | 
				
			||||||
        return self.stack[index]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
MANGLE_LEN = 256 # magic constant from compile.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def mangle(name, klass):
 | 
					 | 
				
			||||||
    if not name.startswith('__'):
 | 
					 | 
				
			||||||
        return name
 | 
					 | 
				
			||||||
    if len(name) + 2 >= MANGLE_LEN:
 | 
					 | 
				
			||||||
        return name
 | 
					 | 
				
			||||||
    if name.endswith('__'):
 | 
					 | 
				
			||||||
        return name
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        i = 0
 | 
					 | 
				
			||||||
        while klass[i] == '_':
 | 
					 | 
				
			||||||
            i = i + 1
 | 
					 | 
				
			||||||
    except IndexError:
 | 
					 | 
				
			||||||
        return name
 | 
					 | 
				
			||||||
    klass = klass[i:]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    tlen = len(klass) + len(name)
 | 
					 | 
				
			||||||
    if tlen > MANGLE_LEN:
 | 
					 | 
				
			||||||
        klass = klass[:MANGLE_LEN-tlen]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return "_%s%s" % (klass, name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def set_filename(filename, tree):
 | 
					 | 
				
			||||||
    """Set the filename attribute to filename on every node in tree"""
 | 
					 | 
				
			||||||
    worklist = [tree]
 | 
					 | 
				
			||||||
    while worklist:
 | 
					 | 
				
			||||||
        node = worklist.pop(0)
 | 
					 | 
				
			||||||
        node.filename = filename
 | 
					 | 
				
			||||||
        worklist.extend(node.getChildNodes())
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,791 +0,0 @@
 | 
				
			||||||
"""A flow graph representation for Python bytecode"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dis
 | 
					 | 
				
			||||||
import new
 | 
					 | 
				
			||||||
import string
 | 
					 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
import types
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from compiler import misc
 | 
					 | 
				
			||||||
from compiler.consts import CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, \
 | 
					 | 
				
			||||||
     CO_VARKEYWORDS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def xxx_sort(l):
 | 
					 | 
				
			||||||
    l = l[:]
 | 
					 | 
				
			||||||
    def sorter(a, b):
 | 
					 | 
				
			||||||
        return cmp(a.bid, b.bid)
 | 
					 | 
				
			||||||
    l.sort(sorter)
 | 
					 | 
				
			||||||
    return l
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FlowGraph:
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.current = self.entry = Block()
 | 
					 | 
				
			||||||
        self.exit = Block("exit")
 | 
					 | 
				
			||||||
        self.blocks = misc.Set()
 | 
					 | 
				
			||||||
        self.blocks.add(self.entry)
 | 
					 | 
				
			||||||
        self.blocks.add(self.exit)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def startBlock(self, block):
 | 
					 | 
				
			||||||
        if self._debug:
 | 
					 | 
				
			||||||
            if self.current:
 | 
					 | 
				
			||||||
                print "end", repr(self.current)
 | 
					 | 
				
			||||||
                print "    next", self.current.next
 | 
					 | 
				
			||||||
                print "   ", self.current.get_children()
 | 
					 | 
				
			||||||
            print repr(block)
 | 
					 | 
				
			||||||
        self.current = block
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def nextBlock(self, block=None):
 | 
					 | 
				
			||||||
        # XXX think we need to specify when there is implicit transfer
 | 
					 | 
				
			||||||
        # from one block to the next.  might be better to represent this
 | 
					 | 
				
			||||||
        # with explicit JUMP_ABSOLUTE instructions that are optimized
 | 
					 | 
				
			||||||
        # out when they are unnecessary.
 | 
					 | 
				
			||||||
        #
 | 
					 | 
				
			||||||
        # I think this strategy works: each block has a child
 | 
					 | 
				
			||||||
        # designated as "next" which is returned as the last of the
 | 
					 | 
				
			||||||
        # children.  because the nodes in a graph are emitted in
 | 
					 | 
				
			||||||
        # reverse post order, the "next" block will always be emitted
 | 
					 | 
				
			||||||
        # immediately after its parent.
 | 
					 | 
				
			||||||
        # Worry: maintaining this invariant could be tricky
 | 
					 | 
				
			||||||
        if block is None:
 | 
					 | 
				
			||||||
            block = self.newBlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Note: If the current block ends with an unconditional
 | 
					 | 
				
			||||||
        # control transfer, then it is incorrect to add an implicit
 | 
					 | 
				
			||||||
        # transfer to the block graph.  The current code requires
 | 
					 | 
				
			||||||
        # these edges to get the blocks emitted in the right order,
 | 
					 | 
				
			||||||
        # however. :-(  If a client needs to remove these edges, call
 | 
					 | 
				
			||||||
        # pruneEdges().
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        self.current.addNext(block)
 | 
					 | 
				
			||||||
        self.startBlock(block)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def newBlock(self):
 | 
					 | 
				
			||||||
        b = Block()
 | 
					 | 
				
			||||||
        self.blocks.add(b)
 | 
					 | 
				
			||||||
        return b
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def startExitBlock(self):
 | 
					 | 
				
			||||||
        self.startBlock(self.exit)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _debug = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _enable_debug(self):
 | 
					 | 
				
			||||||
        self._debug = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _disable_debug(self):
 | 
					 | 
				
			||||||
        self._debug = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def emit(self, *inst):
 | 
					 | 
				
			||||||
        if self._debug:
 | 
					 | 
				
			||||||
            print "\t", inst
 | 
					 | 
				
			||||||
        if inst[0] == 'RETURN_VALUE':
 | 
					 | 
				
			||||||
            self.current.addOutEdge(self.exit)
 | 
					 | 
				
			||||||
        if len(inst) == 2 and isinstance(inst[1], Block):
 | 
					 | 
				
			||||||
            self.current.addOutEdge(inst[1])
 | 
					 | 
				
			||||||
        self.current.emit(inst)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getBlocksInOrder(self):
 | 
					 | 
				
			||||||
        """Return the blocks in reverse postorder
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        i.e. each node appears before all of its successors
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        # XXX make sure every node that doesn't have an explicit next
 | 
					 | 
				
			||||||
        # is set so that next points to exit
 | 
					 | 
				
			||||||
        for b in self.blocks.elements():
 | 
					 | 
				
			||||||
            if b is self.exit:
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            if not b.next:
 | 
					 | 
				
			||||||
                b.addNext(self.exit)
 | 
					 | 
				
			||||||
        order = dfs_postorder(self.entry, {})
 | 
					 | 
				
			||||||
        order.reverse()
 | 
					 | 
				
			||||||
        self.fixupOrder(order, self.exit)
 | 
					 | 
				
			||||||
        # hack alert
 | 
					 | 
				
			||||||
        if not self.exit in order:
 | 
					 | 
				
			||||||
            order.append(self.exit)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return order
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def fixupOrder(self, blocks, default_next):
 | 
					 | 
				
			||||||
        """Fixup bad order introduced by DFS."""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # XXX This is a total mess.  There must be a better way to get
 | 
					 | 
				
			||||||
        # the code blocks in the right order.
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        self.fixupOrderHonorNext(blocks, default_next)
 | 
					 | 
				
			||||||
        self.fixupOrderForward(blocks, default_next)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def fixupOrderHonorNext(self, blocks, default_next):
 | 
					 | 
				
			||||||
        """Fix one problem with DFS.
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        The DFS uses child block, but doesn't know about the special
 | 
					 | 
				
			||||||
        "next" block.  As a result, the DFS can order blocks so that a
 | 
					 | 
				
			||||||
        block isn't next to the right block for implicit control
 | 
					 | 
				
			||||||
        transfers.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        index = {}
 | 
					 | 
				
			||||||
        for i in range(len(blocks)):
 | 
					 | 
				
			||||||
            index[blocks[i]] = i
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for i in range(0, len(blocks) - 1):
 | 
					 | 
				
			||||||
            b = blocks[i]
 | 
					 | 
				
			||||||
            n = blocks[i + 1]
 | 
					 | 
				
			||||||
            if not b.next or b.next[0] == default_next or b.next[0] == n:
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            # The blocks are in the wrong order.  Find the chain of
 | 
					 | 
				
			||||||
            # blocks to insert where they belong.
 | 
					 | 
				
			||||||
            cur = b
 | 
					 | 
				
			||||||
            chain = []
 | 
					 | 
				
			||||||
            elt = cur
 | 
					 | 
				
			||||||
            while elt.next and elt.next[0] != default_next:
 | 
					 | 
				
			||||||
                chain.append(elt.next[0])
 | 
					 | 
				
			||||||
                elt = elt.next[0]
 | 
					 | 
				
			||||||
            # Now remove the blocks in the chain from the current
 | 
					 | 
				
			||||||
            # block list, so that they can be re-inserted.
 | 
					 | 
				
			||||||
            l = []
 | 
					 | 
				
			||||||
            for b in chain:
 | 
					 | 
				
			||||||
                assert index[b] > i
 | 
					 | 
				
			||||||
                l.append((index[b], b))
 | 
					 | 
				
			||||||
            l.sort()
 | 
					 | 
				
			||||||
            l.reverse()
 | 
					 | 
				
			||||||
            for j, b in l:
 | 
					 | 
				
			||||||
                del blocks[index[b]]
 | 
					 | 
				
			||||||
            # Insert the chain in the proper location
 | 
					 | 
				
			||||||
            blocks[i:i + 1] = [cur] + chain
 | 
					 | 
				
			||||||
            # Finally, re-compute the block indexes
 | 
					 | 
				
			||||||
            for i in range(len(blocks)):
 | 
					 | 
				
			||||||
                index[blocks[i]] = i
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def fixupOrderForward(self, blocks, default_next):
 | 
					 | 
				
			||||||
        """Make sure all JUMP_FORWARDs jump forward"""
 | 
					 | 
				
			||||||
        index = {}
 | 
					 | 
				
			||||||
        chains = []
 | 
					 | 
				
			||||||
        cur = []
 | 
					 | 
				
			||||||
        for b in blocks:
 | 
					 | 
				
			||||||
            index[b] = len(chains)
 | 
					 | 
				
			||||||
            cur.append(b)
 | 
					 | 
				
			||||||
            if b.next and b.next[0] == default_next:
 | 
					 | 
				
			||||||
                chains.append(cur)
 | 
					 | 
				
			||||||
                cur = []
 | 
					 | 
				
			||||||
        chains.append(cur)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while 1:
 | 
					 | 
				
			||||||
            constraints = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for i in range(len(chains)):
 | 
					 | 
				
			||||||
                l = chains[i]
 | 
					 | 
				
			||||||
                for b in l:
 | 
					 | 
				
			||||||
                    for c in b.get_children():
 | 
					 | 
				
			||||||
                        if index[c] < i:
 | 
					 | 
				
			||||||
                            forward_p = 0
 | 
					 | 
				
			||||||
                            for inst in b.insts:
 | 
					 | 
				
			||||||
                                if inst[0] == 'JUMP_FORWARD':
 | 
					 | 
				
			||||||
                                    if inst[1] == c:
 | 
					 | 
				
			||||||
                                        forward_p = 1
 | 
					 | 
				
			||||||
                            if not forward_p:
 | 
					 | 
				
			||||||
                                continue
 | 
					 | 
				
			||||||
                            constraints.append((index[c], i))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if not constraints:
 | 
					 | 
				
			||||||
                break
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # XXX just do one for now
 | 
					 | 
				
			||||||
            # do swaps to get things in the right order
 | 
					 | 
				
			||||||
            goes_before, a_chain = constraints[0]
 | 
					 | 
				
			||||||
            assert a_chain > goes_before
 | 
					 | 
				
			||||||
            c = chains[a_chain]
 | 
					 | 
				
			||||||
            chains.remove(c)
 | 
					 | 
				
			||||||
            chains.insert(goes_before, c)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        del blocks[:]
 | 
					 | 
				
			||||||
        for c in chains:
 | 
					 | 
				
			||||||
            for b in c:
 | 
					 | 
				
			||||||
                blocks.append(b)
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
    def getBlocks(self):
 | 
					 | 
				
			||||||
        return self.blocks.elements()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getRoot(self):
 | 
					 | 
				
			||||||
        """Return nodes appropriate for use with dominator"""
 | 
					 | 
				
			||||||
        return self.entry
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    def getContainedGraphs(self):
 | 
					 | 
				
			||||||
        l = []
 | 
					 | 
				
			||||||
        for b in self.getBlocks():
 | 
					 | 
				
			||||||
            l.extend(b.getContainedGraphs())
 | 
					 | 
				
			||||||
        return l
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def dfs_postorder(b, seen):
 | 
					 | 
				
			||||||
    """Depth-first search of tree rooted at b, return in postorder"""
 | 
					 | 
				
			||||||
    order = []
 | 
					 | 
				
			||||||
    seen[b] = b
 | 
					 | 
				
			||||||
    for c in b.get_children():
 | 
					 | 
				
			||||||
        if seen.has_key(c):
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        order = order + dfs_postorder(c, seen)
 | 
					 | 
				
			||||||
    order.append(b)
 | 
					 | 
				
			||||||
    return order
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Block:
 | 
					 | 
				
			||||||
    _count = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, label=''):
 | 
					 | 
				
			||||||
        self.insts = []
 | 
					 | 
				
			||||||
        self.inEdges = misc.Set()
 | 
					 | 
				
			||||||
        self.outEdges = misc.Set()
 | 
					 | 
				
			||||||
        self.label = label
 | 
					 | 
				
			||||||
        self.bid = Block._count
 | 
					 | 
				
			||||||
        self.next = []
 | 
					 | 
				
			||||||
        Block._count = Block._count + 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __repr__(self):
 | 
					 | 
				
			||||||
        if self.label:
 | 
					 | 
				
			||||||
            return "<block %s id=%d>" % (self.label, self.bid)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            return "<block id=%d>" % (self.bid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __str__(self):
 | 
					 | 
				
			||||||
        insts = map(str, self.insts)
 | 
					 | 
				
			||||||
        return "<block %s %d:\n%s>" % (self.label, self.bid,
 | 
					 | 
				
			||||||
                                       string.join(insts, '\n')) 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def emit(self, inst):
 | 
					 | 
				
			||||||
        op = inst[0]
 | 
					 | 
				
			||||||
        if op[:4] == 'JUMP':
 | 
					 | 
				
			||||||
            self.outEdges.add(inst[1])
 | 
					 | 
				
			||||||
        self.insts.append(inst)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getInstructions(self):
 | 
					 | 
				
			||||||
        return self.insts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def addInEdge(self, block):
 | 
					 | 
				
			||||||
        self.inEdges.add(block)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def addOutEdge(self, block):
 | 
					 | 
				
			||||||
        self.outEdges.add(block)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def addNext(self, block):
 | 
					 | 
				
			||||||
        self.next.append(block)
 | 
					 | 
				
			||||||
        assert len(self.next) == 1, map(str, self.next)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS',
 | 
					 | 
				
			||||||
                        'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def pruneNext(self):
 | 
					 | 
				
			||||||
        """Remove bogus edge for unconditional transfers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Each block has a next edge that accounts for implicit control
 | 
					 | 
				
			||||||
        transfers, e.g. from a JUMP_IF_FALSE to the block that will be
 | 
					 | 
				
			||||||
        executed if the test is true.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        These edges must remain for the current assembler code to
 | 
					 | 
				
			||||||
        work. If they are removed, the dfs_postorder gets things in
 | 
					 | 
				
			||||||
        weird orders.  However, they shouldn't be there for other
 | 
					 | 
				
			||||||
        purposes, e.g. conversion to SSA form.  This method will
 | 
					 | 
				
			||||||
        remove the next edge when it follows an unconditional control
 | 
					 | 
				
			||||||
        transfer.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            op, arg = self.insts[-1]
 | 
					 | 
				
			||||||
        except (IndexError, ValueError):
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        if op in self._uncond_transfer:
 | 
					 | 
				
			||||||
            self.next = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_children(self):
 | 
					 | 
				
			||||||
        if self.next and self.next[0] in self.outEdges:
 | 
					 | 
				
			||||||
            self.outEdges.remove(self.next[0])
 | 
					 | 
				
			||||||
        return self.outEdges.elements() + self.next
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getContainedGraphs(self):
 | 
					 | 
				
			||||||
        """Return all graphs contained within this block.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        For example, a MAKE_FUNCTION block will contain a reference to
 | 
					 | 
				
			||||||
        the graph for the function body.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        contained = []
 | 
					 | 
				
			||||||
        for inst in self.insts:
 | 
					 | 
				
			||||||
            if len(inst) == 1:
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            op = inst[1]
 | 
					 | 
				
			||||||
            if hasattr(op, 'graph'):
 | 
					 | 
				
			||||||
                contained.append(op.graph)
 | 
					 | 
				
			||||||
        return contained
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# flags for code objects
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# the FlowGraph is transformed in place; it exists in one of these states
 | 
					 | 
				
			||||||
RAW = "RAW"
 | 
					 | 
				
			||||||
FLAT = "FLAT"
 | 
					 | 
				
			||||||
CONV = "CONV"
 | 
					 | 
				
			||||||
DONE = "DONE"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PyFlowGraph(FlowGraph):
 | 
					 | 
				
			||||||
    super_init = FlowGraph.__init__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, name, filename, args=(), optimized=0, klass=None):
 | 
					 | 
				
			||||||
        self.super_init()
 | 
					 | 
				
			||||||
        self.name = name
 | 
					 | 
				
			||||||
        self.filename = filename
 | 
					 | 
				
			||||||
        self.docstring = None
 | 
					 | 
				
			||||||
        self.args = args # XXX
 | 
					 | 
				
			||||||
        self.argcount = getArgCount(args)
 | 
					 | 
				
			||||||
        self.klass = klass
 | 
					 | 
				
			||||||
        if optimized:
 | 
					 | 
				
			||||||
            self.flags = CO_OPTIMIZED | CO_NEWLOCALS 
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            self.flags = 0
 | 
					 | 
				
			||||||
        self.consts = []
 | 
					 | 
				
			||||||
        self.names = []
 | 
					 | 
				
			||||||
        # Free variables found by the symbol table scan, including
 | 
					 | 
				
			||||||
        # variables used only in nested scopes, are included here.
 | 
					 | 
				
			||||||
        self.freevars = []
 | 
					 | 
				
			||||||
        self.cellvars = []
 | 
					 | 
				
			||||||
        # The closure list is used to track the order of cell
 | 
					 | 
				
			||||||
        # variables and free variables in the resulting code object.
 | 
					 | 
				
			||||||
        # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
 | 
					 | 
				
			||||||
        # kinds of variables.
 | 
					 | 
				
			||||||
        self.closure = []
 | 
					 | 
				
			||||||
        self.varnames = list(args) or []
 | 
					 | 
				
			||||||
        for i in range(len(self.varnames)):
 | 
					 | 
				
			||||||
            var = self.varnames[i]
 | 
					 | 
				
			||||||
            if isinstance(var, TupleArg):
 | 
					 | 
				
			||||||
                self.varnames[i] = var.getName()
 | 
					 | 
				
			||||||
        self.stage = RAW
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setDocstring(self, doc):
 | 
					 | 
				
			||||||
        self.docstring = doc
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setFlag(self, flag):
 | 
					 | 
				
			||||||
        self.flags = self.flags | flag
 | 
					 | 
				
			||||||
        if flag == CO_VARARGS:
 | 
					 | 
				
			||||||
            self.argcount = self.argcount - 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def checkFlag(self, flag):
 | 
					 | 
				
			||||||
        if self.flags & flag:
 | 
					 | 
				
			||||||
            return 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setFreeVars(self, names):
 | 
					 | 
				
			||||||
        self.freevars = list(names)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setCellVars(self, names):
 | 
					 | 
				
			||||||
        self.cellvars = names
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getCode(self):
 | 
					 | 
				
			||||||
        """Get a Python code object"""
 | 
					 | 
				
			||||||
        if self.stage == RAW:
 | 
					 | 
				
			||||||
            self.flattenGraph()
 | 
					 | 
				
			||||||
        if self.stage == FLAT:
 | 
					 | 
				
			||||||
            self.convertArgs()
 | 
					 | 
				
			||||||
        if self.stage == CONV:
 | 
					 | 
				
			||||||
            self.makeByteCode()
 | 
					 | 
				
			||||||
        if self.stage == DONE:
 | 
					 | 
				
			||||||
            return self.newCodeObject()
 | 
					 | 
				
			||||||
        raise RuntimeError, "inconsistent PyFlowGraph state"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def dump(self, io=None):
 | 
					 | 
				
			||||||
        if io:
 | 
					 | 
				
			||||||
            save = sys.stdout
 | 
					 | 
				
			||||||
            sys.stdout = io
 | 
					 | 
				
			||||||
        pc = 0
 | 
					 | 
				
			||||||
        for t in self.insts:
 | 
					 | 
				
			||||||
            opname = t[0]
 | 
					 | 
				
			||||||
            if opname == "SET_LINENO":
 | 
					 | 
				
			||||||
                print
 | 
					 | 
				
			||||||
            if len(t) == 1:
 | 
					 | 
				
			||||||
                print "\t", "%3d" % pc, opname
 | 
					 | 
				
			||||||
                pc = pc + 1
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                print "\t", "%3d" % pc, opname, t[1]
 | 
					 | 
				
			||||||
                pc = pc + 3
 | 
					 | 
				
			||||||
        if io:
 | 
					 | 
				
			||||||
            sys.stdout = save
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def flattenGraph(self):
 | 
					 | 
				
			||||||
        """Arrange the blocks in order and resolve jumps"""
 | 
					 | 
				
			||||||
        assert self.stage == RAW
 | 
					 | 
				
			||||||
        self.insts = insts = []
 | 
					 | 
				
			||||||
        pc = 0
 | 
					 | 
				
			||||||
        begin = {}
 | 
					 | 
				
			||||||
        end = {}
 | 
					 | 
				
			||||||
        for b in self.getBlocksInOrder():
 | 
					 | 
				
			||||||
            begin[b] = pc
 | 
					 | 
				
			||||||
            for inst in b.getInstructions():
 | 
					 | 
				
			||||||
                insts.append(inst)
 | 
					 | 
				
			||||||
                if len(inst) == 1:
 | 
					 | 
				
			||||||
                    pc = pc + 1
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    # arg takes 2 bytes
 | 
					 | 
				
			||||||
                    pc = pc + 3
 | 
					 | 
				
			||||||
            end[b] = pc
 | 
					 | 
				
			||||||
        pc = 0
 | 
					 | 
				
			||||||
        for i in range(len(insts)):
 | 
					 | 
				
			||||||
            inst = insts[i]
 | 
					 | 
				
			||||||
            if len(inst) == 1:
 | 
					 | 
				
			||||||
                pc = pc + 1
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                pc = pc + 3
 | 
					 | 
				
			||||||
            opname = inst[0]
 | 
					 | 
				
			||||||
            if self.hasjrel.has_elt(opname):
 | 
					 | 
				
			||||||
                oparg = inst[1]
 | 
					 | 
				
			||||||
                offset = begin[oparg] - pc
 | 
					 | 
				
			||||||
                insts[i] = opname, offset
 | 
					 | 
				
			||||||
            elif self.hasjabs.has_elt(opname):
 | 
					 | 
				
			||||||
                insts[i] = opname, begin[inst[1]]
 | 
					 | 
				
			||||||
        self.stacksize = findDepth(self.insts)
 | 
					 | 
				
			||||||
        self.stage = FLAT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    hasjrel = misc.Set()
 | 
					 | 
				
			||||||
    for i in dis.hasjrel:
 | 
					 | 
				
			||||||
        hasjrel.add(dis.opname[i])
 | 
					 | 
				
			||||||
    hasjabs = misc.Set()
 | 
					 | 
				
			||||||
    for i in dis.hasjabs:
 | 
					 | 
				
			||||||
        hasjabs.add(dis.opname[i])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def convertArgs(self):
 | 
					 | 
				
			||||||
        """Convert arguments from symbolic to concrete form"""
 | 
					 | 
				
			||||||
        assert self.stage == FLAT
 | 
					 | 
				
			||||||
        self.consts.insert(0, self.docstring)
 | 
					 | 
				
			||||||
        self.sort_cellvars()
 | 
					 | 
				
			||||||
        for i in range(len(self.insts)):
 | 
					 | 
				
			||||||
            t = self.insts[i]
 | 
					 | 
				
			||||||
            if len(t) == 2:
 | 
					 | 
				
			||||||
                opname, oparg = t
 | 
					 | 
				
			||||||
                conv = self._converters.get(opname, None)
 | 
					 | 
				
			||||||
                if conv:
 | 
					 | 
				
			||||||
                    self.insts[i] = opname, conv(self, oparg)
 | 
					 | 
				
			||||||
        self.stage = CONV
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def sort_cellvars(self):
 | 
					 | 
				
			||||||
        """Sort cellvars in the order of varnames and prune from freevars.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        cells = {}
 | 
					 | 
				
			||||||
        for name in self.cellvars:
 | 
					 | 
				
			||||||
            cells[name] = 1
 | 
					 | 
				
			||||||
        self.cellvars = [name for name in self.varnames
 | 
					 | 
				
			||||||
                         if cells.has_key(name)]
 | 
					 | 
				
			||||||
        for name in self.cellvars:
 | 
					 | 
				
			||||||
            del cells[name]
 | 
					 | 
				
			||||||
        self.cellvars = self.cellvars + cells.keys()
 | 
					 | 
				
			||||||
        self.closure = self.cellvars + self.freevars
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _lookupName(self, name, list):
 | 
					 | 
				
			||||||
        """Return index of name in list, appending if necessary
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        This routine uses a list instead of a dictionary, because a
 | 
					 | 
				
			||||||
        dictionary can't store two different keys if the keys have the
 | 
					 | 
				
			||||||
        same value but different types, e.g. 2 and 2L.  The compiler
 | 
					 | 
				
			||||||
        must treat these two separately, so it does an explicit type
 | 
					 | 
				
			||||||
        comparison before comparing the values.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        t = type(name)
 | 
					 | 
				
			||||||
        for i in range(len(list)):
 | 
					 | 
				
			||||||
            if t == type(list[i]) and list[i] == name:
 | 
					 | 
				
			||||||
                return i
 | 
					 | 
				
			||||||
        end = len(list)
 | 
					 | 
				
			||||||
        list.append(name)
 | 
					 | 
				
			||||||
        return end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _converters = {}
 | 
					 | 
				
			||||||
    def _convert_LOAD_CONST(self, arg):
 | 
					 | 
				
			||||||
        if hasattr(arg, 'getCode'):
 | 
					 | 
				
			||||||
            arg = arg.getCode()
 | 
					 | 
				
			||||||
        return self._lookupName(arg, self.consts)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _convert_LOAD_FAST(self, arg):
 | 
					 | 
				
			||||||
        self._lookupName(arg, self.names)
 | 
					 | 
				
			||||||
        return self._lookupName(arg, self.varnames)
 | 
					 | 
				
			||||||
    _convert_STORE_FAST = _convert_LOAD_FAST
 | 
					 | 
				
			||||||
    _convert_DELETE_FAST = _convert_LOAD_FAST
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _convert_LOAD_NAME(self, arg):
 | 
					 | 
				
			||||||
        if self.klass is None:
 | 
					 | 
				
			||||||
            self._lookupName(arg, self.varnames)
 | 
					 | 
				
			||||||
        return self._lookupName(arg, self.names)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _convert_NAME(self, arg):
 | 
					 | 
				
			||||||
        if self.klass is None:
 | 
					 | 
				
			||||||
            self._lookupName(arg, self.varnames)
 | 
					 | 
				
			||||||
        return self._lookupName(arg, self.names)
 | 
					 | 
				
			||||||
    _convert_STORE_NAME = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_DELETE_NAME = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_IMPORT_NAME = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_IMPORT_FROM = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_STORE_ATTR = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_LOAD_ATTR = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_DELETE_ATTR = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_LOAD_GLOBAL = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_STORE_GLOBAL = _convert_NAME
 | 
					 | 
				
			||||||
    _convert_DELETE_GLOBAL = _convert_NAME
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _convert_DEREF(self, arg):
 | 
					 | 
				
			||||||
        self._lookupName(arg, self.names)
 | 
					 | 
				
			||||||
        self._lookupName(arg, self.varnames)
 | 
					 | 
				
			||||||
        return self._lookupName(arg, self.closure)
 | 
					 | 
				
			||||||
    _convert_LOAD_DEREF = _convert_DEREF
 | 
					 | 
				
			||||||
    _convert_STORE_DEREF = _convert_DEREF
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _convert_LOAD_CLOSURE(self, arg):
 | 
					 | 
				
			||||||
        self._lookupName(arg, self.varnames)
 | 
					 | 
				
			||||||
        return self._lookupName(arg, self.closure)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _cmp = list(dis.cmp_op)
 | 
					 | 
				
			||||||
    def _convert_COMPARE_OP(self, arg):
 | 
					 | 
				
			||||||
        return self._cmp.index(arg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # similarly for other opcodes...
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for name, obj in locals().items():
 | 
					 | 
				
			||||||
        if name[:9] == "_convert_":
 | 
					 | 
				
			||||||
            opname = name[9:]
 | 
					 | 
				
			||||||
            _converters[opname] = obj            
 | 
					 | 
				
			||||||
    del name, obj, opname
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def makeByteCode(self):
 | 
					 | 
				
			||||||
        assert self.stage == CONV
 | 
					 | 
				
			||||||
        self.lnotab = lnotab = LineAddrTable()
 | 
					 | 
				
			||||||
        for t in self.insts:
 | 
					 | 
				
			||||||
            opname = t[0]
 | 
					 | 
				
			||||||
            if len(t) == 1:
 | 
					 | 
				
			||||||
                lnotab.addCode(self.opnum[opname])
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                oparg = t[1]
 | 
					 | 
				
			||||||
                if opname == "SET_LINENO":
 | 
					 | 
				
			||||||
                    lnotab.nextLine(oparg)
 | 
					 | 
				
			||||||
                hi, lo = twobyte(oparg)
 | 
					 | 
				
			||||||
                try:
 | 
					 | 
				
			||||||
                    lnotab.addCode(self.opnum[opname], lo, hi)
 | 
					 | 
				
			||||||
                except ValueError:
 | 
					 | 
				
			||||||
                    print opname, oparg
 | 
					 | 
				
			||||||
                    print self.opnum[opname], lo, hi
 | 
					 | 
				
			||||||
                    raise
 | 
					 | 
				
			||||||
        self.stage = DONE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    opnum = {}
 | 
					 | 
				
			||||||
    for num in range(len(dis.opname)):
 | 
					 | 
				
			||||||
        opnum[dis.opname[num]] = num
 | 
					 | 
				
			||||||
    del num
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def newCodeObject(self):
 | 
					 | 
				
			||||||
        assert self.stage == DONE
 | 
					 | 
				
			||||||
        if (self.flags & CO_NEWLOCALS) == 0:
 | 
					 | 
				
			||||||
            nlocals = 0
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            nlocals = len(self.varnames)
 | 
					 | 
				
			||||||
        argcount = self.argcount
 | 
					 | 
				
			||||||
        if self.flags & CO_VARKEYWORDS:
 | 
					 | 
				
			||||||
            argcount = argcount - 1
 | 
					 | 
				
			||||||
        return new.code(argcount, nlocals, self.stacksize, self.flags,
 | 
					 | 
				
			||||||
                        self.lnotab.getCode(), self.getConsts(),
 | 
					 | 
				
			||||||
                        tuple(self.names), tuple(self.varnames),
 | 
					 | 
				
			||||||
                        self.filename, self.name, self.lnotab.firstline,
 | 
					 | 
				
			||||||
                        self.lnotab.getTable(), tuple(self.freevars),
 | 
					 | 
				
			||||||
                        tuple(self.cellvars))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getConsts(self):
 | 
					 | 
				
			||||||
        """Return a tuple for the const slot of the code object
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Must convert references to code (MAKE_FUNCTION) to code
 | 
					 | 
				
			||||||
        objects recursively.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        l = []
 | 
					 | 
				
			||||||
        for elt in self.consts:
 | 
					 | 
				
			||||||
            if isinstance(elt, PyFlowGraph):
 | 
					 | 
				
			||||||
                elt = elt.getCode()
 | 
					 | 
				
			||||||
            l.append(elt)
 | 
					 | 
				
			||||||
        return tuple(l)
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
def isJump(opname):
 | 
					 | 
				
			||||||
    if opname[:4] == 'JUMP':
 | 
					 | 
				
			||||||
        return 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TupleArg:
 | 
					 | 
				
			||||||
    """Helper for marking func defs with nested tuples in arglist"""
 | 
					 | 
				
			||||||
    def __init__(self, count, names):
 | 
					 | 
				
			||||||
        self.count = count
 | 
					 | 
				
			||||||
        self.names = names
 | 
					 | 
				
			||||||
    def __repr__(self):
 | 
					 | 
				
			||||||
        return "TupleArg(%s, %s)" % (self.count, self.names)
 | 
					 | 
				
			||||||
    def getName(self):
 | 
					 | 
				
			||||||
        return ".%d" % self.count
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def getArgCount(args):
 | 
					 | 
				
			||||||
    argcount = len(args)
 | 
					 | 
				
			||||||
    if args:
 | 
					 | 
				
			||||||
        for arg in args:
 | 
					 | 
				
			||||||
            if isinstance(arg, TupleArg):
 | 
					 | 
				
			||||||
                numNames = len(misc.flatten(arg.names))
 | 
					 | 
				
			||||||
                argcount = argcount - numNames
 | 
					 | 
				
			||||||
    return argcount
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def twobyte(val):
 | 
					 | 
				
			||||||
    """Convert an int argument into high and low bytes"""
 | 
					 | 
				
			||||||
    assert type(val) == types.IntType
 | 
					 | 
				
			||||||
    return divmod(val, 256)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LineAddrTable:
 | 
					 | 
				
			||||||
    """lnotab
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    This class builds the lnotab, which is documented in compile.c.
 | 
					 | 
				
			||||||
    Here's a brief recap:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    For each SET_LINENO instruction after the first one, two bytes are
 | 
					 | 
				
			||||||
    added to lnotab.  (In some cases, multiple two-byte entries are
 | 
					 | 
				
			||||||
    added.)  The first byte is the distance in bytes between the
 | 
					 | 
				
			||||||
    instruction for the last SET_LINENO and the current SET_LINENO.
 | 
					 | 
				
			||||||
    The second byte is offset in line numbers.  If either offset is
 | 
					 | 
				
			||||||
    greater than 255, multiple two-byte entries are added -- see
 | 
					 | 
				
			||||||
    compile.c for the delicate details.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.code = []
 | 
					 | 
				
			||||||
        self.codeOffset = 0
 | 
					 | 
				
			||||||
        self.firstline = 0
 | 
					 | 
				
			||||||
        self.lastline = 0
 | 
					 | 
				
			||||||
        self.lastoff = 0
 | 
					 | 
				
			||||||
        self.lnotab = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def addCode(self, *args):
 | 
					 | 
				
			||||||
        for arg in args:
 | 
					 | 
				
			||||||
            self.code.append(chr(arg))
 | 
					 | 
				
			||||||
        self.codeOffset = self.codeOffset + len(args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def nextLine(self, lineno):
 | 
					 | 
				
			||||||
        if self.firstline == 0:
 | 
					 | 
				
			||||||
            self.firstline = lineno
 | 
					 | 
				
			||||||
            self.lastline = lineno
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            # compute deltas
 | 
					 | 
				
			||||||
            addr = self.codeOffset - self.lastoff
 | 
					 | 
				
			||||||
            line = lineno - self.lastline
 | 
					 | 
				
			||||||
            # Python assumes that lineno always increases with
 | 
					 | 
				
			||||||
            # increasing bytecode address (lnotab is unsigned char).
 | 
					 | 
				
			||||||
            # Depending on when SET_LINENO instructions are emitted
 | 
					 | 
				
			||||||
            # this is not always true.  Consider the code:
 | 
					 | 
				
			||||||
            #     a = (1,
 | 
					 | 
				
			||||||
            #          b)
 | 
					 | 
				
			||||||
            # In the bytecode stream, the assignment to "a" occurs
 | 
					 | 
				
			||||||
            # after the loading of "b".  This works with the C Python
 | 
					 | 
				
			||||||
            # compiler because it only generates a SET_LINENO instruction
 | 
					 | 
				
			||||||
            # for the assignment.
 | 
					 | 
				
			||||||
            if line > 0:
 | 
					 | 
				
			||||||
                push = self.lnotab.append
 | 
					 | 
				
			||||||
                while addr > 255:
 | 
					 | 
				
			||||||
                    push(255); push(0)
 | 
					 | 
				
			||||||
                    addr -= 255
 | 
					 | 
				
			||||||
                while line > 255:
 | 
					 | 
				
			||||||
                    push(addr); push(255)
 | 
					 | 
				
			||||||
                    line -= 255
 | 
					 | 
				
			||||||
                    addr = 0
 | 
					 | 
				
			||||||
                if addr > 0 or line > 0:
 | 
					 | 
				
			||||||
                    push(addr); push(line)
 | 
					 | 
				
			||||||
                self.lastline = lineno
 | 
					 | 
				
			||||||
                self.lastoff = self.codeOffset
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getCode(self):
 | 
					 | 
				
			||||||
        return string.join(self.code, '')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getTable(self):
 | 
					 | 
				
			||||||
        return string.join(map(chr, self.lnotab), '')
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
class StackDepthTracker:
 | 
					 | 
				
			||||||
    # XXX 1. need to keep track of stack depth on jumps
 | 
					 | 
				
			||||||
    # XXX 2. at least partly as a result, this code is broken
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def findDepth(self, insts):
 | 
					 | 
				
			||||||
        depth = 0
 | 
					 | 
				
			||||||
        maxDepth = 0
 | 
					 | 
				
			||||||
        for i in insts:
 | 
					 | 
				
			||||||
            opname = i[0]
 | 
					 | 
				
			||||||
            delta = self.effect.get(opname, 0)
 | 
					 | 
				
			||||||
            if delta > 1:
 | 
					 | 
				
			||||||
                depth = depth + delta
 | 
					 | 
				
			||||||
            elif delta < 0:
 | 
					 | 
				
			||||||
                if depth > maxDepth:
 | 
					 | 
				
			||||||
                    maxDepth = depth
 | 
					 | 
				
			||||||
                depth = depth + delta
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                if depth > maxDepth:
 | 
					 | 
				
			||||||
                    maxDepth = depth
 | 
					 | 
				
			||||||
                # now check patterns
 | 
					 | 
				
			||||||
                for pat, pat_delta in self.patterns:
 | 
					 | 
				
			||||||
                    if opname[:len(pat)] == pat:
 | 
					 | 
				
			||||||
                        delta = pat_delta
 | 
					 | 
				
			||||||
                        depth = depth + delta
 | 
					 | 
				
			||||||
                        break
 | 
					 | 
				
			||||||
                # if we still haven't found a match
 | 
					 | 
				
			||||||
                if delta == 0:
 | 
					 | 
				
			||||||
                    meth = getattr(self, opname, None)
 | 
					 | 
				
			||||||
                    if meth is not None:
 | 
					 | 
				
			||||||
                        depth = depth + meth(i[1])
 | 
					 | 
				
			||||||
            if depth < 0:
 | 
					 | 
				
			||||||
                depth = 0
 | 
					 | 
				
			||||||
        return maxDepth
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    effect = {
 | 
					 | 
				
			||||||
        'POP_TOP': -1,
 | 
					 | 
				
			||||||
        'DUP_TOP': 1,
 | 
					 | 
				
			||||||
        'SLICE+1': -1,
 | 
					 | 
				
			||||||
        'SLICE+2': -1,
 | 
					 | 
				
			||||||
        'SLICE+3': -2,
 | 
					 | 
				
			||||||
        'STORE_SLICE+0': -1,
 | 
					 | 
				
			||||||
        'STORE_SLICE+1': -2,
 | 
					 | 
				
			||||||
        'STORE_SLICE+2': -2,
 | 
					 | 
				
			||||||
        'STORE_SLICE+3': -3,
 | 
					 | 
				
			||||||
        'DELETE_SLICE+0': -1,
 | 
					 | 
				
			||||||
        'DELETE_SLICE+1': -2,
 | 
					 | 
				
			||||||
        'DELETE_SLICE+2': -2,
 | 
					 | 
				
			||||||
        'DELETE_SLICE+3': -3,
 | 
					 | 
				
			||||||
        'STORE_SUBSCR': -3,
 | 
					 | 
				
			||||||
        'DELETE_SUBSCR': -2,
 | 
					 | 
				
			||||||
        # PRINT_EXPR?
 | 
					 | 
				
			||||||
        'PRINT_ITEM': -1,
 | 
					 | 
				
			||||||
        'RETURN_VALUE': -1,
 | 
					 | 
				
			||||||
        'EXEC_STMT': -3,
 | 
					 | 
				
			||||||
        'BUILD_CLASS': -2,
 | 
					 | 
				
			||||||
        'STORE_NAME': -1,
 | 
					 | 
				
			||||||
        'STORE_ATTR': -2,
 | 
					 | 
				
			||||||
        'DELETE_ATTR': -1,
 | 
					 | 
				
			||||||
        'STORE_GLOBAL': -1,
 | 
					 | 
				
			||||||
        'BUILD_MAP': 1,
 | 
					 | 
				
			||||||
        'COMPARE_OP': -1,
 | 
					 | 
				
			||||||
        'STORE_FAST': -1,
 | 
					 | 
				
			||||||
        'IMPORT_STAR': -1,
 | 
					 | 
				
			||||||
        'IMPORT_NAME': 0,
 | 
					 | 
				
			||||||
        'IMPORT_FROM': 1,
 | 
					 | 
				
			||||||
        # close enough...
 | 
					 | 
				
			||||||
        'SETUP_EXCEPT': 3,
 | 
					 | 
				
			||||||
        'SETUP_FINALLY': 3,
 | 
					 | 
				
			||||||
        'FOR_ITER': 1,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    # use pattern match
 | 
					 | 
				
			||||||
    patterns = [
 | 
					 | 
				
			||||||
        ('BINARY_', -1),
 | 
					 | 
				
			||||||
        ('LOAD_', 1),
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    def UNPACK_SEQUENCE(self, count):
 | 
					 | 
				
			||||||
        return count-1
 | 
					 | 
				
			||||||
    def BUILD_TUPLE(self, count):
 | 
					 | 
				
			||||||
        return -count+1
 | 
					 | 
				
			||||||
    def BUILD_LIST(self, count):
 | 
					 | 
				
			||||||
        return -count+1
 | 
					 | 
				
			||||||
    def CALL_FUNCTION(self, argc):
 | 
					 | 
				
			||||||
        hi, lo = divmod(argc, 256)
 | 
					 | 
				
			||||||
        return lo + hi * 2
 | 
					 | 
				
			||||||
    def CALL_FUNCTION_VAR(self, argc):
 | 
					 | 
				
			||||||
        return self.CALL_FUNCTION(argc)+1
 | 
					 | 
				
			||||||
    def CALL_FUNCTION_KW(self, argc):
 | 
					 | 
				
			||||||
        return self.CALL_FUNCTION(argc)+1
 | 
					 | 
				
			||||||
    def CALL_FUNCTION_VAR_KW(self, argc):
 | 
					 | 
				
			||||||
        return self.CALL_FUNCTION(argc)+2
 | 
					 | 
				
			||||||
    def MAKE_FUNCTION(self, argc):
 | 
					 | 
				
			||||||
        return -argc
 | 
					 | 
				
			||||||
    def BUILD_SLICE(self, argc):
 | 
					 | 
				
			||||||
        if argc == 2:
 | 
					 | 
				
			||||||
            return -1
 | 
					 | 
				
			||||||
        elif argc == 3:
 | 
					 | 
				
			||||||
            return -2
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
findDepth = StackDepthTracker().findDepth
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -1,415 +0,0 @@
 | 
				
			||||||
"""Module symbol-table generator"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from compiler import ast
 | 
					 | 
				
			||||||
from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL, SC_UNKNOWN
 | 
					 | 
				
			||||||
from compiler.misc import mangle
 | 
					 | 
				
			||||||
import types
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
MANGLE_LEN = 256
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Scope:
 | 
					 | 
				
			||||||
    # XXX how much information do I need about each name?
 | 
					 | 
				
			||||||
    def __init__(self, name, module, klass=None):
 | 
					 | 
				
			||||||
        self.name = name
 | 
					 | 
				
			||||||
        self.module = module
 | 
					 | 
				
			||||||
        self.defs = {}
 | 
					 | 
				
			||||||
        self.uses = {}
 | 
					 | 
				
			||||||
        self.globals = {}
 | 
					 | 
				
			||||||
        self.params = {}
 | 
					 | 
				
			||||||
        self.frees = {}
 | 
					 | 
				
			||||||
        self.cells = {}
 | 
					 | 
				
			||||||
        self.children = []
 | 
					 | 
				
			||||||
        # nested is true if the class could contain free variables,
 | 
					 | 
				
			||||||
        # i.e. if it is nested within another function.
 | 
					 | 
				
			||||||
        self.nested = None
 | 
					 | 
				
			||||||
        self.generator = None
 | 
					 | 
				
			||||||
        self.klass = None
 | 
					 | 
				
			||||||
        if klass is not None:
 | 
					 | 
				
			||||||
            for i in range(len(klass)):
 | 
					 | 
				
			||||||
                if klass[i] != '_':
 | 
					 | 
				
			||||||
                    self.klass = klass[i:]
 | 
					 | 
				
			||||||
                    break
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __repr__(self):
 | 
					 | 
				
			||||||
        return "<%s: %s>" % (self.__class__.__name__, self.name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def mangle(self, name):
 | 
					 | 
				
			||||||
        if self.klass is None:
 | 
					 | 
				
			||||||
            return name
 | 
					 | 
				
			||||||
        return mangle(name, self.klass)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def add_def(self, name):
 | 
					 | 
				
			||||||
        self.defs[self.mangle(name)] = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def add_use(self, name):
 | 
					 | 
				
			||||||
        self.uses[self.mangle(name)] = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def add_global(self, name):
 | 
					 | 
				
			||||||
        name = self.mangle(name)
 | 
					 | 
				
			||||||
        if self.uses.has_key(name) or self.defs.has_key(name):
 | 
					 | 
				
			||||||
            pass # XXX warn about global following def/use
 | 
					 | 
				
			||||||
        if self.params.has_key(name):
 | 
					 | 
				
			||||||
            raise SyntaxError, "%s in %s is global and parameter" % \
 | 
					 | 
				
			||||||
                  (name, self.name)
 | 
					 | 
				
			||||||
        self.globals[name] = 1
 | 
					 | 
				
			||||||
        self.module.add_def(name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def add_param(self, name):
 | 
					 | 
				
			||||||
        name = self.mangle(name)
 | 
					 | 
				
			||||||
        self.defs[name] = 1
 | 
					 | 
				
			||||||
        self.params[name] = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_names(self):
 | 
					 | 
				
			||||||
        d = {}
 | 
					 | 
				
			||||||
        d.update(self.defs)
 | 
					 | 
				
			||||||
        d.update(self.uses)
 | 
					 | 
				
			||||||
        d.update(self.globals)
 | 
					 | 
				
			||||||
        return d.keys()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def add_child(self, child):
 | 
					 | 
				
			||||||
        self.children.append(child)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_children(self):
 | 
					 | 
				
			||||||
        return self.children
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def DEBUG(self):
 | 
					 | 
				
			||||||
        print >> sys.stderr, self.name, self.nested and "nested" or ""
 | 
					 | 
				
			||||||
        print >> sys.stderr, "\tglobals: ", self.globals
 | 
					 | 
				
			||||||
        print >> sys.stderr, "\tcells: ", self.cells
 | 
					 | 
				
			||||||
        print >> sys.stderr, "\tdefs: ", self.defs
 | 
					 | 
				
			||||||
        print >> sys.stderr, "\tuses: ", self.uses
 | 
					 | 
				
			||||||
        print >> sys.stderr, "\tfrees:", self.frees
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_name(self, name):
 | 
					 | 
				
			||||||
        """Return scope of name.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        The scope of a name could be LOCAL, GLOBAL, FREE, or CELL.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if self.globals.has_key(name):
 | 
					 | 
				
			||||||
            return SC_GLOBAL
 | 
					 | 
				
			||||||
        if self.cells.has_key(name):
 | 
					 | 
				
			||||||
            return SC_CELL
 | 
					 | 
				
			||||||
        if self.defs.has_key(name):
 | 
					 | 
				
			||||||
            return SC_LOCAL
 | 
					 | 
				
			||||||
        if self.nested and (self.frees.has_key(name) or
 | 
					 | 
				
			||||||
                            self.uses.has_key(name)):
 | 
					 | 
				
			||||||
            return SC_FREE
 | 
					 | 
				
			||||||
        if self.nested:
 | 
					 | 
				
			||||||
            return SC_UNKNOWN
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            return SC_GLOBAL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_free_vars(self):
 | 
					 | 
				
			||||||
        if not self.nested:
 | 
					 | 
				
			||||||
            return ()
 | 
					 | 
				
			||||||
        free = {}
 | 
					 | 
				
			||||||
        free.update(self.frees)
 | 
					 | 
				
			||||||
        for name in self.uses.keys():
 | 
					 | 
				
			||||||
            if not (self.defs.has_key(name) or
 | 
					 | 
				
			||||||
                    self.globals.has_key(name)):
 | 
					 | 
				
			||||||
                free[name] = 1
 | 
					 | 
				
			||||||
        return free.keys()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def handle_children(self):
 | 
					 | 
				
			||||||
        for child in self.children:
 | 
					 | 
				
			||||||
            frees = child.get_free_vars()
 | 
					 | 
				
			||||||
            globals = self.add_frees(frees)
 | 
					 | 
				
			||||||
            for name in globals:
 | 
					 | 
				
			||||||
                child.force_global(name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def force_global(self, name):
 | 
					 | 
				
			||||||
        """Force name to be global in scope.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Some child of the current node had a free reference to name.
 | 
					 | 
				
			||||||
        When the child was processed, it was labelled a free
 | 
					 | 
				
			||||||
        variable.  Now that all its enclosing scope have been
 | 
					 | 
				
			||||||
        processed, the name is known to be a global or builtin.  So
 | 
					 | 
				
			||||||
        walk back down the child chain and set the name to be global
 | 
					 | 
				
			||||||
        rather than free.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Be careful to stop if a child does not think the name is
 | 
					 | 
				
			||||||
        free. 
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.globals[name] = 1
 | 
					 | 
				
			||||||
        if self.frees.has_key(name):
 | 
					 | 
				
			||||||
            del self.frees[name]
 | 
					 | 
				
			||||||
        for child in self.children:
 | 
					 | 
				
			||||||
            if child.check_name(name) == SC_FREE:
 | 
					 | 
				
			||||||
                child.force_global(name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def add_frees(self, names):
 | 
					 | 
				
			||||||
        """Process list of free vars from nested scope.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Returns a list of names that are either 1) declared global in the
 | 
					 | 
				
			||||||
        parent or 2) undefined in a top-level parent.  In either case,
 | 
					 | 
				
			||||||
        the nested scope should treat them as globals.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        child_globals = []
 | 
					 | 
				
			||||||
        for name in names:
 | 
					 | 
				
			||||||
            sc = self.check_name(name)
 | 
					 | 
				
			||||||
            if self.nested:
 | 
					 | 
				
			||||||
                if sc == SC_UNKNOWN or sc == SC_FREE \
 | 
					 | 
				
			||||||
                   or isinstance(self, ClassScope):
 | 
					 | 
				
			||||||
                    self.frees[name] = 1
 | 
					 | 
				
			||||||
                elif sc == SC_GLOBAL:
 | 
					 | 
				
			||||||
                    child_globals.append(name)
 | 
					 | 
				
			||||||
                elif isinstance(self, FunctionScope) and sc == SC_LOCAL:
 | 
					 | 
				
			||||||
                    self.cells[name] = 1
 | 
					 | 
				
			||||||
                elif sc != SC_CELL:
 | 
					 | 
				
			||||||
                    child_globals.append(name)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                if sc == SC_LOCAL:
 | 
					 | 
				
			||||||
                    self.cells[name] = 1
 | 
					 | 
				
			||||||
                elif sc != SC_CELL:
 | 
					 | 
				
			||||||
                    child_globals.append(name)
 | 
					 | 
				
			||||||
        return child_globals
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_cell_vars(self):
 | 
					 | 
				
			||||||
        return self.cells.keys()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ModuleScope(Scope):
 | 
					 | 
				
			||||||
    __super_init = Scope.__init__
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.__super_init("global", self)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FunctionScope(Scope):
 | 
					 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LambdaScope(FunctionScope):
 | 
					 | 
				
			||||||
    __super_init = Scope.__init__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    __counter = 1
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    def __init__(self, module, klass=None):
 | 
					 | 
				
			||||||
        i = self.__counter
 | 
					 | 
				
			||||||
        self.__counter += 1
 | 
					 | 
				
			||||||
        self.__super_init("lambda.%d" % i, module, klass)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ClassScope(Scope):
 | 
					 | 
				
			||||||
    __super_init = Scope.__init__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, name, module):
 | 
					 | 
				
			||||||
        self.__super_init(name, module, name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SymbolVisitor:
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.scopes = {}
 | 
					 | 
				
			||||||
        self.klass = None
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    # node that define new scopes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitModule(self, node):
 | 
					 | 
				
			||||||
        scope = self.module = self.scopes[node] = ModuleScope()
 | 
					 | 
				
			||||||
        self.visit(node.node, scope)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitFunction(self, node, parent):
 | 
					 | 
				
			||||||
        parent.add_def(node.name)
 | 
					 | 
				
			||||||
        for n in node.defaults:
 | 
					 | 
				
			||||||
            self.visit(n, parent)
 | 
					 | 
				
			||||||
        scope = FunctionScope(node.name, self.module, self.klass)
 | 
					 | 
				
			||||||
        if parent.nested or isinstance(parent, FunctionScope):
 | 
					 | 
				
			||||||
            scope.nested = 1
 | 
					 | 
				
			||||||
        self.scopes[node] = scope
 | 
					 | 
				
			||||||
        self._do_args(scope, node.argnames)
 | 
					 | 
				
			||||||
        self.visit(node.code, scope)
 | 
					 | 
				
			||||||
        self.handle_free_vars(scope, parent)
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    def visitLambda(self, node, parent):
 | 
					 | 
				
			||||||
        for n in node.defaults:
 | 
					 | 
				
			||||||
            self.visit(n, parent)
 | 
					 | 
				
			||||||
        scope = LambdaScope(self.module, self.klass)
 | 
					 | 
				
			||||||
        if parent.nested or isinstance(parent, FunctionScope):
 | 
					 | 
				
			||||||
            scope.nested = 1
 | 
					 | 
				
			||||||
        self.scopes[node] = scope
 | 
					 | 
				
			||||||
        self._do_args(scope, node.argnames)
 | 
					 | 
				
			||||||
        self.visit(node.code, scope)
 | 
					 | 
				
			||||||
        self.handle_free_vars(scope, parent)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _do_args(self, scope, args):
 | 
					 | 
				
			||||||
        for name in args:
 | 
					 | 
				
			||||||
            if type(name) == types.TupleType:
 | 
					 | 
				
			||||||
                self._do_args(scope, name)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                scope.add_param(name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def handle_free_vars(self, scope, parent):
 | 
					 | 
				
			||||||
        parent.add_child(scope)
 | 
					 | 
				
			||||||
        scope.handle_children()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitClass(self, node, parent):
 | 
					 | 
				
			||||||
        parent.add_def(node.name)
 | 
					 | 
				
			||||||
        for n in node.bases:
 | 
					 | 
				
			||||||
            self.visit(n, parent)
 | 
					 | 
				
			||||||
        scope = ClassScope(node.name, self.module)
 | 
					 | 
				
			||||||
        if parent.nested or isinstance(parent, FunctionScope):
 | 
					 | 
				
			||||||
            scope.nested = 1
 | 
					 | 
				
			||||||
        self.scopes[node] = scope
 | 
					 | 
				
			||||||
        prev = self.klass
 | 
					 | 
				
			||||||
        self.klass = node.name
 | 
					 | 
				
			||||||
        self.visit(node.code, scope)
 | 
					 | 
				
			||||||
        self.klass = prev
 | 
					 | 
				
			||||||
        self.handle_free_vars(scope, parent)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # name can be a def or a use
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # XXX a few calls and nodes expect a third "assign" arg that is
 | 
					 | 
				
			||||||
    # true if the name is being used as an assignment.  only
 | 
					 | 
				
			||||||
    # expressions contained within statements may have the assign arg.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitName(self, node, scope, assign=0):
 | 
					 | 
				
			||||||
        if assign:
 | 
					 | 
				
			||||||
            scope.add_def(node.name)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            scope.add_use(node.name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # operations that bind new names
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitFor(self, node, scope):
 | 
					 | 
				
			||||||
        self.visit(node.assign, scope, 1)
 | 
					 | 
				
			||||||
        self.visit(node.list, scope)
 | 
					 | 
				
			||||||
        self.visit(node.body, scope)
 | 
					 | 
				
			||||||
        if node.else_:
 | 
					 | 
				
			||||||
            self.visit(node.else_, scope)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitFrom(self, node, scope):
 | 
					 | 
				
			||||||
        for name, asname in node.names:
 | 
					 | 
				
			||||||
            if name == "*":
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            scope.add_def(asname or name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitImport(self, node, scope):
 | 
					 | 
				
			||||||
        for name, asname in node.names:
 | 
					 | 
				
			||||||
            i = name.find(".")
 | 
					 | 
				
			||||||
            if i > -1:
 | 
					 | 
				
			||||||
                name = name[:i]
 | 
					 | 
				
			||||||
            scope.add_def(asname or name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitGlobal(self, node, scope):
 | 
					 | 
				
			||||||
        for name in node.names:
 | 
					 | 
				
			||||||
            scope.add_global(name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitAssign(self, node, scope):
 | 
					 | 
				
			||||||
        """Propagate assignment flag down to child nodes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        The Assign node doesn't itself contains the variables being
 | 
					 | 
				
			||||||
        assigned to.  Instead, the children in node.nodes are visited
 | 
					 | 
				
			||||||
        with the assign flag set to true.  When the names occur in
 | 
					 | 
				
			||||||
        those nodes, they are marked as defs.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Some names that occur in an assignment target are not bound by
 | 
					 | 
				
			||||||
        the assignment, e.g. a name occurring inside a slice.  The
 | 
					 | 
				
			||||||
        visitor handles these nodes specially; they do not propagate
 | 
					 | 
				
			||||||
        the assign flag to their children.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        for n in node.nodes:
 | 
					 | 
				
			||||||
            self.visit(n, scope, 1)
 | 
					 | 
				
			||||||
        self.visit(node.expr, scope)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitAssName(self, node, scope, assign=1):
 | 
					 | 
				
			||||||
        scope.add_def(node.name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitAssAttr(self, node, scope, assign=0):
 | 
					 | 
				
			||||||
        self.visit(node.expr, scope, 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitSubscript(self, node, scope, assign=0):
 | 
					 | 
				
			||||||
        self.visit(node.expr, scope, 0)
 | 
					 | 
				
			||||||
        for n in node.subs:
 | 
					 | 
				
			||||||
            self.visit(n, scope, 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitSlice(self, node, scope, assign=0):
 | 
					 | 
				
			||||||
        self.visit(node.expr, scope, 0)
 | 
					 | 
				
			||||||
        if node.lower:
 | 
					 | 
				
			||||||
            self.visit(node.lower, scope, 0)
 | 
					 | 
				
			||||||
        if node.upper:
 | 
					 | 
				
			||||||
            self.visit(node.upper, scope, 0)
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
    def visitAugAssign(self, node, scope):
 | 
					 | 
				
			||||||
        # If the LHS is a name, then this counts as assignment.
 | 
					 | 
				
			||||||
        # Otherwise, it's just use.
 | 
					 | 
				
			||||||
        self.visit(node.node, scope)
 | 
					 | 
				
			||||||
        if isinstance(node.node, ast.Name):
 | 
					 | 
				
			||||||
            self.visit(node.node, scope, 1) # XXX worry about this
 | 
					 | 
				
			||||||
        self.visit(node.expr, scope)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # prune if statements if tests are false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _const_types = types.StringType, types.IntType, types.FloatType
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitIf(self, node, scope):
 | 
					 | 
				
			||||||
        for test, body in node.tests:
 | 
					 | 
				
			||||||
            if isinstance(test, ast.Const):
 | 
					 | 
				
			||||||
                if type(test.value) in self._const_types:
 | 
					 | 
				
			||||||
                    if not test.value:
 | 
					 | 
				
			||||||
                        continue
 | 
					 | 
				
			||||||
            self.visit(test, scope)
 | 
					 | 
				
			||||||
            self.visit(body, scope)
 | 
					 | 
				
			||||||
        if node.else_:
 | 
					 | 
				
			||||||
            self.visit(node.else_, scope)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # a yield statement signals a generator
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitYield(self, node, scope):
 | 
					 | 
				
			||||||
        scope.generator = 1
 | 
					 | 
				
			||||||
        self.visit(node.value, scope)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def sort(l):
 | 
					 | 
				
			||||||
    l = l[:]
 | 
					 | 
				
			||||||
    l.sort()
 | 
					 | 
				
			||||||
    return l
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def list_eq(l1, l2):
 | 
					 | 
				
			||||||
    return sort(l1) == sort(l2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == "__main__":
 | 
					 | 
				
			||||||
    import sys
 | 
					 | 
				
			||||||
    from compiler import parseFile, walk
 | 
					 | 
				
			||||||
    import symtable
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_names(syms):
 | 
					 | 
				
			||||||
        return [s for s in [s.get_name() for s in syms.get_symbols()]
 | 
					 | 
				
			||||||
                if not (s.startswith('_[') or s.startswith('.'))]        
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    for file in sys.argv[1:]:
 | 
					 | 
				
			||||||
        print file
 | 
					 | 
				
			||||||
        f = open(file)
 | 
					 | 
				
			||||||
        buf = f.read()
 | 
					 | 
				
			||||||
        f.close()
 | 
					 | 
				
			||||||
        syms = symtable.symtable(buf, file, "exec")
 | 
					 | 
				
			||||||
        mod_names = get_names(syms)
 | 
					 | 
				
			||||||
        tree = parseFile(file)
 | 
					 | 
				
			||||||
        s = SymbolVisitor()
 | 
					 | 
				
			||||||
        walk(tree, s)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # compare module-level symbols
 | 
					 | 
				
			||||||
        names2 = s.scopes[tree].get_names()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not list_eq(mod_names, names2):
 | 
					 | 
				
			||||||
            print
 | 
					 | 
				
			||||||
            print "oops", file
 | 
					 | 
				
			||||||
            print sort(mod_names)
 | 
					 | 
				
			||||||
            print sort(names2)
 | 
					 | 
				
			||||||
            sys.exit(-1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        d = {}
 | 
					 | 
				
			||||||
        d.update(s.scopes)
 | 
					 | 
				
			||||||
        del d[tree]
 | 
					 | 
				
			||||||
        scopes = d.values()
 | 
					 | 
				
			||||||
        del d
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for s in syms.get_symbols():
 | 
					 | 
				
			||||||
            if s.is_namespace():
 | 
					 | 
				
			||||||
                l = [sc for sc in scopes
 | 
					 | 
				
			||||||
                     if sc.name == s.get_name()]
 | 
					 | 
				
			||||||
                if len(l) > 1:
 | 
					 | 
				
			||||||
                    print "skipping", s.get_name()
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    if not list_eq(get_names(s.get_namespace()),
 | 
					 | 
				
			||||||
                                   l[0].get_names()):
 | 
					 | 
				
			||||||
                        print s.get_name()
 | 
					 | 
				
			||||||
                        print sort(get_names(s.get_namespace()))
 | 
					 | 
				
			||||||
                        print sort(l[0].get_names())
 | 
					 | 
				
			||||||
                        sys.exit(-1)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,46 +0,0 @@
 | 
				
			||||||
"""Check for errs in the AST.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Python parser does not catch all syntax errors.  Others, like
 | 
					 | 
				
			||||||
assignments with invalid targets, are caught in the code generation
 | 
					 | 
				
			||||||
phase.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The compiler package catches some errors in the transformer module.
 | 
					 | 
				
			||||||
But it seems clearer to write checkers that use the AST to detect
 | 
					 | 
				
			||||||
errors.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from compiler import ast, walk
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def check(tree, multi=None):
 | 
					 | 
				
			||||||
    v = SyntaxErrorChecker(multi)
 | 
					 | 
				
			||||||
    walk(tree, v)
 | 
					 | 
				
			||||||
    return v.errors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SyntaxErrorChecker:
 | 
					 | 
				
			||||||
    """A visitor to find syntax errors in the AST."""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, multi=None):
 | 
					 | 
				
			||||||
        """Create new visitor object.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        If optional argument multi is not None, then print messages
 | 
					 | 
				
			||||||
        for each error rather than raising a SyntaxError for the
 | 
					 | 
				
			||||||
        first.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.multi = multi
 | 
					 | 
				
			||||||
        self.errors = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def error(self, node, msg):
 | 
					 | 
				
			||||||
        self.errors = self.errors + 1
 | 
					 | 
				
			||||||
        if self.multi is not None:
 | 
					 | 
				
			||||||
            print "%s:%s: %s" % (node.filename, node.lineno, msg)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            raise SyntaxError, "%s (%s:%s)" % (msg, node.filename, node.lineno)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def visitAssign(self, node):
 | 
					 | 
				
			||||||
        # the transformer module handles many of these
 | 
					 | 
				
			||||||
        for target in node.nodes:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
##            if isinstance(target, ast.AssList):
 | 
					 | 
				
			||||||
##                if target.lineno is None:
 | 
					 | 
				
			||||||
##                    target.lineno = node.lineno
 | 
					 | 
				
			||||||
##                self.error(target, "can't assign to list comprehension")
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -1,121 +0,0 @@
 | 
				
			||||||
from compiler import ast
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# XXX should probably rename ASTVisitor to ASTWalker
 | 
					 | 
				
			||||||
# XXX can it be made even more generic?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ASTVisitor:
 | 
					 | 
				
			||||||
    """Performs a depth-first walk of the AST
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The ASTVisitor will walk the AST, performing either a preorder or
 | 
					 | 
				
			||||||
    postorder traversal depending on which method is called.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    methods:
 | 
					 | 
				
			||||||
    preorder(tree, visitor)
 | 
					 | 
				
			||||||
    postorder(tree, visitor)
 | 
					 | 
				
			||||||
        tree: an instance of ast.Node
 | 
					 | 
				
			||||||
        visitor: an instance with visitXXX methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The ASTVisitor is responsible for walking over the tree in the
 | 
					 | 
				
			||||||
    correct order.  For each node, it checks the visitor argument for
 | 
					 | 
				
			||||||
    a method named 'visitNodeType' where NodeType is the name of the
 | 
					 | 
				
			||||||
    node's class, e.g. Class.  If the method exists, it is called
 | 
					 | 
				
			||||||
    with the node as its sole argument.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The visitor method for a particular node type can control how
 | 
					 | 
				
			||||||
    child nodes are visited during a preorder walk.  (It can't control
 | 
					 | 
				
			||||||
    the order during a postorder walk, because it is called _after_
 | 
					 | 
				
			||||||
    the walk has occurred.)  The ASTVisitor modifies the visitor
 | 
					 | 
				
			||||||
    argument by adding a visit method to the visitor; this method can
 | 
					 | 
				
			||||||
    be used to visit a particular child node.  If the visitor method
 | 
					 | 
				
			||||||
    returns a true value, the ASTVisitor will not traverse the child
 | 
					 | 
				
			||||||
    nodes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    XXX The interface for controlling the preorder walk needs to be
 | 
					 | 
				
			||||||
    re-considered.  The current interface is convenient for visitors
 | 
					 | 
				
			||||||
    that mostly let the ASTVisitor do everything.  For something like
 | 
					 | 
				
			||||||
    a code generator, where you want to walk to occur in a specific
 | 
					 | 
				
			||||||
    order, it's a pain to add "return 1" to the end of each method.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    VERBOSE = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.node = None
 | 
					 | 
				
			||||||
        self._cache = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def default(self, node, *args):
 | 
					 | 
				
			||||||
        for child in node.getChildNodes():
 | 
					 | 
				
			||||||
            self.dispatch(child, *args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def dispatch(self, node, *args):
 | 
					 | 
				
			||||||
        self.node = node
 | 
					 | 
				
			||||||
        klass = node.__class__
 | 
					 | 
				
			||||||
        meth = self._cache.get(klass, None)
 | 
					 | 
				
			||||||
        if meth is None:
 | 
					 | 
				
			||||||
            className = klass.__name__
 | 
					 | 
				
			||||||
            meth = getattr(self.visitor, 'visit' + className, self.default)
 | 
					 | 
				
			||||||
            self._cache[klass] = meth
 | 
					 | 
				
			||||||
##        if self.VERBOSE > 0:
 | 
					 | 
				
			||||||
##            className = klass.__name__
 | 
					 | 
				
			||||||
##            if self.VERBOSE == 1:
 | 
					 | 
				
			||||||
##                if meth == 0:
 | 
					 | 
				
			||||||
##                    print "dispatch", className
 | 
					 | 
				
			||||||
##            else:
 | 
					 | 
				
			||||||
##                print "dispatch", className, (meth and meth.__name__ or '')
 | 
					 | 
				
			||||||
        return meth(node, *args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def preorder(self, tree, visitor, *args):
 | 
					 | 
				
			||||||
        """Do preorder walk of tree using visitor"""
 | 
					 | 
				
			||||||
        self.visitor = visitor
 | 
					 | 
				
			||||||
        visitor.visit = self.dispatch
 | 
					 | 
				
			||||||
        self.dispatch(tree, *args) # XXX *args make sense?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ExampleASTVisitor(ASTVisitor):
 | 
					 | 
				
			||||||
    """Prints examples of the nodes that aren't visited
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This visitor-driver is only useful for development, when it's
 | 
					 | 
				
			||||||
    helpful to develop a visitor incremently, and get feedback on what
 | 
					 | 
				
			||||||
    you still have to do.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    examples = {}
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    def dispatch(self, node, *args):
 | 
					 | 
				
			||||||
        self.node = node
 | 
					 | 
				
			||||||
        meth = self._cache.get(node.__class__, None)
 | 
					 | 
				
			||||||
        className = node.__class__.__name__
 | 
					 | 
				
			||||||
        if meth is None:
 | 
					 | 
				
			||||||
            meth = getattr(self.visitor, 'visit' + className, 0)
 | 
					 | 
				
			||||||
            self._cache[node.__class__] = meth
 | 
					 | 
				
			||||||
        if self.VERBOSE > 1:
 | 
					 | 
				
			||||||
            print "dispatch", className, (meth and meth.__name__ or '')
 | 
					 | 
				
			||||||
        if meth:
 | 
					 | 
				
			||||||
            meth(node, *args)
 | 
					 | 
				
			||||||
        elif self.VERBOSE > 0:
 | 
					 | 
				
			||||||
            klass = node.__class__
 | 
					 | 
				
			||||||
            if not self.examples.has_key(klass):
 | 
					 | 
				
			||||||
                self.examples[klass] = klass
 | 
					 | 
				
			||||||
                print
 | 
					 | 
				
			||||||
                print self.visitor
 | 
					 | 
				
			||||||
                print klass
 | 
					 | 
				
			||||||
                for attr in dir(node):
 | 
					 | 
				
			||||||
                    if attr[0] != '_':
 | 
					 | 
				
			||||||
                        print "\t", "%-12.12s" % attr, getattr(node, attr)
 | 
					 | 
				
			||||||
                print
 | 
					 | 
				
			||||||
            return self.default(node, *args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# XXX this is an API change
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
_walker = ASTVisitor
 | 
					 | 
				
			||||||
def walk(tree, visitor, walker=None, verbose=None):
 | 
					 | 
				
			||||||
    if walker is None:
 | 
					 | 
				
			||||||
        walker = _walker()
 | 
					 | 
				
			||||||
    if verbose is not None:
 | 
					 | 
				
			||||||
        walker.VERBOSE = verbose
 | 
					 | 
				
			||||||
    walker.preorder(tree, visitor)
 | 
					 | 
				
			||||||
    return walker.visitor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def dumpNode(node):
 | 
					 | 
				
			||||||
    print node.__class__
 | 
					 | 
				
			||||||
    for attr in dir(node):
 | 
					 | 
				
			||||||
        if attr[0] != '_':
 | 
					 | 
				
			||||||
            print "\t", "%-10.10s" % attr, getattr(node, attr)
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue