1 """Generate ast module from specification
3 This script generates the ast module from a simple specification,
4 which makes it easy to accomodate changes in the grammar. This
5 approach would be quite reasonable if the grammar changed often.
6 Instead, it is rather complex to generate the appropriate code. And
7 the Node interface has changed more often than the grammar.
13 from StringIO
import StringIO
18 def load_boilerplate(file):
22 i
= buf
.find('### ''PROLOGUE')
23 j
= buf
.find('### ''EPILOGUE')
24 pro
= buf
[i
+12:j
].strip()
25 epi
= buf
[j
+12:].strip()
28 def strip_default(arg
):
29 """Return the argname from an 'arg = default' string"""
42 """Each instance describes a specific AST node"""
43 def __init__(self
, name
, args
):
45 self
.args
= args
.strip()
46 self
.argnames
= self
.get_argnames()
47 self
.argprops
= self
.get_argprops()
48 self
.nargs
= len(self
.argnames
)
51 def get_argnames(self
):
53 i
= self
.args
.find('(')
54 j
= self
.args
.rfind(')')
55 args
= self
.args
[i
+1:j
]
58 return [strip_default(arg
.strip())
59 for arg
in args
.split(',') if arg
]
61 def get_argprops(self
):
62 """Each argument can have a property like '*' or '!'
64 XXX This method modifies the argnames in place!
68 for i
in range(len(self
.argnames
)):
69 arg
= self
.argnames
[i
]
71 arg
= self
.argnames
[i
] = arg
[:-1]
73 hardest_arg
= max(hardest_arg
, P_OTHER
)
74 elif arg
.endswith('!'):
75 arg
= self
.argnames
[i
] = arg
[:-1]
77 hardest_arg
= max(hardest_arg
, P_NESTED
)
78 elif arg
.endswith('&'):
79 arg
= self
.argnames
[i
] = arg
[:-1]
81 hardest_arg
= max(hardest_arg
, P_NONE
)
84 self
.hardest_arg
= hardest_arg
86 if hardest_arg
> P_NODE
:
87 self
.args
= self
.args
.replace('*', '')
88 self
.args
= self
.args
.replace('!', '')
89 self
.args
= self
.args
.replace('&', '')
95 print >> buf
, "class %s(Node):" % self
.name
98 self
._gen
_getChildren
(buf
)
100 self
._gen
_getChildNodes
(buf
)
106 def _gen_init(self
, buf
):
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
112 print >> buf
, " def __init__(self, lineno=None):"
115 for idx
, name
in enumerate(self
.argnames
):
116 print >> buf
, " self.%s = %s[%s]" % (name
, args
, idx
)
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()
127 def _gen_getChildren(self
, buf
):
128 print >> buf
, " def getChildren(self):"
129 if len(self
.argnames
) == 0:
130 print >> buf
, " return ()"
132 if self
.hardest_arg
< P_NESTED
:
133 clist
= COMMA
.join(["self.%s" % c
134 for c
in self
.argnames
])
136 print >> buf
, " return %s," % clist
138 print >> buf
, " return %s" % clist
140 if len(self
.argnames
) == 1:
141 print >> buf
, " return tuple(flatten(self.%s))" % self
.argnames
[0]
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(",
150 print >> buf
, template
% ("append", "", name
, "")
151 print >> buf
, " return tuple(children)"
153 def _gen_getChildNodes(self
, buf
):
154 print >> buf
, " def getChildNodes(self):"
155 if len(self
.argnames
) == 0:
156 print >> buf
, " return ()"
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
]
163 print >> buf
, " return ()"
164 elif len(clist
) == 1:
165 print >> buf
, " return %s," % clist
[0]
167 print >> buf
, " return %s" % COMMA
.join(clist
)
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(",
179 elif self
.argprops
[name
] == P_NODE
:
180 print >> buf
, template
% ("append", "", name
, "")
181 print >> buf
, " return tuple(nodelist)"
183 def _gen_repr(self
, buf
):
184 print >> buf
, " def __repr__(self):"
186 fmt
= COMMA
.join(["%s"] * self
.nargs
)
189 vals
= ["repr(self.%s)" % name
for name
in self
.argnames
]
190 vals
= COMMA
.join(vals
)
193 print >> buf
, ' return "%s(%s)" %% (%s)' % \
194 (self
.name
, fmt
, vals
)
196 print >> buf
, ' return "%s()"' % self
.name
198 rx_init
= re
.compile('init\((.*)\):')
200 def parse_spec(file):
203 for line
in fileinput
.input(file):
204 if line
.strip().startswith('#'):
206 mo
= rx_init
.search(line
)
211 name
, args
= line
.split(':')
214 classes
[name
] = NodeInfo(name
, args
)
217 # some code for the __init__ method
218 cur
.init
.append(line
)
220 # some extra code for a Node's __init__ method
223 return sorted(classes
.values(), key
=lambda n
: n
.name
)
226 prologue
, epilogue
= load_boilerplate(sys
.argv
[-1])
229 classes
= parse_spec(SPEC
)
231 print info
.gen_source()
234 if __name__
== "__main__":
239 """Python abstract syntax node definitions
241 This file is automatically generated by Tools/compiler/astgen.py
243 from consts
import CO_VARARGS
, CO_VARKEYWORDS
249 if t
is tuple or t
is list:
250 for elt2
in flatten(elt
):
256 def flatten_nodes(seq
):
257 return [n
for n
in flatten(seq
) if isinstance(n
, Node
)]
262 """Abstract base class for ast nodes."""
263 def getChildren(self
):
264 pass # implemented by subclasses
266 for n
in self
.getChildren():
268 def asList(self
): # for backwards compatibility
269 return self
.getChildren()
270 def getChildNodes(self
):
271 pass # implemented by subclasses
273 class EmptyNode(Node
):
276 class Expression(Node
):
277 # Expression is an artificial node class to support "eval"
278 nodes
["expression"] = "Expression"
279 def __init__(self
, node
):
282 def getChildren(self
):
285 def getChildNodes(self
):
289 return "Expression(%s)" % (repr(self
.node
))
292 for name
, obj
in globals().items():
293 if isinstance(obj
, type) and issubclass(obj
, Node
):
294 nodes
[name
.lower()] = obj