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.
14 from StringIO
import StringIO
19 def load_boilerplate(file):
23 i
= buf
.find('### ''PROLOGUE')
24 j
= buf
.find('### ''EPILOGUE')
25 pro
= buf
[i
+12:j
].strip()
26 epi
= buf
[j
+12:].strip()
29 def strip_default(arg
):
30 """Return the argname from an 'arg = default' string"""
43 """Each instance describes a specific AST node"""
44 def __init__(self
, name
, args
):
46 self
.args
= args
.strip()
47 self
.argnames
= self
.get_argnames()
48 self
.argprops
= self
.get_argprops()
49 self
.nargs
= len(self
.argnames
)
52 def get_argnames(self
):
54 i
= self
.args
.find('(')
55 j
= self
.args
.rfind(')')
56 args
= self
.args
[i
+1:j
]
59 return [strip_default(arg
.strip())
60 for arg
in args
.split(',') if arg
]
62 def get_argprops(self
):
63 """Each argument can have a property like '*' or '!'
65 XXX This method modifies the argnames in place!
69 for i
in range(len(self
.argnames
)):
70 arg
= self
.argnames
[i
]
72 arg
= self
.argnames
[i
] = arg
[:-1]
74 hardest_arg
= max(hardest_arg
, P_OTHER
)
75 elif arg
.endswith('!'):
76 arg
= self
.argnames
[i
] = arg
[:-1]
78 hardest_arg
= max(hardest_arg
, P_NESTED
)
79 elif arg
.endswith('&'):
80 arg
= self
.argnames
[i
] = arg
[:-1]
82 hardest_arg
= max(hardest_arg
, P_NONE
)
85 self
.hardest_arg
= hardest_arg
87 if hardest_arg
> P_NODE
:
88 self
.args
= self
.args
.replace('*', '')
89 self
.args
= self
.args
.replace('!', '')
90 self
.args
= self
.args
.replace('&', '')
96 print >> buf
, "class %s(Node):" % self
.name
99 self
._gen
_getChildren
(buf
)
101 self
._gen
_getChildNodes
(buf
)
107 def _gen_init(self
, buf
):
109 print >> buf
, " def __init__(self, %s, lineno=None):" % self
.args
111 print >> buf
, " def __init__(self, lineno=None):"
113 for name
in self
.argnames
:
114 print >> buf
, " self.%s = %s" % (name
, name
)
115 print >> buf
, " self.lineno = lineno"
116 # Copy the lines in self.init, indented four spaces. The rstrip()
117 # business is to get rid of the four spaces if line happens to be
118 # empty, so that reindent.py is happy with the output.
119 for line
in self
.init
:
120 print >> buf
, (" " + line
).rstrip()
122 def _gen_getChildren(self
, buf
):
123 print >> buf
, " def getChildren(self):"
124 if len(self
.argnames
) == 0:
125 print >> buf
, " return ()"
127 if self
.hardest_arg
< P_NESTED
:
128 clist
= COMMA
.join(["self.%s" % c
129 for c
in self
.argnames
])
131 print >> buf
, " return %s," % clist
133 print >> buf
, " return %s" % clist
135 if len(self
.argnames
) == 1:
136 print >> buf
, " return tuple(flatten(self.%s))" % self
.argnames
[0]
138 print >> buf
, " children = []"
139 template
= " children.%s(%sself.%s%s)"
140 for name
in self
.argnames
:
141 if self
.argprops
[name
] == P_NESTED
:
142 print >> buf
, template
% ("extend", "flatten(",
145 print >> buf
, template
% ("append", "", name
, "")
146 print >> buf
, " return tuple(children)"
148 def _gen_getChildNodes(self
, buf
):
149 print >> buf
, " def getChildNodes(self):"
150 if len(self
.argnames
) == 0:
151 print >> buf
, " return ()"
153 if self
.hardest_arg
< P_NESTED
:
154 clist
= ["self.%s" % c
155 for c
in self
.argnames
156 if self
.argprops
[c
] == P_NODE
]
158 print >> buf
, " return ()"
159 elif len(clist
) == 1:
160 print >> buf
, " return %s," % clist
[0]
162 print >> buf
, " return %s" % COMMA
.join(clist
)
164 print >> buf
, " nodelist = []"
165 template
= " nodelist.%s(%sself.%s%s)"
166 for name
in self
.argnames
:
167 if self
.argprops
[name
] == P_NONE
:
168 tmp
= (" if self.%s is not None:\n"
169 " nodelist.append(self.%s)")
170 print >> buf
, tmp
% (name
, name
)
171 elif self
.argprops
[name
] == P_NESTED
:
172 print >> buf
, template
% ("extend", "flatten_nodes(",
174 elif self
.argprops
[name
] == P_NODE
:
175 print >> buf
, template
% ("append", "", name
, "")
176 print >> buf
, " return tuple(nodelist)"
178 def _gen_repr(self
, buf
):
179 print >> buf
, " def __repr__(self):"
181 fmt
= COMMA
.join(["%s"] * self
.nargs
)
184 vals
= ["repr(self.%s)" % name
for name
in self
.argnames
]
185 vals
= COMMA
.join(vals
)
188 print >> buf
, ' return "%s(%s)" %% (%s)' % \
189 (self
.name
, fmt
, vals
)
191 print >> buf
, ' return "%s()"' % self
.name
193 rx_init
= re
.compile('init\((.*)\):')
195 def parse_spec(file):
198 for line
in fileinput
.input(file):
199 if line
.strip().startswith('#'):
201 mo
= rx_init
.search(line
)
206 name
, args
= line
.split(':')
209 classes
[name
] = NodeInfo(name
, args
)
212 # some code for the __init__ method
213 cur
.init
.append(line
)
215 # some extra code for a Node's __init__ method
218 return sorted(classes
.values(), key
=lambda n
: n
.name
)
221 prologue
, epilogue
= load_boilerplate(sys
.argv
[-1])
224 classes
= parse_spec(SPEC
)
226 print info
.gen_source()
229 if __name__
== "__main__":
234 """Python abstract syntax node definitions
236 This file is automatically generated by Tools/compiler/astgen.py
238 from consts
import CO_VARARGS
, CO_VARKEYWORDS
244 if t
is tuple or t
is list:
245 for elt2
in flatten(elt
):
251 def flatten_nodes(seq
):
252 return [n
for n
in flatten(seq
) if isinstance(n
, Node
)]
257 """Abstract base class for ast nodes."""
258 def getChildren(self
):
259 pass # implemented by subclasses
261 for n
in self
.getChildren():
263 def asList(self
): # for backwards compatibility
264 return self
.getChildren()
265 def getChildNodes(self
):
266 pass # implemented by subclasses
268 class EmptyNode(Node
):
271 class Expression(Node
):
272 # Expression is an artificial node class to support "eval"
273 nodes
["expression"] = "Expression"
274 def __init__(self
, node
):
277 def getChildren(self
):
280 def getChildNodes(self
):
284 return "Expression(%s)" % (repr(self
.node
))
287 for name
, obj
in globals().items():
288 if isinstance(obj
, type) and issubclass(obj
, Node
):
289 nodes
[name
.lower()] = obj