blob: 1724276add7cb01e0c2c02e00690d18544141128 [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:
Antoine Pitroub9d49632010-01-04 23:22:44 +0000108 argtuple = '(' in self.args
109 args = self.args if not argtuple else ''.join(self.argnames)
110 print >> buf, " def __init__(self, %s, lineno=None):" % args
Jeremy Hylton566d9342004-09-07 15:28:01 +0000111 else:
112 print >> buf, " def __init__(self, lineno=None):"
Jeremy Hylton821eee32000-10-25 17:59:17 +0000113 if self.argnames:
Antoine Pitroub9d49632010-01-04 23:22:44 +0000114 if argtuple:
115 for idx, name in enumerate(self.argnames):
116 print >> buf, " self.%s = %s[%s]" % (name, args, idx)
117 else:
118 for name in self.argnames:
119 print >> buf, " self.%s = %s" % (name, name)
Jeremy Hylton566d9342004-09-07 15:28:01 +0000120 print >> buf, " self.lineno = lineno"
Tim Petersca4d08b2006-03-09 22:31:45 +0000121 # Copy the lines in self.init, indented four spaces. The rstrip()
122 # business is to get rid of the four spaces if line happens to be
123 # empty, so that reindent.py is happy with the output.
124 for line in self.init:
125 print >> buf, (" " + line).rstrip()
Jeremy Hylton821eee32000-10-25 17:59:17 +0000126
127 def _gen_getChildren(self, buf):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000128 print >> buf, " def getChildren(self):"
129 if len(self.argnames) == 0:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000130 print >> buf, " return ()"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000131 else:
132 if self.hardest_arg < P_NESTED:
133 clist = COMMA.join(["self.%s" % c
134 for c in self.argnames])
135 if self.nargs == 1:
136 print >> buf, " return %s," % clist
137 else:
138 print >> buf, " return %s" % clist
139 else:
Jeremy Hylton566d9342004-09-07 15:28:01 +0000140 if len(self.argnames) == 1:
141 print >> buf, " return tuple(flatten(self.%s))" % self.argnames[0]
142 else:
143 print >> buf, " children = []"
144 template = " children.%s(%sself.%s%s)"
145 for name in self.argnames:
146 if self.argprops[name] == P_NESTED:
147 print >> buf, template % ("extend", "flatten(",
148 name, ")")
149 else:
150 print >> buf, template % ("append", "", name, "")
151 print >> buf, " return tuple(children)"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000152
153 def _gen_getChildNodes(self, buf):
154 print >> buf, " def getChildNodes(self):"
155 if len(self.argnames) == 0:
156 print >> buf, " return ()"
157 else:
158 if self.hardest_arg < P_NESTED:
159 clist = ["self.%s" % c
160 for c in self.argnames
161 if self.argprops[c] == P_NODE]
162 if len(clist) == 0:
163 print >> buf, " return ()"
164 elif len(clist) == 1:
165 print >> buf, " return %s," % clist[0]
166 else:
167 print >> buf, " return %s" % COMMA.join(clist)
168 else:
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000169 print >> buf, " nodelist = []"
170 template = " nodelist.%s(%sself.%s%s)"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000171 for name in self.argnames:
172 if self.argprops[name] == P_NONE:
Jeremy Hylton566d9342004-09-07 15:28:01 +0000173 tmp = (" if self.%s is not None:\n"
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000174 " nodelist.append(self.%s)")
Jeremy Hylton5477f522001-08-29 18:08:02 +0000175 print >> buf, tmp % (name, name)
176 elif self.argprops[name] == P_NESTED:
177 print >> buf, template % ("extend", "flatten_nodes(",
178 name, ")")
179 elif self.argprops[name] == P_NODE:
180 print >> buf, template % ("append", "", name, "")
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000181 print >> buf, " return tuple(nodelist)"
Jeremy Hylton821eee32000-10-25 17:59:17 +0000182
183 def _gen_repr(self, buf):
184 print >> buf, " def __repr__(self):"
185 if self.argnames:
186 fmt = COMMA.join(["%s"] * self.nargs)
Jeremy Hyltonab427b82001-08-18 00:14:37 +0000187 if '(' in self.args:
188 fmt = '(%s)' % fmt
Jeremy Hylton821eee32000-10-25 17:59:17 +0000189 vals = ["repr(self.%s)" % name for name in self.argnames]
190 vals = COMMA.join(vals)
191 if self.nargs == 1:
192 vals = vals + ","
193 print >> buf, ' return "%s(%s)" %% (%s)' % \
194 (self.name, fmt, vals)
195 else:
196 print >> buf, ' return "%s()"' % self.name
197
198rx_init = re.compile('init\((.*)\):')
199
200def parse_spec(file):
201 classes = {}
202 cur = None
203 for line in fileinput.input(file):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000204 if line.strip().startswith('#'):
205 continue
Jeremy Hylton821eee32000-10-25 17:59:17 +0000206 mo = rx_init.search(line)
207 if mo is None:
208 if cur is None:
209 # a normal entry
210 try:
211 name, args = line.split(':')
212 except ValueError:
213 continue
214 classes[name] = NodeInfo(name, args)
215 cur = None
216 else:
217 # some code for the __init__ method
218 cur.init.append(line)
219 else:
220 # some extra code for a Node's __init__ method
221 name = mo.group(1)
222 cur = classes[name]
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000223 return sorted(classes.values(), key=lambda n: n.name)
Jeremy Hylton821eee32000-10-25 17:59:17 +0000224
225def main():
226 prologue, epilogue = load_boilerplate(sys.argv[-1])
227 print prologue
228 print
229 classes = parse_spec(SPEC)
230 for info in classes:
231 print info.gen_source()
232 print epilogue
233
234if __name__ == "__main__":
235 main()
236 sys.exit(0)
237
238### PROLOGUE
239"""Python abstract syntax node definitions
240
Jeremy Hylton566d9342004-09-07 15:28:01 +0000241This file is automatically generated by Tools/compiler/astgen.py
Jeremy Hylton821eee32000-10-25 17:59:17 +0000242"""
Jeremy Hylton821eee32000-10-25 17:59:17 +0000243from consts import CO_VARARGS, CO_VARKEYWORDS
244
Neil Schemenauerf3694702005-06-02 05:55:20 +0000245def flatten(seq):
Jeremy Hylton821eee32000-10-25 17:59:17 +0000246 l = []
Neil Schemenauerf3694702005-06-02 05:55:20 +0000247 for elt in seq:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000248 t = type(elt)
Jeremy Hylton566d9342004-09-07 15:28:01 +0000249 if t is tuple or t is list:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000250 for elt2 in flatten(elt):
251 l.append(elt2)
252 else:
253 l.append(elt)
254 return l
255
Neil Schemenauerf3694702005-06-02 05:55:20 +0000256def flatten_nodes(seq):
257 return [n for n in flatten(seq) if isinstance(n, Node)]
Jeremy Hylton5477f522001-08-29 18:08:02 +0000258
Jeremy Hylton821eee32000-10-25 17:59:17 +0000259nodes = {}
260
Jeremy Hylton566d9342004-09-07 15:28:01 +0000261class Node:
262 """Abstract base class for ast nodes."""
Jeremy Hylton821eee32000-10-25 17:59:17 +0000263 def getChildren(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000264 pass # implemented by subclasses
Jeremy Hylton566d9342004-09-07 15:28:01 +0000265 def __iter__(self):
266 for n in self.getChildren():
267 yield n
268 def asList(self): # for backwards compatibility
269 return self.getChildren()
Jeremy Hyltoneef65902001-08-14 18:58:00 +0000270 def getChildNodes(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000271 pass # implemented by subclasses
Jeremy Hylton821eee32000-10-25 17:59:17 +0000272
273class EmptyNode(Node):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000274 pass
Jeremy Hylton821eee32000-10-25 17:59:17 +0000275
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000276class Expression(Node):
277 # Expression is an artificial node class to support "eval"
278 nodes["expression"] = "Expression"
279 def __init__(self, node):
280 self.node = node
281
282 def getChildren(self):
283 return self.node,
284
285 def getChildNodes(self):
286 return self.node,
287
288 def __repr__(self):
289 return "Expression(%s)" % (repr(self.node))
290
Jeremy Hylton821eee32000-10-25 17:59:17 +0000291### EPILOGUE
Jeremy Hylton566d9342004-09-07 15:28:01 +0000292for name, obj in globals().items():
293 if isinstance(obj, type) and issubclass(obj, Node):
Tim Peters0e9980f2004-09-12 03:49:31 +0000294 nodes[name.lower()] = obj