| """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]] |