mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			182 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""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()))
 | 
						|
 | 
						|
class EmptyNode(Node):
 | 
						|
    def __init__(self):
 | 
						|
        self.lineno = None
 | 
						|
 | 
						|
### EPILOGUE
 | 
						|
klasses = globals()
 | 
						|
for k in nodes.keys():
 | 
						|
    nodes[k] = klasses[nodes[k]]
 |