|
1 "Usage: unparse.py <path to source file>" |
|
2 import sys |
|
3 import _ast |
|
4 import cStringIO |
|
5 import os |
|
6 |
|
7 def interleave(inter, f, seq): |
|
8 """Call f on each item in seq, calling inter() in between. |
|
9 """ |
|
10 seq = iter(seq) |
|
11 try: |
|
12 f(seq.next()) |
|
13 except StopIteration: |
|
14 pass |
|
15 else: |
|
16 for x in seq: |
|
17 inter() |
|
18 f(x) |
|
19 |
|
20 class Unparser: |
|
21 """Methods in this class recursively traverse an AST and |
|
22 output source code for the abstract syntax; original formatting |
|
23 is disregarged. """ |
|
24 |
|
25 def __init__(self, tree, file = sys.stdout): |
|
26 """Unparser(tree, file=sys.stdout) -> None. |
|
27 Print the source for tree to file.""" |
|
28 self.f = file |
|
29 self._indent = 0 |
|
30 self.dispatch(tree) |
|
31 print >>self.f,"" |
|
32 self.f.flush() |
|
33 |
|
34 def fill(self, text = ""): |
|
35 "Indent a piece of text, according to the current indentation level" |
|
36 self.f.write("\n"+" "*self._indent + text) |
|
37 |
|
38 def write(self, text): |
|
39 "Append a piece of text to the current line." |
|
40 self.f.write(text) |
|
41 |
|
42 def enter(self): |
|
43 "Print ':', and increase the indentation." |
|
44 self.write(":") |
|
45 self._indent += 1 |
|
46 |
|
47 def leave(self): |
|
48 "Decrease the indentation level." |
|
49 self._indent -= 1 |
|
50 |
|
51 def dispatch(self, tree): |
|
52 "Dispatcher function, dispatching tree type T to method _T." |
|
53 if isinstance(tree, list): |
|
54 for t in tree: |
|
55 self.dispatch(t) |
|
56 return |
|
57 meth = getattr(self, "_"+tree.__class__.__name__) |
|
58 meth(tree) |
|
59 |
|
60 |
|
61 ############### Unparsing methods ###################### |
|
62 # There should be one method per concrete grammar type # |
|
63 # Constructors should be grouped by sum type. Ideally, # |
|
64 # this would follow the order in the grammar, but # |
|
65 # currently doesn't. # |
|
66 ######################################################## |
|
67 |
|
68 def _Module(self, tree): |
|
69 for stmt in tree.body: |
|
70 self.dispatch(stmt) |
|
71 |
|
72 # stmt |
|
73 def _Expr(self, tree): |
|
74 self.fill() |
|
75 self.dispatch(tree.value) |
|
76 |
|
77 def _Import(self, t): |
|
78 self.fill("import ") |
|
79 interleave(lambda: self.write(", "), self.dispatch, t.names) |
|
80 |
|
81 def _ImportFrom(self, t): |
|
82 self.fill("from ") |
|
83 self.write(t.module) |
|
84 self.write(" import ") |
|
85 interleave(lambda: self.write(", "), self.dispatch, t.names) |
|
86 # XXX(jpe) what is level for? |
|
87 |
|
88 def _Assign(self, t): |
|
89 self.fill() |
|
90 for target in t.targets: |
|
91 self.dispatch(target) |
|
92 self.write(" = ") |
|
93 self.dispatch(t.value) |
|
94 |
|
95 def _AugAssign(self, t): |
|
96 self.fill() |
|
97 self.dispatch(t.target) |
|
98 self.write(" "+self.binop[t.op.__class__.__name__]+"= ") |
|
99 self.dispatch(t.value) |
|
100 |
|
101 def _Return(self, t): |
|
102 self.fill("return") |
|
103 if t.value: |
|
104 self.write(" ") |
|
105 self.dispatch(t.value) |
|
106 |
|
107 def _Pass(self, t): |
|
108 self.fill("pass") |
|
109 |
|
110 def _Break(self, t): |
|
111 self.fill("break") |
|
112 |
|
113 def _Continue(self, t): |
|
114 self.fill("continue") |
|
115 |
|
116 def _Delete(self, t): |
|
117 self.fill("del ") |
|
118 self.dispatch(t.targets) |
|
119 |
|
120 def _Assert(self, t): |
|
121 self.fill("assert ") |
|
122 self.dispatch(t.test) |
|
123 if t.msg: |
|
124 self.write(", ") |
|
125 self.dispatch(t.msg) |
|
126 |
|
127 def _Exec(self, t): |
|
128 self.fill("exec ") |
|
129 self.dispatch(t.body) |
|
130 if t.globals: |
|
131 self.write(" in ") |
|
132 self.dispatch(t.globals) |
|
133 if t.locals: |
|
134 self.write(", ") |
|
135 self.dispatch(t.locals) |
|
136 |
|
137 def _Print(self, t): |
|
138 self.fill("print ") |
|
139 do_comma = False |
|
140 if t.dest: |
|
141 self.write(">>") |
|
142 self.dispatch(t.dest) |
|
143 do_comma = True |
|
144 for e in t.values: |
|
145 if do_comma:self.write(", ") |
|
146 else:do_comma=True |
|
147 self.dispatch(e) |
|
148 if not t.nl: |
|
149 self.write(",") |
|
150 |
|
151 def _Global(self, t): |
|
152 self.fill("global ") |
|
153 interleave(lambda: self.write(", "), self.write, t.names) |
|
154 |
|
155 def _Yield(self, t): |
|
156 self.write("(") |
|
157 self.write("yield") |
|
158 if t.value: |
|
159 self.write(" ") |
|
160 self.dispatch(t.value) |
|
161 self.write(")") |
|
162 |
|
163 def _Raise(self, t): |
|
164 self.fill('raise ') |
|
165 if t.type: |
|
166 self.dispatch(t.type) |
|
167 if t.inst: |
|
168 self.write(", ") |
|
169 self.dispatch(t.inst) |
|
170 if t.tback: |
|
171 self.write(", ") |
|
172 self.dispatch(t.tback) |
|
173 |
|
174 def _TryExcept(self, t): |
|
175 self.fill("try") |
|
176 self.enter() |
|
177 self.dispatch(t.body) |
|
178 self.leave() |
|
179 |
|
180 for ex in t.handlers: |
|
181 self.dispatch(ex) |
|
182 if t.orelse: |
|
183 self.fill("else") |
|
184 self.enter() |
|
185 self.dispatch(t.orelse) |
|
186 self.leave() |
|
187 |
|
188 def _TryFinally(self, t): |
|
189 self.fill("try") |
|
190 self.enter() |
|
191 self.dispatch(t.body) |
|
192 self.leave() |
|
193 |
|
194 self.fill("finally") |
|
195 self.enter() |
|
196 self.dispatch(t.finalbody) |
|
197 self.leave() |
|
198 |
|
199 def _ExceptHandler(self, t): |
|
200 self.fill("except") |
|
201 if t.type: |
|
202 self.write(" ") |
|
203 self.dispatch(t.type) |
|
204 if t.name: |
|
205 self.write(", ") |
|
206 self.dispatch(t.name) |
|
207 self.enter() |
|
208 self.dispatch(t.body) |
|
209 self.leave() |
|
210 |
|
211 def _ClassDef(self, t): |
|
212 self.write("\n") |
|
213 self.fill("class "+t.name) |
|
214 if t.bases: |
|
215 self.write("(") |
|
216 for a in t.bases: |
|
217 self.dispatch(a) |
|
218 self.write(", ") |
|
219 self.write(")") |
|
220 self.enter() |
|
221 self.dispatch(t.body) |
|
222 self.leave() |
|
223 |
|
224 def _FunctionDef(self, t): |
|
225 self.write("\n") |
|
226 for deco in t.decorator_list: |
|
227 self.fill("@") |
|
228 self.dispatch(deco) |
|
229 self.fill("def "+t.name + "(") |
|
230 self.dispatch(t.args) |
|
231 self.write(")") |
|
232 self.enter() |
|
233 self.dispatch(t.body) |
|
234 self.leave() |
|
235 |
|
236 def _For(self, t): |
|
237 self.fill("for ") |
|
238 self.dispatch(t.target) |
|
239 self.write(" in ") |
|
240 self.dispatch(t.iter) |
|
241 self.enter() |
|
242 self.dispatch(t.body) |
|
243 self.leave() |
|
244 if t.orelse: |
|
245 self.fill("else") |
|
246 self.enter() |
|
247 self.dispatch(t.orelse) |
|
248 self.leave |
|
249 |
|
250 def _If(self, t): |
|
251 self.fill("if ") |
|
252 self.dispatch(t.test) |
|
253 self.enter() |
|
254 # XXX elif? |
|
255 self.dispatch(t.body) |
|
256 self.leave() |
|
257 if t.orelse: |
|
258 self.fill("else") |
|
259 self.enter() |
|
260 self.dispatch(t.orelse) |
|
261 self.leave() |
|
262 |
|
263 def _While(self, t): |
|
264 self.fill("while ") |
|
265 self.dispatch(t.test) |
|
266 self.enter() |
|
267 self.dispatch(t.body) |
|
268 self.leave() |
|
269 if t.orelse: |
|
270 self.fill("else") |
|
271 self.enter() |
|
272 self.dispatch(t.orelse) |
|
273 self.leave |
|
274 |
|
275 def _With(self, t): |
|
276 self.fill("with ") |
|
277 self.dispatch(t.context_expr) |
|
278 if t.optional_vars: |
|
279 self.write(" as ") |
|
280 self.dispatch(t.optional_vars) |
|
281 self.enter() |
|
282 self.dispatch(t.body) |
|
283 self.leave() |
|
284 |
|
285 # expr |
|
286 def _Str(self, tree): |
|
287 self.write(repr(tree.s)) |
|
288 |
|
289 def _Name(self, t): |
|
290 self.write(t.id) |
|
291 |
|
292 def _Repr(self, t): |
|
293 self.write("`") |
|
294 self.dispatch(t.value) |
|
295 self.write("`") |
|
296 |
|
297 def _Num(self, t): |
|
298 self.write(repr(t.n)) |
|
299 |
|
300 def _List(self, t): |
|
301 self.write("[") |
|
302 interleave(lambda: self.write(", "), self.dispatch, t.elts) |
|
303 self.write("]") |
|
304 |
|
305 def _ListComp(self, t): |
|
306 self.write("[") |
|
307 self.dispatch(t.elt) |
|
308 for gen in t.generators: |
|
309 self.dispatch(gen) |
|
310 self.write("]") |
|
311 |
|
312 def _GeneratorExp(self, t): |
|
313 self.write("(") |
|
314 self.dispatch(t.elt) |
|
315 for gen in t.generators: |
|
316 self.dispatch(gen) |
|
317 self.write(")") |
|
318 |
|
319 def _comprehension(self, t): |
|
320 self.write(" for ") |
|
321 self.dispatch(t.target) |
|
322 self.write(" in ") |
|
323 self.dispatch(t.iter) |
|
324 for if_clause in t.ifs: |
|
325 self.write(" if ") |
|
326 self.dispatch(if_clause) |
|
327 |
|
328 def _IfExp(self, t): |
|
329 self.write("(") |
|
330 self.dispatch(t.body) |
|
331 self.write(" if ") |
|
332 self.dispatch(t.test) |
|
333 self.write(" else ") |
|
334 self.dispatch(t.orelse) |
|
335 self.write(")") |
|
336 |
|
337 def _Dict(self, t): |
|
338 self.write("{") |
|
339 def writem((k, v)): |
|
340 self.dispatch(k) |
|
341 self.write(": ") |
|
342 self.dispatch(v) |
|
343 interleave(lambda: self.write(", "), writem, zip(t.keys, t.values)) |
|
344 self.write("}") |
|
345 |
|
346 def _Tuple(self, t): |
|
347 self.write("(") |
|
348 if len(t.elts) == 1: |
|
349 (elt,) = t.elts |
|
350 self.dispatch(elt) |
|
351 self.write(",") |
|
352 else: |
|
353 interleave(lambda: self.write(", "), self.dispatch, t.elts) |
|
354 self.write(")") |
|
355 |
|
356 unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"} |
|
357 def _UnaryOp(self, t): |
|
358 self.write(self.unop[t.op.__class__.__name__]) |
|
359 self.write("(") |
|
360 self.dispatch(t.operand) |
|
361 self.write(")") |
|
362 |
|
363 binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%", |
|
364 "LShift":">>", "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&", |
|
365 "FloorDiv":"//", "Pow": "**"} |
|
366 def _BinOp(self, t): |
|
367 self.write("(") |
|
368 self.dispatch(t.left) |
|
369 self.write(" " + self.binop[t.op.__class__.__name__] + " ") |
|
370 self.dispatch(t.right) |
|
371 self.write(")") |
|
372 |
|
373 cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=", |
|
374 "Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"} |
|
375 def _Compare(self, t): |
|
376 self.write("(") |
|
377 self.dispatch(t.left) |
|
378 for o, e in zip(t.ops, t.comparators): |
|
379 self.write(" " + self.cmpops[o.__class__.__name__] + " ") |
|
380 self.dispatch(e) |
|
381 self.write(")") |
|
382 |
|
383 boolops = {_ast.And: 'and', _ast.Or: 'or'} |
|
384 def _BoolOp(self, t): |
|
385 self.write("(") |
|
386 s = " %s " % self.boolops[t.op.__class__] |
|
387 interleave(lambda: self.write(s), self.dispatch, t.values) |
|
388 self.write(")") |
|
389 |
|
390 def _Attribute(self,t): |
|
391 self.dispatch(t.value) |
|
392 self.write(".") |
|
393 self.write(t.attr) |
|
394 |
|
395 def _Call(self, t): |
|
396 self.dispatch(t.func) |
|
397 self.write("(") |
|
398 comma = False |
|
399 for e in t.args: |
|
400 if comma: self.write(", ") |
|
401 else: comma = True |
|
402 self.dispatch(e) |
|
403 for e in t.keywords: |
|
404 if comma: self.write(", ") |
|
405 else: comma = True |
|
406 self.dispatch(e) |
|
407 if t.starargs: |
|
408 if comma: self.write(", ") |
|
409 else: comma = True |
|
410 self.write("*") |
|
411 self.dispatch(t.starargs) |
|
412 if t.kwargs: |
|
413 if comma: self.write(", ") |
|
414 else: comma = True |
|
415 self.write("**") |
|
416 self.dispatch(t.kwargs) |
|
417 self.write(")") |
|
418 |
|
419 def _Subscript(self, t): |
|
420 self.dispatch(t.value) |
|
421 self.write("[") |
|
422 self.dispatch(t.slice) |
|
423 self.write("]") |
|
424 |
|
425 # slice |
|
426 def _Ellipsis(self, t): |
|
427 self.write("...") |
|
428 |
|
429 def _Index(self, t): |
|
430 self.dispatch(t.value) |
|
431 |
|
432 def _Slice(self, t): |
|
433 if t.lower: |
|
434 self.dispatch(t.lower) |
|
435 self.write(":") |
|
436 if t.upper: |
|
437 self.dispatch(t.upper) |
|
438 if t.step: |
|
439 self.write(":") |
|
440 self.dispatch(t.step) |
|
441 |
|
442 def _ExtSlice(self, t): |
|
443 interleave(lambda: self.write(', '), self.dispatch, t.dims) |
|
444 |
|
445 # others |
|
446 def _arguments(self, t): |
|
447 first = True |
|
448 nonDef = len(t.args)-len(t.defaults) |
|
449 for a in t.args[0:nonDef]: |
|
450 if first:first = False |
|
451 else: self.write(", ") |
|
452 self.dispatch(a) |
|
453 for a,d in zip(t.args[nonDef:], t.defaults): |
|
454 if first:first = False |
|
455 else: self.write(", ") |
|
456 self.dispatch(a), |
|
457 self.write("=") |
|
458 self.dispatch(d) |
|
459 if t.vararg: |
|
460 if first:first = False |
|
461 else: self.write(", ") |
|
462 self.write("*"+t.vararg) |
|
463 if t.kwarg: |
|
464 if first:first = False |
|
465 else: self.write(", ") |
|
466 self.write("**"+t.kwarg) |
|
467 |
|
468 def _keyword(self, t): |
|
469 self.write(t.arg) |
|
470 self.write("=") |
|
471 self.dispatch(t.value) |
|
472 |
|
473 def _Lambda(self, t): |
|
474 self.write("lambda ") |
|
475 self.dispatch(t.args) |
|
476 self.write(": ") |
|
477 self.dispatch(t.body) |
|
478 |
|
479 def _alias(self, t): |
|
480 self.write(t.name) |
|
481 if t.asname: |
|
482 self.write(" as "+t.asname) |
|
483 |
|
484 def roundtrip(filename, output=sys.stdout): |
|
485 source = open(filename).read() |
|
486 tree = compile(source, filename, "exec", _ast.PyCF_ONLY_AST) |
|
487 Unparser(tree, output) |
|
488 |
|
489 |
|
490 |
|
491 def testdir(a): |
|
492 try: |
|
493 names = [n for n in os.listdir(a) if n.endswith('.py')] |
|
494 except OSError: |
|
495 print >> sys.stderr, "Directory not readable: %s" % a |
|
496 else: |
|
497 for n in names: |
|
498 fullname = os.path.join(a, n) |
|
499 if os.path.isfile(fullname): |
|
500 output = cStringIO.StringIO() |
|
501 print 'Testing %s' % fullname |
|
502 try: |
|
503 roundtrip(fullname, output) |
|
504 except Exception, e: |
|
505 print ' Failed to compile, exception is %s' % repr(e) |
|
506 elif os.path.isdir(fullname): |
|
507 testdir(fullname) |
|
508 |
|
509 def main(args): |
|
510 if args[0] == '--testdir': |
|
511 for a in args[1:]: |
|
512 testdir(a) |
|
513 else: |
|
514 for a in args: |
|
515 roundtrip(a) |
|
516 |
|
517 if __name__=='__main__': |
|
518 main(sys.argv[1:]) |