+++ /dev/null
-"""Generate ast module from specification\r
-\r
-This script generates the ast module from a simple specification,\r
-which makes it easy to accomodate changes in the grammar. This\r
-approach would be quite reasonable if the grammar changed often.\r
-Instead, it is rather complex to generate the appropriate code. And\r
-the Node interface has changed more often than the grammar.\r
-"""\r
-\r
-import fileinput\r
-import re\r
-import sys\r
-from StringIO import StringIO\r
-\r
-SPEC = "ast.txt"\r
-COMMA = ", "\r
-\r
-def load_boilerplate(file):\r
- f = open(file)\r
- buf = f.read()\r
- f.close()\r
- i = buf.find('### ''PROLOGUE')\r
- j = buf.find('### ''EPILOGUE')\r
- pro = buf[i+12:j].strip()\r
- epi = buf[j+12:].strip()\r
- return pro, epi\r
-\r
-def strip_default(arg):\r
- """Return the argname from an 'arg = default' string"""\r
- i = arg.find('=')\r
- if i == -1:\r
- return arg\r
- t = arg[:i].strip()\r
- return t\r
-\r
-P_NODE = 1\r
-P_OTHER = 2\r
-P_NESTED = 3\r
-P_NONE = 4\r
-\r
-class NodeInfo:\r
- """Each instance describes a specific AST node"""\r
- def __init__(self, name, args):\r
- self.name = name\r
- self.args = args.strip()\r
- self.argnames = self.get_argnames()\r
- self.argprops = self.get_argprops()\r
- self.nargs = len(self.argnames)\r
- self.init = []\r
-\r
- def get_argnames(self):\r
- if '(' in self.args:\r
- i = self.args.find('(')\r
- j = self.args.rfind(')')\r
- args = self.args[i+1:j]\r
- else:\r
- args = self.args\r
- return [strip_default(arg.strip())\r
- for arg in args.split(',') if arg]\r
-\r
- def get_argprops(self):\r
- """Each argument can have a property like '*' or '!'\r
-\r
- XXX This method modifies the argnames in place!\r
- """\r
- d = {}\r
- hardest_arg = P_NODE\r
- for i in range(len(self.argnames)):\r
- arg = self.argnames[i]\r
- if arg.endswith('*'):\r
- arg = self.argnames[i] = arg[:-1]\r
- d[arg] = P_OTHER\r
- hardest_arg = max(hardest_arg, P_OTHER)\r
- elif arg.endswith('!'):\r
- arg = self.argnames[i] = arg[:-1]\r
- d[arg] = P_NESTED\r
- hardest_arg = max(hardest_arg, P_NESTED)\r
- elif arg.endswith('&'):\r
- arg = self.argnames[i] = arg[:-1]\r
- d[arg] = P_NONE\r
- hardest_arg = max(hardest_arg, P_NONE)\r
- else:\r
- d[arg] = P_NODE\r
- self.hardest_arg = hardest_arg\r
-\r
- if hardest_arg > P_NODE:\r
- self.args = self.args.replace('*', '')\r
- self.args = self.args.replace('!', '')\r
- self.args = self.args.replace('&', '')\r
-\r
- return d\r
-\r
- def gen_source(self):\r
- buf = StringIO()\r
- print >> buf, "class %s(Node):" % self.name\r
- self._gen_init(buf)\r
- print >> buf\r
- self._gen_getChildren(buf)\r
- print >> buf\r
- self._gen_getChildNodes(buf)\r
- print >> buf\r
- self._gen_repr(buf)\r
- buf.seek(0, 0)\r
- return buf.read()\r
-\r
- def _gen_init(self, buf):\r
- if self.args:\r
- argtuple = '(' in self.args\r
- args = self.args if not argtuple else ''.join(self.argnames)\r
- print >> buf, " def __init__(self, %s, lineno=None):" % args\r
- else:\r
- print >> buf, " def __init__(self, lineno=None):"\r
- if self.argnames:\r
- if argtuple:\r
- for idx, name in enumerate(self.argnames):\r
- print >> buf, " self.%s = %s[%s]" % (name, args, idx)\r
- else:\r
- for name in self.argnames:\r
- print >> buf, " self.%s = %s" % (name, name)\r
- print >> buf, " self.lineno = lineno"\r
- # Copy the lines in self.init, indented four spaces. The rstrip()\r
- # business is to get rid of the four spaces if line happens to be\r
- # empty, so that reindent.py is happy with the output.\r
- for line in self.init:\r
- print >> buf, (" " + line).rstrip()\r
-\r
- def _gen_getChildren(self, buf):\r
- print >> buf, " def getChildren(self):"\r
- if len(self.argnames) == 0:\r
- print >> buf, " return ()"\r
- else:\r
- if self.hardest_arg < P_NESTED:\r
- clist = COMMA.join(["self.%s" % c\r
- for c in self.argnames])\r
- if self.nargs == 1:\r
- print >> buf, " return %s," % clist\r
- else:\r
- print >> buf, " return %s" % clist\r
- else:\r
- if len(self.argnames) == 1:\r
- print >> buf, " return tuple(flatten(self.%s))" % self.argnames[0]\r
- else:\r
- print >> buf, " children = []"\r
- template = " children.%s(%sself.%s%s)"\r
- for name in self.argnames:\r
- if self.argprops[name] == P_NESTED:\r
- print >> buf, template % ("extend", "flatten(",\r
- name, ")")\r
- else:\r
- print >> buf, template % ("append", "", name, "")\r
- print >> buf, " return tuple(children)"\r
-\r
- def _gen_getChildNodes(self, buf):\r
- print >> buf, " def getChildNodes(self):"\r
- if len(self.argnames) == 0:\r
- print >> buf, " return ()"\r
- else:\r
- if self.hardest_arg < P_NESTED:\r
- clist = ["self.%s" % c\r
- for c in self.argnames\r
- if self.argprops[c] == P_NODE]\r
- if len(clist) == 0:\r
- print >> buf, " return ()"\r
- elif len(clist) == 1:\r
- print >> buf, " return %s," % clist[0]\r
- else:\r
- print >> buf, " return %s" % COMMA.join(clist)\r
- else:\r
- print >> buf, " nodelist = []"\r
- template = " nodelist.%s(%sself.%s%s)"\r
- for name in self.argnames:\r
- if self.argprops[name] == P_NONE:\r
- tmp = (" if self.%s is not None:\n"\r
- " nodelist.append(self.%s)")\r
- print >> buf, tmp % (name, name)\r
- elif self.argprops[name] == P_NESTED:\r
- print >> buf, template % ("extend", "flatten_nodes(",\r
- name, ")")\r
- elif self.argprops[name] == P_NODE:\r
- print >> buf, template % ("append", "", name, "")\r
- print >> buf, " return tuple(nodelist)"\r
-\r
- def _gen_repr(self, buf):\r
- print >> buf, " def __repr__(self):"\r
- if self.argnames:\r
- fmt = COMMA.join(["%s"] * self.nargs)\r
- if '(' in self.args:\r
- fmt = '(%s)' % fmt\r
- vals = ["repr(self.%s)" % name for name in self.argnames]\r
- vals = COMMA.join(vals)\r
- if self.nargs == 1:\r
- vals = vals + ","\r
- print >> buf, ' return "%s(%s)" %% (%s)' % \\r
- (self.name, fmt, vals)\r
- else:\r
- print >> buf, ' return "%s()"' % self.name\r
-\r
-rx_init = re.compile('init\((.*)\):')\r
-\r
-def parse_spec(file):\r
- classes = {}\r
- cur = None\r
- for line in fileinput.input(file):\r
- if line.strip().startswith('#'):\r
- continue\r
- mo = rx_init.search(line)\r
- if mo is None:\r
- if cur is None:\r
- # a normal entry\r
- try:\r
- name, args = line.split(':')\r
- except ValueError:\r
- continue\r
- classes[name] = NodeInfo(name, args)\r
- cur = None\r
- else:\r
- # some code for the __init__ method\r
- cur.init.append(line)\r
- else:\r
- # some extra code for a Node's __init__ method\r
- name = mo.group(1)\r
- cur = classes[name]\r
- return sorted(classes.values(), key=lambda n: n.name)\r
-\r
-def main():\r
- prologue, epilogue = load_boilerplate(sys.argv[-1])\r
- print prologue\r
- print\r
- classes = parse_spec(SPEC)\r
- for info in classes:\r
- print info.gen_source()\r
- print epilogue\r
-\r
-if __name__ == "__main__":\r
- main()\r
- sys.exit(0)\r
-\r
-### PROLOGUE\r
-"""Python abstract syntax node definitions\r
-\r
-This file is automatically generated by Tools/compiler/astgen.py\r
-"""\r
-from consts import CO_VARARGS, CO_VARKEYWORDS\r
-\r
-def flatten(seq):\r
- l = []\r
- for elt in seq:\r
- t = type(elt)\r
- if t is tuple or t is list:\r
- for elt2 in flatten(elt):\r
- l.append(elt2)\r
- else:\r
- l.append(elt)\r
- return l\r
-\r
-def flatten_nodes(seq):\r
- return [n for n in flatten(seq) if isinstance(n, Node)]\r
-\r
-nodes = {}\r
-\r
-class Node:\r
- """Abstract base class for ast nodes."""\r
- def getChildren(self):\r
- pass # implemented by subclasses\r
- def __iter__(self):\r
- for n in self.getChildren():\r
- yield n\r
- def asList(self): # for backwards compatibility\r
- return self.getChildren()\r
- def getChildNodes(self):\r
- pass # implemented by subclasses\r
-\r
-class EmptyNode(Node):\r
- pass\r
-\r
-class Expression(Node):\r
- # Expression is an artificial node class to support "eval"\r
- nodes["expression"] = "Expression"\r
- def __init__(self, node):\r
- self.node = node\r
-\r
- def getChildren(self):\r
- return self.node,\r
-\r
- def getChildNodes(self):\r
- return self.node,\r
-\r
- def __repr__(self):\r
- return "Expression(%s)" % (repr(self.node))\r
-\r
-### EPILOGUE\r
-for name, obj in globals().items():\r
- if isinstance(obj, type) and issubclass(obj, Node):\r
- nodes[name.lower()] = obj\r