| """Generate ast module from specification""" |
| |
| 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 |
| return arg[:i].strip() |
| |
| 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.nargs = len(self.argnames) |
| self.children = COMMA.join(["self.%s" % c |
| for c in 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 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) |
| self._gen_getChildren(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 self.argnames: |
| if self.nargs == 1: |
| print >> buf, " return %s," % self.children |
| else: |
| print >> buf, " return %s" % self.children |
| else: |
| print >> buf, " return ()" |
| |
| def _gen_repr(self, buf): |
| print >> buf, " def __repr__(self):" |
| if self.argnames: |
| fmt = COMMA.join(["%s"] * self.nargs) |
| 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): |
| 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 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: |
| lineno = None |
| def getType(self): |
| pass |
| def getChildren(self): |
| # XXX It would be better to generate flat values to begin with |
| return flatten(self._getChildren()) |
| def asList(self): |
| return tuple(asList(self.getChildren())) |
| def getChildNodes(self): |
| return [n for n in self.getChildren() if isinstance(n, Node)] |
| |
| class EmptyNode(Node): |
| def __init__(self): |
| self.lineno = None |
| |
| ### EPILOGUE |
| klasses = globals() |
| for k in nodes.keys(): |
| nodes[k] = klasses[nodes[k]] |