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 print >> buf
, " def __init__(self, %s, lineno=None):" % self
.args
110 print >> buf
, " def __init__(self, lineno=None):"
112 for name
in self
.argnames
:
113 print >> buf
, " self.%s = %s" % (name
, name
)
114 print >> buf
, " self.lineno = lineno"
115 # Copy the lines in self.init, indented four spaces. The rstrip()
116 # business is to get rid of the four spaces if line happens to be
117 # empty, so that reindent.py is happy with the output.
118 for line
in self
.init
:
119 print >> buf
, (" " + line
).rstrip()
121 def _gen_getChildren(self
, buf
):
122 print >> buf
, " def getChildren(self):"
123 if len(self
.argnames
) == 0:
124 print >> buf
, " return ()"
126 if self
.hardest_arg
< P_NESTED
:
127 clist
= COMMA
.join(["self.%s" % c
128 for c
in self
.argnames
])
130 print >> buf
, " return %s," % clist
132 print >> buf
, " return %s" % clist
134 if len(self
.argnames
) == 1:
135 print >> buf
, " return tuple(flatten(self.%s))" % self
.argnames
[0]
137 print >> buf
, " children = []"
138 template
= " children.%s(%sself.%s%s)"
139 for name
in self
.argnames
:
140 if self
.argprops
[name
] == P_NESTED
:
141 print >> buf
, template
% ("extend", "flatten(",
144 print >> buf
, template
% ("append", "", name
, "")
145 print >> buf
, " return tuple(children)"
147 def _gen_getChildNodes(self
, buf
):
148 print >> buf
, " def getChildNodes(self):"
149 if len(self
.argnames
) == 0:
150 print >> buf
, " return ()"
152 if self
.hardest_arg
< P_NESTED
:
153 clist
= ["self.%s" % c
154 for c
in self
.argnames
155 if self
.argprops
[c
] == P_NODE
]
157 print >> buf
, " return ()"
158 elif len(clist
) == 1:
159 print >> buf
, " return %s," % clist
[0]
161 print >> buf
, " return %s" % COMMA
.join(clist
)
163 print >> buf
, " nodelist = []"
164 template
= " nodelist.%s(%sself.%s%s)"
165 for name
in self
.argnames
:
166 if self
.argprops
[name
] == P_NONE
:
167 tmp
= (" if self.%s is not None:\n"
168 " nodelist.append(self.%s)")
169 print >> buf
, tmp
% (name
, name
)
170 elif self
.argprops
[name
] == P_NESTED
:
171 print >> buf
, template
% ("extend", "flatten_nodes(",
173 elif self
.argprops
[name
] == P_NODE
:
174 print >> buf
, template
% ("append", "", name
, "")
175 print >> buf
, " return tuple(nodelist)"
177 def _gen_repr(self
, buf
):
178 print >> buf
, " def __repr__(self):"
180 fmt
= COMMA
.join(["%s"] * self
.nargs
)
183 vals
= ["repr(self.%s)" % name
for name
in self
.argnames
]
184 vals
= COMMA
.join(vals
)
187 print >> buf
, ' return "%s(%s)" %% (%s)' % \
188 (self
.name
, fmt
, vals
)
190 print >> buf
, ' return "%s()"' % self
.name
192 rx_init
= re
.compile('init\((.*)\):')
194 def parse_spec(file):
197 for line
in fileinput
.input(file):
198 if line
.strip().startswith('#'):
200 mo
= rx_init
.search(line
)
205 name
, args
= line
.split(':')
208 classes
[name
] = NodeInfo(name
, args
)
211 # some code for the __init__ method
212 cur
.init
.append(line
)
214 # some extra code for a Node's __init__ method
217 return sorted(classes
.values(), key
=lambda n
: n
.name
)
220 prologue
, epilogue
= load_boilerplate(sys
.argv
[-1])
223 classes
= parse_spec(SPEC
)
225 print info
.gen_source()
228 if __name__
== "__main__":
233 """Python abstract syntax node definitions
235 This file is automatically generated by Tools/compiler/astgen.py
237 from consts
import CO_VARARGS
, CO_VARKEYWORDS
243 if t
is tuple or t
is list:
244 for elt2
in flatten(elt
):
250 def flatten_nodes(seq
):
251 return [n
for n
in flatten(seq
) if isinstance(n
, Node
)]
256 """Abstract base class for ast nodes."""
257 def getChildren(self
):
258 pass # implemented by subclasses
260 for n
in self
.getChildren():
262 def asList(self
): # for backwards compatibility
263 return self
.getChildren()
264 def getChildNodes(self
):
265 pass # implemented by subclasses
267 class EmptyNode(Node
):
270 class Expression(Node
):
271 # Expression is an artificial node class to support "eval"
272 nodes
["expression"] = "Expression"
273 def __init__(self
, node
):
276 def getChildren(self
):
279 def getChildNodes(self
):
283 return "Expression(%s)" % (repr(self
.node
))
286 for name
, obj
in globals().items():
287 if isinstance(obj
, type) and issubclass(obj
, Node
):
288 nodes
[name
.lower()] = obj