blob: 90201d31e35f8849e98b0c39139e1b875ed0131a [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('&', '')
91
92 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
97 print >> buf, ' nodes["%s"] = "%s"' % (self.name.lower(), self.name)
98 self._gen_init(buf)
Jeremy Hylton5477f522001-08-29 18:08:02 +000099 print >> buf
Jeremy Hylton821eee32000-10-25 17:59:17 +0000100 self._gen_getChildren(buf)
Jeremy Hylton5477f522001-08-29 18:08:02 +0000101 print >> buf
102 self._gen_getChildNodes(buf)
103 print >> buf
Jeremy Hylton821eee32000-10-25 17:59:17 +0000104 self._gen_repr(buf)
105 buf.seek(0, 0)
106 return buf.read()
107
108 def _gen_init(self, buf):
109 print >> buf, " def __init__(self, %s):" % self.args
110 if self.argnames:
111 for name in self.argnames:
112 print >> buf, " self.%s = %s" % (name, name)
113 else:
114 print >> buf, " pass"
115 if self.init:
116 print >> buf, "".join([" " + line for line in self.init])
117
118 def _gen_getChildren(self, buf):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000119 print >> buf, " def getChildren(self):"
120 if len(self.argnames) == 0:
Jeremy Hylton821eee32000-10-25 17:59:17 +0000121 print >> buf, " return ()"
Jeremy Hylton5477f522001-08-29 18:08:02 +0000122 else:
123 if self.hardest_arg < P_NESTED:
124 clist = COMMA.join(["self.%s" % c
125 for c in self.argnames])
126 if self.nargs == 1:
127 print >> buf, " return %s," % clist
128 else:
129 print >> buf, " return %s" % clist
130 else:
131 print >> buf, " children = []"
132 template = " children.%s(%sself.%s%s)"
133 for name in self.argnames:
134 if self.argprops[name] == P_NESTED:
135 print >> buf, template % ("extend", "flatten(",
136 name, ")")
137 else:
138 print >> buf, template % ("append", "", name, "")
139 print >> buf, " return tuple(children)"
140
141 def _gen_getChildNodes(self, buf):
142 print >> buf, " def getChildNodes(self):"
143 if len(self.argnames) == 0:
144 print >> buf, " return ()"
145 else:
146 if self.hardest_arg < P_NESTED:
147 clist = ["self.%s" % c
148 for c in self.argnames
149 if self.argprops[c] == P_NODE]
150 if len(clist) == 0:
151 print >> buf, " return ()"
152 elif len(clist) == 1:
153 print >> buf, " return %s," % clist[0]
154 else:
155 print >> buf, " return %s" % COMMA.join(clist)
156 else:
157 print >> buf, " nodes = []"
158 template = " nodes.%s(%sself.%s%s)"
159 for name in self.argnames:
160 if self.argprops[name] == P_NONE:
161 tmp = (" if self.%s is not None:"
162 " nodes.append(self.%s)")
163 print >> buf, tmp % (name, name)
164 elif self.argprops[name] == P_NESTED:
165 print >> buf, template % ("extend", "flatten_nodes(",
166 name, ")")
167 elif self.argprops[name] == P_NODE:
168 print >> buf, template % ("append", "", name, "")
169 print >> buf, " return tuple(nodes)"
Jeremy Hylton821eee32000-10-25 17:59:17 +0000170
171 def _gen_repr(self, buf):
172 print >> buf, " def __repr__(self):"
173 if self.argnames:
174 fmt = COMMA.join(["%s"] * self.nargs)
Jeremy Hyltonab427b82001-08-18 00:14:37 +0000175 if '(' in self.args:
176 fmt = '(%s)' % fmt
Jeremy Hylton821eee32000-10-25 17:59:17 +0000177 vals = ["repr(self.%s)" % name for name in self.argnames]
178 vals = COMMA.join(vals)
179 if self.nargs == 1:
180 vals = vals + ","
181 print >> buf, ' return "%s(%s)" %% (%s)' % \
182 (self.name, fmt, vals)
183 else:
184 print >> buf, ' return "%s()"' % self.name
185
186rx_init = re.compile('init\((.*)\):')
187
188def parse_spec(file):
189 classes = {}
190 cur = None
191 for line in fileinput.input(file):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000192 if line.strip().startswith('#'):
193 continue
Jeremy Hylton821eee32000-10-25 17:59:17 +0000194 mo = rx_init.search(line)
195 if mo is None:
196 if cur is None:
197 # a normal entry
198 try:
199 name, args = line.split(':')
200 except ValueError:
201 continue
202 classes[name] = NodeInfo(name, args)
203 cur = None
204 else:
205 # some code for the __init__ method
206 cur.init.append(line)
207 else:
208 # some extra code for a Node's __init__ method
209 name = mo.group(1)
210 cur = classes[name]
211 return classes.values()
212
213def main():
214 prologue, epilogue = load_boilerplate(sys.argv[-1])
215 print prologue
216 print
217 classes = parse_spec(SPEC)
218 for info in classes:
219 print info.gen_source()
220 print epilogue
221
222if __name__ == "__main__":
223 main()
224 sys.exit(0)
225
226### PROLOGUE
227"""Python abstract syntax node definitions
228
229This file is automatically generated.
230"""
231from types import TupleType, ListType
232from consts import CO_VARARGS, CO_VARKEYWORDS
233
234def flatten(list):
235 l = []
236 for elt in list:
237 t = type(elt)
238 if t is TupleType or t is ListType:
239 for elt2 in flatten(elt):
240 l.append(elt2)
241 else:
242 l.append(elt)
243 return l
244
Jeremy Hylton5477f522001-08-29 18:08:02 +0000245def flatten_nodes(list):
246 return [n for n in flatten(list) if isinstance(n, Node)]
247
Jeremy Hylton821eee32000-10-25 17:59:17 +0000248def asList(nodes):
249 l = []
250 for item in nodes:
251 if hasattr(item, "asList"):
252 l.append(item.asList())
253 else:
254 t = type(item)
255 if t is TupleType or t is ListType:
256 l.append(tuple(asList(item)))
257 else:
258 l.append(item)
259 return l
260
261nodes = {}
262
Jeremy Hylton5477f522001-08-29 18:08:02 +0000263class Node: # an abstract base class
264 lineno = None # provide a lineno for nodes that don't have one
Jeremy Hylton821eee32000-10-25 17:59:17 +0000265 def getType(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000266 pass # implemented by subclass
Jeremy Hylton821eee32000-10-25 17:59:17 +0000267 def getChildren(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000268 pass # implemented by subclasses
Jeremy Hylton821eee32000-10-25 17:59:17 +0000269 def asList(self):
270 return tuple(asList(self.getChildren()))
Jeremy Hyltoneef65902001-08-14 18:58:00 +0000271 def getChildNodes(self):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000272 pass # implemented by subclasses
Jeremy Hylton821eee32000-10-25 17:59:17 +0000273
274class EmptyNode(Node):
Jeremy Hylton5477f522001-08-29 18:08:02 +0000275 pass
Jeremy Hylton821eee32000-10-25 17:59:17 +0000276
277### EPILOGUE
278klasses = globals()
279for k in nodes.keys():
280 nodes[k] = klasses[nodes[k]]