summaryrefslogtreecommitdiffstats
path: root/Tools/compiler/astgen.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/compiler/astgen.py')
-rw-r--r--Tools/compiler/astgen.py130
1 files changed, 112 insertions, 18 deletions
diff --git a/Tools/compiler/astgen.py b/Tools/compiler/astgen.py
index c0eb464..245eebd 100644
--- a/Tools/compiler/astgen.py
+++ b/Tools/compiler/astgen.py
@@ -1,4 +1,11 @@
-"""Generate ast module from specification"""
+"""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
@@ -24,7 +31,13 @@ def strip_default(arg):
i = arg.find('=')
if i == -1:
return arg
- return arg[:i].strip()
+ 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"""
@@ -32,9 +45,8 @@ class NodeInfo:
self.name = name
self.args = args.strip()
self.argnames = self.get_argnames()
+ self.argprops = self.get_argprops()
self.nargs = len(self.argnames)
- self.children = COMMA.join(["self.%s" % c
- for c in self.argnames])
self.init = []
def get_argnames(self):
@@ -47,12 +59,48 @@ class NodeInfo:
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 = P_OTHER
+ elif arg.endswith('!'):
+ arg = self.argnames[i] = arg[:-1]
+ d[arg] = P_NESTED
+ hardest_arg = P_NESTED
+ elif arg.endswith('&'):
+ arg = self.argnames[i] = arg[:-1]
+ d[arg] = P_NONE
+ 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()
@@ -68,14 +116,57 @@ class NodeInfo:
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
+ 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):"
@@ -98,6 +189,8 @@ 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:
@@ -149,6 +242,9 @@ def flatten(list):
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:
@@ -164,21 +260,19 @@ def asList(nodes):
nodes = {}
-class Node:
- lineno = None
+class Node: # an abstract base class
+ lineno = None # provide a lineno for nodes that don't have one
def getType(self):
- pass
+ pass # implemented by subclass
def getChildren(self):
- # XXX It would be better to generate flat values to begin with
- return flatten(self._getChildren())
+ pass # implemented by subclasses
def asList(self):
return tuple(asList(self.getChildren()))
def getChildNodes(self):
- return [n for n in self.getChildren() if isinstance(n, Node)]
+ pass # implemented by subclasses
class EmptyNode(Node):
- def __init__(self):
- self.lineno = None
+ pass
### EPILOGUE
klasses = globals()