blob: c431d226b9d5f202881029a55358f35aa2a953a8 [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
Jeremy Hylton821eee32000-10-25 17:59:17 +000011import re
12import sys
13from StringIO import StringIO
14
15SPEC = "ast.txt"
16COMMA = ", "
17
18def load_boilerplate(file):
19 f = open(file)
20 buf = f.read()
21 f.close()
22 i = buf.find('### ''PROLOGUE')
23 j = buf.find('### ''EPILOGUE')
24 pro = buf[i+12:j].strip()
25 epi = buf[j+12:].strip()
26 return pro, epi
27
28def strip_default(arg):
29 """Return the argname from an 'arg = default' string"""
30 i = arg.find('=')
31 if i == -1:
32 return arg
Jeremy Hylton5477f522001-08-29 18:08:02 +000033 t = arg[:i].strip()
34 return t
35
36P_NODE = 1
37P_OTHER = 2
38P_NESTED = 3
39P_NONE = 4
Jeremy Hylton821eee32000-10-25 17:59:17 +000040
41class NodeInfo:
42 """Each instance describes a specific AST node"""
43 def __init__(self, name, args):
44 self.name = name
45 self.args = args.strip()
46 self.argnames = self.get_argnames()
Jeremy Hylton5477f522001-08-29 18:08:02 +000047 self.argprops = self.get_argprops()
Jeremy Hylton821eee32000-10-25 17:59:17 +000048 self.nargs = len(self.argnames)
Jeremy Hylton821eee32000-10-25 17:59:17 +000049 self.init = []
50
51 def get_argnames(self):
52 if '(' in self.args:
53 i = self.args.find('(')
54 j = self.args.rfind(')')
55 args = self.args[i+1:j]
56 else:
57 args = self.args
58 return [strip_default(arg.strip())
59 for arg in args.split(',') if arg]
60
Jeremy Hylton5477f522001-08-29 18:08:02 +000061 def get_argprops(self):
62 """Each argument can have a property like '*' or '!'
63
64 XXX This method modifies the argnames in place!
65 """
66 d = {}
67 hardest_arg = P_NODE
68 for i in range(len(self.argnames)):
69 arg = self.argnames[i]
70 if arg.endswith('*'):
71 arg = self.argnames[i] = arg[:-1]
72 d[arg] = P_OTHER
Jeremy Hyltoneab43282001-09-17 20:16:30 +000073 hardest_arg = max(hardest_arg, P_OTHER)
Jeremy Hylton5477f522001-08-29 18:08:02 +000074 elif arg.endswith('!'):
75 arg = self.argnames[i] = arg[:-1]
76 d[arg] = P_NESTED
Jeremy Hyltoneab43282001-09-17 20:16:30 +000077 hardest_arg = max(hardest_arg, P_NESTED)
Jeremy Hylton5477f522001-08-29 18:08:02 +000078 elif arg.endswith('&'):
79 arg = self.argnames[i] = arg[:-1]
80 d[arg] = P_NONE
Jeremy Hyltoneab43282001-09-17 20:16:30 +000081 hardest_arg = max(hardest_arg, P_NONE)
Jeremy Hylton5477f522001-08-29 18:08:02 +000082 else:
83 d[arg] = P_NODE
84 self.hardest_arg = hardest_arg
85
86 if hardest_arg > P_NODE:
87 self.args = self.args.replace('*', '')
88 self.args = self.args.replace('!', '')
89 self.args = self.args.replace('&', '')
Tim Peters182b5ac2004-07-18 06:16:08 +000090
Jeremy Hylton5477f522001-08-29 18:08:02 +000091 return d
92
Jeremy Hylton821eee32000-10-25 17:59:17 +000093 def gen_source(self):
94 buf = StringIO()
95 print >> buf, "class %s(Node):" % self.name
Jeremy Hylton821eee32000-10-25 17:59:17 +000096 self._gen_init(buf)
Jeremy Hylton5477f522001-08-29 18:08:02 +000097 print >> buf
Jeremy Hylton821eee32000-10-25 17:59:17 +000098 self._gen_getChildren(buf)
Jeremy Hylton5477f522001-08-29 18:08:02 +000099 print >> buf
100 self._gen_getChildNodes(buf)
101 print >> buf
Jeremy Hylton821eee32000-10-25 17:59:17 +0000102 self._gen_repr(buf)
103 buf.seek(0, 0)
104 return buf.read()
105
106 def _gen_init(self, buf):
Jeremy Hylton566d9342004-09-07 15:28:01 +0000107 if self.args:
108 print >> buf, " def __init__(self, %s, lineno=None):" % self.args
109 else:
110 print >> buf, " def __init__(self, lineno=None):"
Jeremy Hylton821eee32000-10-25 17:59:17 +0000111 if self.argnames:
112 for name in self.argnames:
113 print >> buf, " self.%s = %s" % (name, name)
Jeremy Hylton566d9342004-09-07 15:28:01 +0000114 print >> buf, " self.lineno = lineno"
Tim Petersca4d08b2006-03-09 22:31:45 +0000115 # Copy the lines in self.init, indented four spaces. The rstrip()
116 # business is to get rid of the four spaces if line happens to be
117 # empty, so that reindent.py is happy with the output.
118 for line in self.init:
119 print >> buf, (" " + line).rstrip()
Jeremy Hylton821eee32000-10-25 17:59:17 +0000120
121 def _gen_getChildren(self, buf):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000122 print >> buf, " def getChildren(self):"
123 if len(self.argnames) == 0:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000124 print >> buf, " return ()"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000125 else:
126 if self.hardest_arg < P_NESTED:
127 clist = COMMA.join(["self.%s" % c
128 for c in self.argnames])
129 if self.nargs == 1:
130 print >> buf, " return %s," % clist
131 else:
132 print >> buf, " return %s" % clist
133 else:
Jeremy Hylton566d9342004-09-07 15:28:01 +0000134 if len(self.argnames) == 1:
135 print >> buf, " return tuple(flatten(self.%s))" % self.argnames[0]
136 else:
137 print >> buf, " children = []"
138 template = " children.%s(%sself.%s%s)"
139 for name in self.argnames:
140 if self.argprops[name] == P_NESTED:
141 print >> buf, template % ("extend", "flatten(",
142 name, ")")
143 else:
144 print >> buf, template % ("append", "", name, "")
145 print >> buf, " return tuple(children)"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000146
147 def _gen_getChildNodes(self, buf):
148 print >> buf, " def getChildNodes(self):"
149 if len(self.argnames) == 0:
150 print >> buf, " return ()"
151 else:
152 if self.hardest_arg < P_NESTED:
153 clist = ["self.%s" % c
154 for c in self.argnames
155 if self.argprops[c] == P_NODE]
156 if len(clist) == 0:
157 print >> buf, " return ()"
158 elif len(clist) == 1:
159 print >> buf, " return %s," % clist[0]
160 else:
161 print >> buf, " return %s" % COMMA.join(clist)
162 else:
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000163 print >> buf, " nodelist = []"
164 template = " nodelist.%s(%sself.%s%s)"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000165 for name in self.argnames:
166 if self.argprops[name] == P_NONE:
Jeremy Hylton566d9342004-09-07 15:28:01 +0000167 tmp = (" if self.%s is not None:\n"
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000168 " nodelist.append(self.%s)")
Jeremy Hylton5477f522001-08-29 18:08:02 +0000169 print >> buf, tmp % (name, name)
170 elif self.argprops[name] == P_NESTED:
171 print >> buf, template % ("extend", "flatten_nodes(",
172 name, ")")
173 elif self.argprops[name] == P_NODE:
174 print >> buf, template % ("append", "", name, "")
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000175 print >> buf, " return tuple(nodelist)"
Jeremy Hylton821eee32000-10-25 17:59:17 +0000176
177 def _gen_repr(self, buf):
178 print >> buf, " def __repr__(self):"
179 if self.argnames:
180 fmt = COMMA.join(["%s"] * self.nargs)
Jeremy Hyltonab427b82001-08-18 00:14:37 +0000181 if '(' in self.args:
182 fmt = '(%s)' % fmt
Jeremy Hylton821eee32000-10-25 17:59:17 +0000183 vals = ["repr(self.%s)" % name for name in self.argnames]
184 vals = COMMA.join(vals)
185 if self.nargs == 1:
186 vals = vals + ","
187 print >> buf, ' return "%s(%s)" %% (%s)' % \
188 (self.name, fmt, vals)
189 else:
190 print >> buf, ' return "%s()"' % self.name
191
192rx_init = re.compile('init\((.*)\):')
193
194def parse_spec(file):
195 classes = {}
196 cur = None
197 for line in fileinput.input(file):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000198 if line.strip().startswith('#'):
199 continue
Jeremy Hylton821eee32000-10-25 17:59:17 +0000200 mo = rx_init.search(line)
201 if mo is None:
202 if cur is None:
203 # a normal entry
204 try:
205 name, args = line.split(':')
206 except ValueError:
207 continue
208 classes[name] = NodeInfo(name, args)
209 cur = None
210 else:
211 # some code for the __init__ method
212 cur.init.append(line)
213 else:
214 # some extra code for a Node's __init__ method
215 name = mo.group(1)
216 cur = classes[name]
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000217 return sorted(classes.values(), key=lambda n: n.name)
Jeremy Hylton821eee32000-10-25 17:59:17 +0000218
219def main():
220 prologue, epilogue = load_boilerplate(sys.argv[-1])
221 print prologue
222 print
223 classes = parse_spec(SPEC)
224 for info in classes:
225 print info.gen_source()
226 print epilogue
227
228if __name__ == "__main__":
229 main()
230 sys.exit(0)
231
232### PROLOGUE
233"""Python abstract syntax node definitions
234
Jeremy Hylton566d9342004-09-07 15:28:01 +0000235This file is automatically generated by Tools/compiler/astgen.py
Jeremy Hylton821eee32000-10-25 17:59:17 +0000236"""
Jeremy Hylton821eee32000-10-25 17:59:17 +0000237from consts import CO_VARARGS, CO_VARKEYWORDS
238
Neil Schemenauerf3694702005-06-02 05:55:20 +0000239def flatten(seq):
Jeremy Hylton821eee32000-10-25 17:59:17 +0000240 l = []
Neil Schemenauerf3694702005-06-02 05:55:20 +0000241 for elt in seq:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000242 t = type(elt)
Jeremy Hylton566d9342004-09-07 15:28:01 +0000243 if t is tuple or t is list:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000244 for elt2 in flatten(elt):
245 l.append(elt2)
246 else:
247 l.append(elt)
248 return l
249
Neil Schemenauerf3694702005-06-02 05:55:20 +0000250def flatten_nodes(seq):
251 return [n for n in flatten(seq) if isinstance(n, Node)]
Jeremy Hylton5477f522001-08-29 18:08:02 +0000252
Jeremy Hylton821eee32000-10-25 17:59:17 +0000253nodes = {}
254
Jeremy Hylton566d9342004-09-07 15:28:01 +0000255class Node:
256 """Abstract base class for ast nodes."""
Jeremy Hylton821eee32000-10-25 17:59:17 +0000257 def getChildren(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000258 pass # implemented by subclasses
Jeremy Hylton566d9342004-09-07 15:28:01 +0000259 def __iter__(self):
260 for n in self.getChildren():
261 yield n
262 def asList(self): # for backwards compatibility
263 return self.getChildren()
Jeremy Hyltoneef65902001-08-14 18:58:00 +0000264 def getChildNodes(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000265 pass # implemented by subclasses
Jeremy Hylton821eee32000-10-25 17:59:17 +0000266
267class EmptyNode(Node):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000268 pass
Jeremy Hylton821eee32000-10-25 17:59:17 +0000269
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000270class Expression(Node):
271 # Expression is an artificial node class to support "eval"
272 nodes["expression"] = "Expression"
273 def __init__(self, node):
274 self.node = node
275
276 def getChildren(self):
277 return self.node,
278
279 def getChildNodes(self):
280 return self.node,
281
282 def __repr__(self):
283 return "Expression(%s)" % (repr(self.node))
284
Jeremy Hylton821eee32000-10-25 17:59:17 +0000285### EPILOGUE
Jeremy Hylton566d9342004-09-07 15:28:01 +0000286for name, obj in globals().items():
287 if isinstance(obj, type) and issubclass(obj, Node):
Tim Peters0e9980f2004-09-12 03:49:31 +0000288 nodes[name.lower()] = obj