blob: 59b98f2b0a186f562fbe729ea9b35d19dc3ac6d4 [file] [log] [blame]
Jeremy Hylton5477f522001-08-29 18:08:02 +00001"""Generate ast module from specification
2
3This script generates the ast module from a simple specification,
4which makes it easy to accomodate changes in the grammar. This
5approach would be quite reasonable if the grammar changed often.
6Instead, it is rather complex to generate the appropriate code. And
7the Node interface has changed more often than the grammar.
8"""
Jeremy Hylton821eee32000-10-25 17:59:17 +00009
10import fileinput
11import getopt
12import re
13import sys
14from StringIO import StringIO
15
16SPEC = "ast.txt"
17COMMA = ", "
18
19def load_boilerplate(file):
20 f = open(file)
21 buf = f.read()
22 f.close()
23 i = buf.find('### ''PROLOGUE')
24 j = buf.find('### ''EPILOGUE')
25 pro = buf[i+12:j].strip()
26 epi = buf[j+12:].strip()
27 return pro, epi
28
29def strip_default(arg):
30 """Return the argname from an 'arg = default' string"""
31 i = arg.find('=')
32 if i == -1:
33 return arg
Jeremy Hylton5477f522001-08-29 18:08:02 +000034 t = arg[:i].strip()
35 return t
36
37P_NODE = 1
38P_OTHER = 2
39P_NESTED = 3
40P_NONE = 4
Jeremy Hylton821eee32000-10-25 17:59:17 +000041
42class NodeInfo:
43 """Each instance describes a specific AST node"""
44 def __init__(self, name, args):
45 self.name = name
46 self.args = args.strip()
47 self.argnames = self.get_argnames()
Jeremy Hylton5477f522001-08-29 18:08:02 +000048 self.argprops = self.get_argprops()
Jeremy Hylton821eee32000-10-25 17:59:17 +000049 self.nargs = len(self.argnames)
Jeremy Hylton821eee32000-10-25 17:59:17 +000050 self.init = []
51
52 def get_argnames(self):
53 if '(' in self.args:
54 i = self.args.find('(')
55 j = self.args.rfind(')')
56 args = self.args[i+1:j]
57 else:
58 args = self.args
59 return [strip_default(arg.strip())
60 for arg in args.split(',') if arg]
61
Jeremy Hylton5477f522001-08-29 18:08:02 +000062 def get_argprops(self):
63 """Each argument can have a property like '*' or '!'
64
65 XXX This method modifies the argnames in place!
66 """
67 d = {}
68 hardest_arg = P_NODE
69 for i in range(len(self.argnames)):
70 arg = self.argnames[i]
71 if arg.endswith('*'):
72 arg = self.argnames[i] = arg[:-1]
73 d[arg] = P_OTHER
Jeremy Hyltoneab43282001-09-17 20:16:30 +000074 hardest_arg = max(hardest_arg, P_OTHER)
Jeremy Hylton5477f522001-08-29 18:08:02 +000075 elif arg.endswith('!'):
76 arg = self.argnames[i] = arg[:-1]
77 d[arg] = P_NESTED
Jeremy Hyltoneab43282001-09-17 20:16:30 +000078 hardest_arg = max(hardest_arg, P_NESTED)
Jeremy Hylton5477f522001-08-29 18:08:02 +000079 elif arg.endswith('&'):
80 arg = self.argnames[i] = arg[:-1]
81 d[arg] = P_NONE
Jeremy Hyltoneab43282001-09-17 20:16:30 +000082 hardest_arg = max(hardest_arg, P_NONE)
Jeremy Hylton5477f522001-08-29 18:08:02 +000083 else:
84 d[arg] = P_NODE
85 self.hardest_arg = hardest_arg
86
87 if hardest_arg > P_NODE:
88 self.args = self.args.replace('*', '')
89 self.args = self.args.replace('!', '')
90 self.args = self.args.replace('&', '')
Tim Peters182b5ac2004-07-18 06:16:08 +000091
Jeremy Hylton5477f522001-08-29 18:08:02 +000092 return d
93
Jeremy Hylton821eee32000-10-25 17:59:17 +000094 def gen_source(self):
95 buf = StringIO()
96 print >> buf, "class %s(Node):" % self.name
Jeremy Hylton821eee32000-10-25 17:59:17 +000097 self._gen_init(buf)
Jeremy Hylton5477f522001-08-29 18:08:02 +000098 print >> buf
Jeremy Hylton821eee32000-10-25 17:59:17 +000099 self._gen_getChildren(buf)
Jeremy Hylton5477f522001-08-29 18:08:02 +0000100 print >> buf
101 self._gen_getChildNodes(buf)
102 print >> buf
Jeremy Hylton821eee32000-10-25 17:59:17 +0000103 self._gen_repr(buf)
104 buf.seek(0, 0)
105 return buf.read()
106
107 def _gen_init(self, buf):
Jeremy Hylton566d9342004-09-07 15:28:01 +0000108 if self.args:
109 print >> buf, " def __init__(self, %s, lineno=None):" % self.args
110 else:
111 print >> buf, " def __init__(self, lineno=None):"
Jeremy Hylton821eee32000-10-25 17:59:17 +0000112 if self.argnames:
113 for name in self.argnames:
114 print >> buf, " self.%s = %s" % (name, name)
Jeremy Hylton566d9342004-09-07 15:28:01 +0000115 print >> buf, " self.lineno = lineno"
Tim Petersca4d08b2006-03-09 22:31:45 +0000116 # Copy the lines in self.init, indented four spaces. The rstrip()
117 # business is to get rid of the four spaces if line happens to be
118 # empty, so that reindent.py is happy with the output.
119 for line in self.init:
120 print >> buf, (" " + line).rstrip()
Jeremy Hylton821eee32000-10-25 17:59:17 +0000121
122 def _gen_getChildren(self, buf):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000123 print >> buf, " def getChildren(self):"
124 if len(self.argnames) == 0:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000125 print >> buf, " return ()"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000126 else:
127 if self.hardest_arg < P_NESTED:
128 clist = COMMA.join(["self.%s" % c
129 for c in self.argnames])
130 if self.nargs == 1:
131 print >> buf, " return %s," % clist
132 else:
133 print >> buf, " return %s" % clist
134 else:
Jeremy Hylton566d9342004-09-07 15:28:01 +0000135 if len(self.argnames) == 1:
136 print >> buf, " return tuple(flatten(self.%s))" % self.argnames[0]
137 else:
138 print >> buf, " children = []"
139 template = " children.%s(%sself.%s%s)"
140 for name in self.argnames:
141 if self.argprops[name] == P_NESTED:
142 print >> buf, template % ("extend", "flatten(",
143 name, ")")
144 else:
145 print >> buf, template % ("append", "", name, "")
146 print >> buf, " return tuple(children)"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000147
148 def _gen_getChildNodes(self, buf):
149 print >> buf, " def getChildNodes(self):"
150 if len(self.argnames) == 0:
151 print >> buf, " return ()"
152 else:
153 if self.hardest_arg < P_NESTED:
154 clist = ["self.%s" % c
155 for c in self.argnames
156 if self.argprops[c] == P_NODE]
157 if len(clist) == 0:
158 print >> buf, " return ()"
159 elif len(clist) == 1:
160 print >> buf, " return %s," % clist[0]
161 else:
162 print >> buf, " return %s" % COMMA.join(clist)
163 else:
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000164 print >> buf, " nodelist = []"
165 template = " nodelist.%s(%sself.%s%s)"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000166 for name in self.argnames:
167 if self.argprops[name] == P_NONE:
Jeremy Hylton566d9342004-09-07 15:28:01 +0000168 tmp = (" if self.%s is not None:\n"
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000169 " nodelist.append(self.%s)")
Jeremy Hylton5477f522001-08-29 18:08:02 +0000170 print >> buf, tmp % (name, name)
171 elif self.argprops[name] == P_NESTED:
172 print >> buf, template % ("extend", "flatten_nodes(",
173 name, ")")
174 elif self.argprops[name] == P_NODE:
175 print >> buf, template % ("append", "", name, "")
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000176 print >> buf, " return tuple(nodelist)"
Jeremy Hylton821eee32000-10-25 17:59:17 +0000177
178 def _gen_repr(self, buf):
179 print >> buf, " def __repr__(self):"
180 if self.argnames:
181 fmt = COMMA.join(["%s"] * self.nargs)
Jeremy Hyltonab427b82001-08-18 00:14:37 +0000182 if '(' in self.args:
183 fmt = '(%s)' % fmt
Jeremy Hylton821eee32000-10-25 17:59:17 +0000184 vals = ["repr(self.%s)" % name for name in self.argnames]
185 vals = COMMA.join(vals)
186 if self.nargs == 1:
187 vals = vals + ","
188 print >> buf, ' return "%s(%s)" %% (%s)' % \
189 (self.name, fmt, vals)
190 else:
191 print >> buf, ' return "%s()"' % self.name
192
193rx_init = re.compile('init\((.*)\):')
194
195def parse_spec(file):
196 classes = {}
197 cur = None
198 for line in fileinput.input(file):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000199 if line.strip().startswith('#'):
200 continue
Jeremy Hylton821eee32000-10-25 17:59:17 +0000201 mo = rx_init.search(line)
202 if mo is None:
203 if cur is None:
204 # a normal entry
205 try:
206 name, args = line.split(':')
207 except ValueError:
208 continue
209 classes[name] = NodeInfo(name, args)
210 cur = None
211 else:
212 # some code for the __init__ method
213 cur.init.append(line)
214 else:
215 # some extra code for a Node's __init__ method
216 name = mo.group(1)
217 cur = classes[name]
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000218 return sorted(classes.values(), key=lambda n: n.name)
Jeremy Hylton821eee32000-10-25 17:59:17 +0000219
220def main():
221 prologue, epilogue = load_boilerplate(sys.argv[-1])
222 print prologue
223 print
224 classes = parse_spec(SPEC)
225 for info in classes:
226 print info.gen_source()
227 print epilogue
228
229if __name__ == "__main__":
230 main()
231 sys.exit(0)
232
233### PROLOGUE
234"""Python abstract syntax node definitions
235
Jeremy Hylton566d9342004-09-07 15:28:01 +0000236This file is automatically generated by Tools/compiler/astgen.py
Jeremy Hylton821eee32000-10-25 17:59:17 +0000237"""
Jeremy Hylton821eee32000-10-25 17:59:17 +0000238from consts import CO_VARARGS, CO_VARKEYWORDS
239
Neil Schemenauerf3694702005-06-02 05:55:20 +0000240def flatten(seq):
Jeremy Hylton821eee32000-10-25 17:59:17 +0000241 l = []
Neil Schemenauerf3694702005-06-02 05:55:20 +0000242 for elt in seq:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000243 t = type(elt)
Jeremy Hylton566d9342004-09-07 15:28:01 +0000244 if t is tuple or t is list:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000245 for elt2 in flatten(elt):
246 l.append(elt2)
247 else:
248 l.append(elt)
249 return l
250
Neil Schemenauerf3694702005-06-02 05:55:20 +0000251def flatten_nodes(seq):
252 return [n for n in flatten(seq) if isinstance(n, Node)]
Jeremy Hylton5477f522001-08-29 18:08:02 +0000253
Jeremy Hylton821eee32000-10-25 17:59:17 +0000254nodes = {}
255
Jeremy Hylton566d9342004-09-07 15:28:01 +0000256class Node:
257 """Abstract base class for ast nodes."""
Jeremy Hylton821eee32000-10-25 17:59:17 +0000258 def getChildren(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000259 pass # implemented by subclasses
Jeremy Hylton566d9342004-09-07 15:28:01 +0000260 def __iter__(self):
261 for n in self.getChildren():
262 yield n
263 def asList(self): # for backwards compatibility
264 return self.getChildren()
Jeremy Hyltoneef65902001-08-14 18:58:00 +0000265 def getChildNodes(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000266 pass # implemented by subclasses
Jeremy Hylton821eee32000-10-25 17:59:17 +0000267
268class EmptyNode(Node):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000269 pass
Jeremy Hylton821eee32000-10-25 17:59:17 +0000270
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000271class Expression(Node):
272 # Expression is an artificial node class to support "eval"
273 nodes["expression"] = "Expression"
274 def __init__(self, node):
275 self.node = node
276
277 def getChildren(self):
278 return self.node,
279
280 def getChildNodes(self):
281 return self.node,
282
283 def __repr__(self):
284 return "Expression(%s)" % (repr(self.node))
285
Jeremy Hylton821eee32000-10-25 17:59:17 +0000286### EPILOGUE
Jeremy Hylton566d9342004-09-07 15:28:01 +0000287for name, obj in globals().items():
288 if isinstance(obj, type) and issubclass(obj, Node):
Tim Peters0e9980f2004-09-12 03:49:31 +0000289 nodes[name.lower()] = obj