source: python/vendor/Python-2.7.6/Tools/compiler/astgen.py

Last change on this file was 388, checked in by dmik, 11 years ago

python: Update vendor to 2.7.6.

  • Property svn:eol-style set to native
File size: 9.6 KB
Line 
1"""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"""
9
10import fileinput
11import 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
33 t = arg[:i].strip()
34 return t
35
36P_NODE = 1
37P_OTHER = 2
38P_NESTED = 3
39P_NONE = 4
40
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()
47 self.argprops = self.get_argprops()
48 self.nargs = len(self.argnames)
49 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
61 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
73 hardest_arg = max(hardest_arg, P_OTHER)
74 elif arg.endswith('!'):
75 arg = self.argnames[i] = arg[:-1]
76 d[arg] = P_NESTED
77 hardest_arg = max(hardest_arg, P_NESTED)
78 elif arg.endswith('&'):
79 arg = self.argnames[i] = arg[:-1]
80 d[arg] = P_NONE
81 hardest_arg = max(hardest_arg, P_NONE)
82 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('&', '')
90
91 return d
92
93 def gen_source(self):
94 buf = StringIO()
95 print >> buf, "class %s(Node):" % self.name
96 self._gen_init(buf)
97 print >> buf
98 self._gen_getChildren(buf)
99 print >> buf
100 self._gen_getChildNodes(buf)
101 print >> buf
102 self._gen_repr(buf)
103 buf.seek(0, 0)
104 return buf.read()
105
106 def _gen_init(self, buf):
107 if self.args:
108 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
111 else:
112 print >> buf, " def __init__(self, lineno=None):"
113 if self.argnames:
114 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)
120 print >> buf, " self.lineno = lineno"
121 # 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()
126
127 def _gen_getChildren(self, buf):
128 print >> buf, " def getChildren(self):"
129 if len(self.argnames) == 0:
130 print >> buf, " return ()"
131 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:
140 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)"
152
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:
169 print >> buf, " nodelist = []"
170 template = " nodelist.%s(%sself.%s%s)"
171 for name in self.argnames:
172 if self.argprops[name] == P_NONE:
173 tmp = (" if self.%s is not None:\n"
174 " nodelist.append(self.%s)")
175 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, "")
181 print >> buf, " return tuple(nodelist)"
182
183 def _gen_repr(self, buf):
184 print >> buf, " def __repr__(self):"
185 if self.argnames:
186 fmt = COMMA.join(["%s"] * self.nargs)
187 if '(' in self.args:
188 fmt = '(%s)' % fmt
189 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):
204 if line.strip().startswith('#'):
205 continue
206 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]
223 return sorted(classes.values(), key=lambda n: n.name)
224
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
241This file is automatically generated by Tools/compiler/astgen.py
242"""
243from consts import CO_VARARGS, CO_VARKEYWORDS
244
245def flatten(seq):
246 l = []
247 for elt in seq:
248 t = type(elt)
249 if t is tuple or t is list:
250 for elt2 in flatten(elt):
251 l.append(elt2)
252 else:
253 l.append(elt)
254 return l
255
256def flatten_nodes(seq):
257 return [n for n in flatten(seq) if isinstance(n, Node)]
258
259nodes = {}
260
261class Node:
262 """Abstract base class for ast nodes."""
263 def getChildren(self):
264 pass # implemented by subclasses
265 def __iter__(self):
266 for n in self.getChildren():
267 yield n
268 def asList(self): # for backwards compatibility
269 return self.getChildren()
270 def getChildNodes(self):
271 pass # implemented by subclasses
272
273class EmptyNode(Node):
274 pass
275
276class 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
291### EPILOGUE
292for name, obj in globals().items():
293 if isinstance(obj, type) and issubclass(obj, Node):
294 nodes[name.lower()] = obj
Note: See TracBrowser for help on using the repository browser.