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
97 print >> buf
, ' nodes["%s"] = "%s"' % (self
.name
.lower(), self
.name
)
100 self
._gen
_getChildren
(buf
)
102 self
._gen
_getChildNodes
(buf
)
108 def _gen_init(self
, buf
):
109 print >> buf
, " def __init__(self, %s):" % self
.args
111 for name
in self
.argnames
:
112 print >> buf
, " self.%s = %s" % (name
, name
)
114 print >> buf
, " pass"
116 print >> buf
, "".join([" " + line
for line
in self
.init
])
118 def _gen_getChildren(self
, buf
):
119 print >> buf
, " def getChildren(self):"
120 if len(self
.argnames
) == 0:
121 print >> buf
, " return ()"
123 if self
.hardest_arg
< P_NESTED
:
124 clist
= COMMA
.join(["self.%s" % c
125 for c
in self
.argnames
])
127 print >> buf
, " return %s," % clist
129 print >> buf
, " return %s" % clist
131 print >> buf
, " children = []"
132 template
= " children.%s(%sself.%s%s)"
133 for name
in self
.argnames
:
134 if self
.argprops
[name
] == P_NESTED
:
135 print >> buf
, template
% ("extend", "flatten(",
138 print >> buf
, template
% ("append", "", name
, "")
139 print >> buf
, " return tuple(children)"
141 def _gen_getChildNodes(self
, buf
):
142 print >> buf
, " def getChildNodes(self):"
143 if len(self
.argnames
) == 0:
144 print >> buf
, " return ()"
146 if self
.hardest_arg
< P_NESTED
:
147 clist
= ["self.%s" % c
148 for c
in self
.argnames
149 if self
.argprops
[c
] == P_NODE
]
151 print >> buf
, " return ()"
152 elif len(clist
) == 1:
153 print >> buf
, " return %s," % clist
[0]
155 print >> buf
, " return %s" % COMMA
.join(clist
)
157 print >> buf
, " nodes = []"
158 template
= " nodes.%s(%sself.%s%s)"
159 for name
in self
.argnames
:
160 if self
.argprops
[name
] == P_NONE
:
161 tmp
= (" if self.%s is not None:"
162 " nodes.append(self.%s)")
163 print >> buf
, tmp
% (name
, name
)
164 elif self
.argprops
[name
] == P_NESTED
:
165 print >> buf
, template
% ("extend", "flatten_nodes(",
167 elif self
.argprops
[name
] == P_NODE
:
168 print >> buf
, template
% ("append", "", name
, "")
169 print >> buf
, " return tuple(nodes)"
171 def _gen_repr(self
, buf
):
172 print >> buf
, " def __repr__(self):"
174 fmt
= COMMA
.join(["%s"] * self
.nargs
)
177 vals
= ["repr(self.%s)" % name
for name
in self
.argnames
]
178 vals
= COMMA
.join(vals
)
181 print >> buf
, ' return "%s(%s)" %% (%s)' % \
182 (self
.name
, fmt
, vals
)
184 print >> buf
, ' return "%s()"' % self
.name
186 rx_init
= re
.compile('init\((.*)\):')
188 def parse_spec(file):
191 for line
in fileinput
.input(file):
192 if line
.strip().startswith('#'):
194 mo
= rx_init
.search(line
)
199 name
, args
= line
.split(':')
202 classes
[name
] = NodeInfo(name
, args
)
205 # some code for the __init__ method
206 cur
.init
.append(line
)
208 # some extra code for a Node's __init__ method
211 return classes
.values()
214 prologue
, epilogue
= load_boilerplate(sys
.argv
[-1])
217 classes
= parse_spec(SPEC
)
219 print info
.gen_source()
222 if __name__
== "__main__":
227 """Python abstract syntax node definitions
229 This file is automatically generated.
231 from types
import TupleType
, ListType
232 from consts
import CO_VARARGS
, CO_VARKEYWORDS
238 if t
is TupleType
or t
is ListType
:
239 for elt2
in flatten(elt
):
245 def flatten_nodes(list):
246 return [n
for n
in flatten(list) if isinstance(n
, Node
)]
251 if hasattr(item
, "asList"):
252 l
.append(item
.asList())
255 if t
is TupleType
or t
is ListType
:
256 l
.append(tuple(asList(item
)))
263 class Node
: # an abstract base class
264 lineno
= None # provide a lineno for nodes that don't have one
266 pass # implemented by subclass
267 def getChildren(self
):
268 pass # implemented by subclasses
270 return tuple(asList(self
.getChildren()))
271 def getChildNodes(self
):
272 pass # implemented by subclasses
274 class EmptyNode(Node
):
279 for k
in nodes
.keys():
280 nodes
[k
] = klasses
[nodes
[k
]]