]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """Generate ast module from specification\r |
2 | \r | |
3 | This script generates the ast module from a simple specification,\r | |
4 | which makes it easy to accomodate changes in the grammar. This\r | |
5 | approach would be quite reasonable if the grammar changed often.\r | |
6 | Instead, it is rather complex to generate the appropriate code. And\r | |
7 | the Node interface has changed more often than the grammar.\r | |
8 | """\r | |
9 | \r | |
10 | import fileinput\r | |
11 | import re\r | |
12 | import sys\r | |
13 | from StringIO import StringIO\r | |
14 | \r | |
15 | SPEC = "ast.txt"\r | |
16 | COMMA = ", "\r | |
17 | \r | |
18 | def load_boilerplate(file):\r | |
19 | f = open(file)\r | |
20 | buf = f.read()\r | |
21 | f.close()\r | |
22 | i = buf.find('### ''PROLOGUE')\r | |
23 | j = buf.find('### ''EPILOGUE')\r | |
24 | pro = buf[i+12:j].strip()\r | |
25 | epi = buf[j+12:].strip()\r | |
26 | return pro, epi\r | |
27 | \r | |
28 | def strip_default(arg):\r | |
29 | """Return the argname from an 'arg = default' string"""\r | |
30 | i = arg.find('=')\r | |
31 | if i == -1:\r | |
32 | return arg\r | |
33 | t = arg[:i].strip()\r | |
34 | return t\r | |
35 | \r | |
36 | P_NODE = 1\r | |
37 | P_OTHER = 2\r | |
38 | P_NESTED = 3\r | |
39 | P_NONE = 4\r | |
40 | \r | |
41 | class NodeInfo:\r | |
42 | """Each instance describes a specific AST node"""\r | |
43 | def __init__(self, name, args):\r | |
44 | self.name = name\r | |
45 | self.args = args.strip()\r | |
46 | self.argnames = self.get_argnames()\r | |
47 | self.argprops = self.get_argprops()\r | |
48 | self.nargs = len(self.argnames)\r | |
49 | self.init = []\r | |
50 | \r | |
51 | def get_argnames(self):\r | |
52 | if '(' in self.args:\r | |
53 | i = self.args.find('(')\r | |
54 | j = self.args.rfind(')')\r | |
55 | args = self.args[i+1:j]\r | |
56 | else:\r | |
57 | args = self.args\r | |
58 | return [strip_default(arg.strip())\r | |
59 | for arg in args.split(',') if arg]\r | |
60 | \r | |
61 | def get_argprops(self):\r | |
62 | """Each argument can have a property like '*' or '!'\r | |
63 | \r | |
64 | XXX This method modifies the argnames in place!\r | |
65 | """\r | |
66 | d = {}\r | |
67 | hardest_arg = P_NODE\r | |
68 | for i in range(len(self.argnames)):\r | |
69 | arg = self.argnames[i]\r | |
70 | if arg.endswith('*'):\r | |
71 | arg = self.argnames[i] = arg[:-1]\r | |
72 | d[arg] = P_OTHER\r | |
73 | hardest_arg = max(hardest_arg, P_OTHER)\r | |
74 | elif arg.endswith('!'):\r | |
75 | arg = self.argnames[i] = arg[:-1]\r | |
76 | d[arg] = P_NESTED\r | |
77 | hardest_arg = max(hardest_arg, P_NESTED)\r | |
78 | elif arg.endswith('&'):\r | |
79 | arg = self.argnames[i] = arg[:-1]\r | |
80 | d[arg] = P_NONE\r | |
81 | hardest_arg = max(hardest_arg, P_NONE)\r | |
82 | else:\r | |
83 | d[arg] = P_NODE\r | |
84 | self.hardest_arg = hardest_arg\r | |
85 | \r | |
86 | if hardest_arg > P_NODE:\r | |
87 | self.args = self.args.replace('*', '')\r | |
88 | self.args = self.args.replace('!', '')\r | |
89 | self.args = self.args.replace('&', '')\r | |
90 | \r | |
91 | return d\r | |
92 | \r | |
93 | def gen_source(self):\r | |
94 | buf = StringIO()\r | |
95 | print >> buf, "class %s(Node):" % self.name\r | |
96 | self._gen_init(buf)\r | |
97 | print >> buf\r | |
98 | self._gen_getChildren(buf)\r | |
99 | print >> buf\r | |
100 | self._gen_getChildNodes(buf)\r | |
101 | print >> buf\r | |
102 | self._gen_repr(buf)\r | |
103 | buf.seek(0, 0)\r | |
104 | return buf.read()\r | |
105 | \r | |
106 | def _gen_init(self, buf):\r | |
107 | if self.args:\r | |
108 | argtuple = '(' in self.args\r | |
109 | args = self.args if not argtuple else ''.join(self.argnames)\r | |
110 | print >> buf, " def __init__(self, %s, lineno=None):" % args\r | |
111 | else:\r | |
112 | print >> buf, " def __init__(self, lineno=None):"\r | |
113 | if self.argnames:\r | |
114 | if argtuple:\r | |
115 | for idx, name in enumerate(self.argnames):\r | |
116 | print >> buf, " self.%s = %s[%s]" % (name, args, idx)\r | |
117 | else:\r | |
118 | for name in self.argnames:\r | |
119 | print >> buf, " self.%s = %s" % (name, name)\r | |
120 | print >> buf, " self.lineno = lineno"\r | |
121 | # Copy the lines in self.init, indented four spaces. The rstrip()\r | |
122 | # business is to get rid of the four spaces if line happens to be\r | |
123 | # empty, so that reindent.py is happy with the output.\r | |
124 | for line in self.init:\r | |
125 | print >> buf, (" " + line).rstrip()\r | |
126 | \r | |
127 | def _gen_getChildren(self, buf):\r | |
128 | print >> buf, " def getChildren(self):"\r | |
129 | if len(self.argnames) == 0:\r | |
130 | print >> buf, " return ()"\r | |
131 | else:\r | |
132 | if self.hardest_arg < P_NESTED:\r | |
133 | clist = COMMA.join(["self.%s" % c\r | |
134 | for c in self.argnames])\r | |
135 | if self.nargs == 1:\r | |
136 | print >> buf, " return %s," % clist\r | |
137 | else:\r | |
138 | print >> buf, " return %s" % clist\r | |
139 | else:\r | |
140 | if len(self.argnames) == 1:\r | |
141 | print >> buf, " return tuple(flatten(self.%s))" % self.argnames[0]\r | |
142 | else:\r | |
143 | print >> buf, " children = []"\r | |
144 | template = " children.%s(%sself.%s%s)"\r | |
145 | for name in self.argnames:\r | |
146 | if self.argprops[name] == P_NESTED:\r | |
147 | print >> buf, template % ("extend", "flatten(",\r | |
148 | name, ")")\r | |
149 | else:\r | |
150 | print >> buf, template % ("append", "", name, "")\r | |
151 | print >> buf, " return tuple(children)"\r | |
152 | \r | |
153 | def _gen_getChildNodes(self, buf):\r | |
154 | print >> buf, " def getChildNodes(self):"\r | |
155 | if len(self.argnames) == 0:\r | |
156 | print >> buf, " return ()"\r | |
157 | else:\r | |
158 | if self.hardest_arg < P_NESTED:\r | |
159 | clist = ["self.%s" % c\r | |
160 | for c in self.argnames\r | |
161 | if self.argprops[c] == P_NODE]\r | |
162 | if len(clist) == 0:\r | |
163 | print >> buf, " return ()"\r | |
164 | elif len(clist) == 1:\r | |
165 | print >> buf, " return %s," % clist[0]\r | |
166 | else:\r | |
167 | print >> buf, " return %s" % COMMA.join(clist)\r | |
168 | else:\r | |
169 | print >> buf, " nodelist = []"\r | |
170 | template = " nodelist.%s(%sself.%s%s)"\r | |
171 | for name in self.argnames:\r | |
172 | if self.argprops[name] == P_NONE:\r | |
173 | tmp = (" if self.%s is not None:\n"\r | |
174 | " nodelist.append(self.%s)")\r | |
175 | print >> buf, tmp % (name, name)\r | |
176 | elif self.argprops[name] == P_NESTED:\r | |
177 | print >> buf, template % ("extend", "flatten_nodes(",\r | |
178 | name, ")")\r | |
179 | elif self.argprops[name] == P_NODE:\r | |
180 | print >> buf, template % ("append", "", name, "")\r | |
181 | print >> buf, " return tuple(nodelist)"\r | |
182 | \r | |
183 | def _gen_repr(self, buf):\r | |
184 | print >> buf, " def __repr__(self):"\r | |
185 | if self.argnames:\r | |
186 | fmt = COMMA.join(["%s"] * self.nargs)\r | |
187 | if '(' in self.args:\r | |
188 | fmt = '(%s)' % fmt\r | |
189 | vals = ["repr(self.%s)" % name for name in self.argnames]\r | |
190 | vals = COMMA.join(vals)\r | |
191 | if self.nargs == 1:\r | |
192 | vals = vals + ","\r | |
193 | print >> buf, ' return "%s(%s)" %% (%s)' % \\r | |
194 | (self.name, fmt, vals)\r | |
195 | else:\r | |
196 | print >> buf, ' return "%s()"' % self.name\r | |
197 | \r | |
198 | rx_init = re.compile('init\((.*)\):')\r | |
199 | \r | |
200 | def parse_spec(file):\r | |
201 | classes = {}\r | |
202 | cur = None\r | |
203 | for line in fileinput.input(file):\r | |
204 | if line.strip().startswith('#'):\r | |
205 | continue\r | |
206 | mo = rx_init.search(line)\r | |
207 | if mo is None:\r | |
208 | if cur is None:\r | |
209 | # a normal entry\r | |
210 | try:\r | |
211 | name, args = line.split(':')\r | |
212 | except ValueError:\r | |
213 | continue\r | |
214 | classes[name] = NodeInfo(name, args)\r | |
215 | cur = None\r | |
216 | else:\r | |
217 | # some code for the __init__ method\r | |
218 | cur.init.append(line)\r | |
219 | else:\r | |
220 | # some extra code for a Node's __init__ method\r | |
221 | name = mo.group(1)\r | |
222 | cur = classes[name]\r | |
223 | return sorted(classes.values(), key=lambda n: n.name)\r | |
224 | \r | |
225 | def main():\r | |
226 | prologue, epilogue = load_boilerplate(sys.argv[-1])\r | |
227 | print prologue\r | |
228 | print\r | |
229 | classes = parse_spec(SPEC)\r | |
230 | for info in classes:\r | |
231 | print info.gen_source()\r | |
232 | print epilogue\r | |
233 | \r | |
234 | if __name__ == "__main__":\r | |
235 | main()\r | |
236 | sys.exit(0)\r | |
237 | \r | |
238 | ### PROLOGUE\r | |
239 | """Python abstract syntax node definitions\r | |
240 | \r | |
241 | This file is automatically generated by Tools/compiler/astgen.py\r | |
242 | """\r | |
243 | from consts import CO_VARARGS, CO_VARKEYWORDS\r | |
244 | \r | |
245 | def flatten(seq):\r | |
246 | l = []\r | |
247 | for elt in seq:\r | |
248 | t = type(elt)\r | |
249 | if t is tuple or t is list:\r | |
250 | for elt2 in flatten(elt):\r | |
251 | l.append(elt2)\r | |
252 | else:\r | |
253 | l.append(elt)\r | |
254 | return l\r | |
255 | \r | |
256 | def flatten_nodes(seq):\r | |
257 | return [n for n in flatten(seq) if isinstance(n, Node)]\r | |
258 | \r | |
259 | nodes = {}\r | |
260 | \r | |
261 | class Node:\r | |
262 | """Abstract base class for ast nodes."""\r | |
263 | def getChildren(self):\r | |
264 | pass # implemented by subclasses\r | |
265 | def __iter__(self):\r | |
266 | for n in self.getChildren():\r | |
267 | yield n\r | |
268 | def asList(self): # for backwards compatibility\r | |
269 | return self.getChildren()\r | |
270 | def getChildNodes(self):\r | |
271 | pass # implemented by subclasses\r | |
272 | \r | |
273 | class EmptyNode(Node):\r | |
274 | pass\r | |
275 | \r | |
276 | class Expression(Node):\r | |
277 | # Expression is an artificial node class to support "eval"\r | |
278 | nodes["expression"] = "Expression"\r | |
279 | def __init__(self, node):\r | |
280 | self.node = node\r | |
281 | \r | |
282 | def getChildren(self):\r | |
283 | return self.node,\r | |
284 | \r | |
285 | def getChildNodes(self):\r | |
286 | return self.node,\r | |
287 | \r | |
288 | def __repr__(self):\r | |
289 | return "Expression(%s)" % (repr(self.node))\r | |
290 | \r | |
291 | ### EPILOGUE\r | |
292 | for name, obj in globals().items():\r | |
293 | if isinstance(obj, type) and issubclass(obj, Node):\r | |
294 | nodes[name.lower()] = obj\r |