source: proto/Compiler/unparse.py @ 5143

Last change on this file since 5143 was 2380, checked in by ksherdy, 7 years ago

Added quick and dirty python to pablo translator.

File size: 17.1 KB
Line 
1"Usage: unparse.py <path to source file>"
2
3#
4# Adapted from http://svn.python.org/projects/python/trunk/Demo/parser/unparse.py
5# Adds ast_show
6
7import sys
8import ast
9import cStringIO
10import os
11
12# Unparse to standard output; discard the unparser; add newline.
13def ast_show(ast_node):
14   unparser = Unparser(ast_node)
15   unparser.write("\n")
16#
17
18# Large float and imaginary literals get turned into infinities in the AST.
19# We unparse those infinities to INFSTR.
20INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
21
22def interleave(inter, f, seq):
23    """Call f on each item in seq, calling inter() in between.
24    """
25    seq = iter(seq)
26    try:
27        f(next(seq))
28    except StopIteration:
29        pass
30    else:
31        for x in seq:
32            inter()
33            f(x)
34
35class Unparser(object):
36    """Methods in this class recursively traverse an AST and
37    output source code for the abstract syntax; original formatting
38    is disregarded. """
39
40    def __init__(self, tree, file = sys.stdout):
41        """Unparser(tree, file=sys.stdout) -> None.
42         Print the source for tree to file."""
43        self.f = file
44        self.future_imports = []
45        self._indent = 0
46        self.dispatch(tree)
47        self.f.write("")
48        self.f.flush()
49
50    def fill(self, text = ""):
51        "Indent a piece of text, according to the current indentation level"
52        self.f.write("\n"+"    "*self._indent + text)
53
54    def write(self, text):
55        "Append a piece of text to the current line."
56        self.f.write(text)
57
58    def enter(self):
59        "Print ':', and increase the indentation."
60        self.write(":")
61        self._indent += 1
62
63    def leave(self):
64        "Decrease the indentation level."
65        self._indent -= 1
66
67    def dispatch(self, tree):
68        "Dispatcher function, dispatching tree type T to method _T."
69        if isinstance(tree, list):
70            for t in tree:
71                self.dispatch(t)
72            return
73        meth = getattr(self, "_"+tree.__class__.__name__)
74        meth(tree)
75
76
77    ############### Unparsing methods ######################
78    # There should be one method per concrete grammar type #
79    # Constructors should be grouped by sum type. Ideally, #
80    # this would follow the order in the grammar, but      #
81    # currently doesn't.                                   #
82    ########################################################
83
84    def _Module(self, tree):
85        for stmt in tree.body:
86            self.dispatch(stmt)
87
88    # stmt
89    def _Expr(self, tree):
90        self.fill()
91        self.dispatch(tree.value)
92
93    def _Import(self, t):
94        self.fill("import ")
95        interleave(lambda: self.write(", "), self.dispatch, t.names)
96
97    def _ImportFrom(self, t):
98        # A from __future__ import may affect unparsing, so record it.
99        if t.module and t.module == '__future__':
100            self.future_imports.extend(n.name for n in t.names)
101
102        self.fill("from ")
103        self.write("." * t.level)
104        if t.module:
105            self.write(t.module)
106        self.write(" import ")
107        interleave(lambda: self.write(", "), self.dispatch, t.names)
108
109    def _Assign(self, t):
110        self.fill()
111        for target in t.targets:
112            self.dispatch(target)
113            self.write(" = ")
114        self.dispatch(t.value)
115
116    def _AugAssign(self, t):
117        self.fill()
118        self.dispatch(t.target)
119        self.write(" "+self.binop[t.op.__class__.__name__]+"= ")
120        self.dispatch(t.value)
121
122    def _Return(self, t):
123        self.fill("return")
124        if t.value:
125            self.write(" ")
126            self.dispatch(t.value)
127
128    def _Pass(self, t):
129        self.fill("pass")
130
131    def _Break(self, t):
132        self.fill("break")
133
134    def _Continue(self, t):
135        self.fill("continue")
136
137    def _Delete(self, t):
138        self.fill("del ")
139        interleave(lambda: self.write(", "), self.dispatch, t.targets)
140
141    def _Assert(self, t):
142        self.fill("assert ")
143        self.dispatch(t.test)
144        if t.msg:
145            self.write(", ")
146            self.dispatch(t.msg)
147
148    def _Exec(self, t):
149        self.fill("exec ")
150        self.dispatch(t.body)
151        if t.globals:
152            self.write(" in ")
153            self.dispatch(t.globals)
154        if t.locals:
155            self.write(", ")
156            self.dispatch(t.locals)
157
158    def _Print(self, t):
159        self.fill("print ")
160        do_comma = False
161        if t.dest:
162            self.write(">>")
163            self.dispatch(t.dest)
164            do_comma = True
165        for e in t.values:
166            if do_comma:self.write(", ")
167            else:do_comma=True
168            self.dispatch(e)
169        if not t.nl:
170            self.write(",")
171
172    def _Global(self, t):
173        self.fill("global ")
174        interleave(lambda: self.write(", "), self.write, t.names)
175
176    def _Yield(self, t):
177        self.write("(")
178        self.write("yield")
179        if t.value:
180            self.write(" ")
181            self.dispatch(t.value)
182        self.write(")")
183
184    def _Raise(self, t):
185        self.fill('raise ')
186        if t.type:
187            self.dispatch(t.type)
188        if t.inst:
189            self.write(", ")
190            self.dispatch(t.inst)
191        if t.tback:
192            self.write(", ")
193            self.dispatch(t.tback)
194
195    def _TryExcept(self, t):
196        self.fill("try")
197        self.enter()
198        self.dispatch(t.body)
199        self.leave()
200
201        for ex in t.handlers:
202            self.dispatch(ex)
203        if t.orelse:
204            self.fill("else")
205            self.enter()
206            self.dispatch(t.orelse)
207            self.leave()
208
209    def _TryFinally(self, t):
210        if len(t.body) == 1 and isinstance(t.body[0], ast.TryExcept):
211            # try-except-finally
212            self.dispatch(t.body)
213        else:
214            self.fill("try")
215            self.enter()
216            self.dispatch(t.body)
217            self.leave()
218
219        self.fill("finally")
220        self.enter()
221        self.dispatch(t.finalbody)
222        self.leave()
223
224    def _ExceptHandler(self, t):
225        self.fill("except")
226        if t.type:
227            self.write(" ")
228            self.dispatch(t.type)
229        if t.name:
230            self.write(" as ")
231            self.dispatch(t.name)
232        self.enter()
233        self.dispatch(t.body)
234        self.leave()
235
236    def _ClassDef(self, t):
237        self.write("\n")
238        for deco in t.decorator_list:
239            self.fill("@")
240            self.dispatch(deco)
241        self.fill("class "+t.name)
242        if t.bases:
243            self.write("(")
244            for a in t.bases:
245                self.dispatch(a)
246                self.write(", ")
247            self.write(")")
248        self.enter()
249        self.dispatch(t.body)
250        self.leave()
251
252    def _FunctionDef(self, t):
253        self.write("\n")
254        for deco in t.decorator_list:
255            self.fill("@")
256            self.dispatch(deco)
257        self.fill("def "+t.name + "(")
258        self.dispatch(t.args)
259        self.write(")")
260        self.enter()
261        self.dispatch(t.body)
262        self.leave()
263
264    def _For(self, t):
265        self.fill("for ")
266        self.dispatch(t.target)
267        self.write(" in ")
268        self.dispatch(t.iter)
269        self.enter()
270        self.dispatch(t.body)
271        self.leave()
272        if t.orelse:
273            self.fill("else")
274            self.enter()
275            self.dispatch(t.orelse)
276            self.leave()
277
278    def _If(self, t):
279        self.fill("if ")
280        self.dispatch(t.test)
281        self.enter()
282        self.dispatch(t.body)
283        self.leave()
284        # collapse nested ifs into equivalent elifs.
285        while (t.orelse and len(t.orelse) == 1 and
286               isinstance(t.orelse[0], ast.If)):
287            t = t.orelse[0]
288            self.fill("elif ")
289            self.dispatch(t.test)
290            self.enter()
291            self.dispatch(t.body)
292            self.leave()
293        # final else
294        if t.orelse:
295            self.fill("else")
296            self.enter()
297            self.dispatch(t.orelse)
298            self.leave()
299
300    def _While(self, t):
301        self.fill("while ")
302        self.dispatch(t.test)
303        self.enter()
304        self.dispatch(t.body)
305        self.leave()
306        if t.orelse:
307            self.fill("else")
308            self.enter()
309            self.dispatch(t.orelse)
310            self.leave()
311
312    def _With(self, t):
313        self.fill("with ")
314        self.dispatch(t.context_expr)
315        if t.optional_vars:
316            self.write(" as ")
317            self.dispatch(t.optional_vars)
318        self.enter()
319        self.dispatch(t.body)
320        self.leave()
321
322    # expr
323    def _Str(self, tree):
324        # if from __future__ import unicode_literals is in effect,
325        # then we want to output string literals using a 'b' prefix
326        # and unicode literals with no prefix.
327        if "unicode_literals" not in self.future_imports:
328            self.write(repr(tree.s))
329        elif isinstance(tree.s, str):
330            self.write("b" + repr(tree.s))
331        elif isinstance(tree.s, unicode):
332            self.write(repr(tree.s).lstrip("u"))
333        else:
334            assert False, "shouldn't get here"
335
336    def _Name(self, t):
337        self.write(t.id)
338
339    def _Repr(self, t):
340        self.write("`")
341        self.dispatch(t.value)
342        self.write("`")
343
344    def _Num(self, t):
345        repr_n = repr(t.n)
346        # Parenthesize negative numbers, to avoid turning (-1)**2 into -1**2.
347        if repr_n.startswith("-"):
348            self.write("(")
349        # Substitute overflowing decimal literal for AST infinities.
350        self.write(repr_n.replace("inf", INFSTR))
351        if repr_n.startswith("-"):
352            self.write(")")
353
354    def _List(self, t):
355        self.write("[")
356        interleave(lambda: self.write(", "), self.dispatch, t.elts)
357        self.write("]")
358
359    def _ListComp(self, t):
360        self.write("[")
361        self.dispatch(t.elt)
362        for gen in t.generators:
363            self.dispatch(gen)
364        self.write("]")
365
366    def _GeneratorExp(self, t):
367        self.write("(")
368        self.dispatch(t.elt)
369        for gen in t.generators:
370            self.dispatch(gen)
371        self.write(")")
372
373    def _SetComp(self, t):
374        self.write("{")
375        self.dispatch(t.elt)
376        for gen in t.generators:
377            self.dispatch(gen)
378        self.write("}")
379
380    def _DictComp(self, t):
381        self.write("{")
382        self.dispatch(t.key)
383        self.write(": ")
384        self.dispatch(t.value)
385        for gen in t.generators:
386            self.dispatch(gen)
387        self.write("}")
388
389    def _comprehension(self, t):
390        self.write(" for ")
391        self.dispatch(t.target)
392        self.write(" in ")
393        self.dispatch(t.iter)
394        for if_clause in t.ifs:
395            self.write(" if ")
396            self.dispatch(if_clause)
397
398    def _IfExp(self, t):
399        self.write("(")
400        self.dispatch(t.body)
401        self.write(" if ")
402        self.dispatch(t.test)
403        self.write(" else ")
404        self.dispatch(t.orelse)
405        self.write(")")
406
407    def _Set(self, t):
408        assert(t.elts) # should be at least one element
409        self.write("{")
410        interleave(lambda: self.write(", "), self.dispatch, t.elts)
411        self.write("}")
412
413    def _Dict(self, t):
414        self.write("{")
415        def write_pair(pair):
416            (k, v) = pair
417            self.dispatch(k)
418            self.write(": ")
419            self.dispatch(v)
420        interleave(lambda: self.write(", "), write_pair, zip(t.keys, t.values))
421        self.write("}")
422
423    def _Tuple(self, t):
424        self.write("(")
425        if len(t.elts) == 1:
426            (elt,) = t.elts
427            self.dispatch(elt)
428            self.write(",")
429        else:
430            interleave(lambda: self.write(", "), self.dispatch, t.elts)
431        self.write(")")
432
433    unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"}
434    def _UnaryOp(self, t):
435        self.write("(")
436        self.write(self.unop[t.op.__class__.__name__])
437        self.write(" ")
438        # If we're applying unary minus to a number, parenthesize the number.
439        # This is necessary: -2147483648 is different from -(2147483648) on
440        # a 32-bit machine (the first is an int, the second a long), and
441        # -7j is different from -(7j).  (The first has real part 0.0, the second
442        # has real part -0.0.)
443        if isinstance(t.op, ast.USub) and isinstance(t.operand, ast.Num):
444            self.write("(")
445            self.dispatch(t.operand)
446            self.write(")")
447        else:
448            self.dispatch(t.operand)
449        self.write(")")
450
451    binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%",
452                    "LShift":"<<", "RShift":">>", "BitOr":"|", "BitXor":"^", "BitAnd":"&",
453                    "FloorDiv":"//", "Pow": "**"}
454    def _BinOp(self, t):
455        self.write("(")
456        self.dispatch(t.left)
457        self.write(" " + self.binop[t.op.__class__.__name__] + " ")
458        self.dispatch(t.right)
459        self.write(")")
460
461    cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=",
462                        "Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"}
463    def _Compare(self, t):
464        self.write("(")
465        self.dispatch(t.left)
466        for o, e in zip(t.ops, t.comparators):
467            self.write(" " + self.cmpops[o.__class__.__name__] + " ")
468            self.dispatch(e)
469        self.write(")")
470
471    boolops = {ast.And: 'and', ast.Or: 'or'}
472    def _BoolOp(self, t):
473        self.write("(")
474        s = " %s " % self.boolops[t.op.__class__]
475        interleave(lambda: self.write(s), self.dispatch, t.values)
476        self.write(")")
477
478    def _Attribute(self,t):
479        self.dispatch(t.value)
480        # Special case: 3.__abs__() is a syntax error, so if t.value
481        # is an integer literal then we need to either parenthesize
482        # it or add an extra space to get 3 .__abs__().
483        if isinstance(t.value, ast.Num) and isinstance(t.value.n, int):
484            self.write(" ")
485        self.write(".")
486        self.write(t.attr)
487
488    def _Call(self, t):
489        self.dispatch(t.func)
490        self.write("(")
491        comma = False
492        for e in t.args:
493            if comma: self.write(", ")
494            else: comma = True
495            self.dispatch(e)
496        for e in t.keywords:
497            if comma: self.write(", ")
498            else: comma = True
499            self.dispatch(e)
500        if t.starargs:
501            if comma: self.write(", ")
502            else: comma = True
503            self.write("*")
504            self.dispatch(t.starargs)
505        if t.kwargs:
506            if comma: self.write(", ")
507            else: comma = True
508            self.write("**")
509            self.dispatch(t.kwargs)
510        self.write(")")
511
512    def _Subscript(self, t):
513        self.dispatch(t.value)
514        self.write("[")
515        self.dispatch(t.slice)
516        self.write("]")
517
518    # slice
519    def _Ellipsis(self, t):
520        self.write("...")
521
522    def _Index(self, t):
523        self.dispatch(t.value)
524
525    def _Slice(self, t):
526        if t.lower:
527            self.dispatch(t.lower)
528        self.write(":")
529        if t.upper:
530            self.dispatch(t.upper)
531        if t.step:
532            self.write(":")
533            self.dispatch(t.step)
534
535    def _ExtSlice(self, t):
536        interleave(lambda: self.write(', '), self.dispatch, t.dims)
537
538    # others
539    def _arguments(self, t):
540        first = True
541        # normal arguments
542        defaults = [None] * (len(t.args) - len(t.defaults)) + t.defaults
543        for a,d in zip(t.args, defaults):
544            if first:first = False
545            else: self.write(", ")
546            self.dispatch(a),
547            if d:
548                self.write("=")
549                self.dispatch(d)
550
551        # varargs
552        if t.vararg:
553            if first:first = False
554            else: self.write(", ")
555            self.write("*")
556            self.write(t.vararg)
557
558        # kwargs
559        if t.kwarg:
560            if first:first = False
561            else: self.write(", ")
562            self.write("**"+t.kwarg)
563
564    def _keyword(self, t):
565        self.write(t.arg)
566        self.write("=")
567        self.dispatch(t.value)
568
569    def _Lambda(self, t):
570        self.write("(")
571        self.write("lambda ")
572        self.dispatch(t.args)
573        self.write(": ")
574        self.dispatch(t.body)
575        self.write(")")
576
577    def _alias(self, t):
578        self.write(t.name)
579        if t.asname:
580            self.write(" as "+t.asname)
581
582def roundtrip(filename, output=sys.stdout):
583    with open(filename, "r") as pyfile:
584        source = pyfile.read()
585    tree = compile(source, filename, "exec", ast.PyCF_ONLY_AST)
586    Unparser(tree, output)
587
588
589
590def testdir(a):
591    try:
592        names = [n for n in os.listdir(a) if n.endswith('.py')]
593    except OSError:
594        sys.stderr.write("Directory not readable: %s" % a)
595    else:
596        for n in names:
597            fullname = os.path.join(a, n)
598            if os.path.isfile(fullname):
599                output = cStringIO.StringIO()
600                print 'Testing %s' % fullname
601                try:
602                    roundtrip(fullname, output)
603                except Exception as e:
604                    print '  Failed to compile, exception is %s' % repr(e)
605            elif os.path.isdir(fullname):
606                testdir(fullname)
607
608def main(args):
609    if args[0] == '--testdir':
610        for a in args[1:]:
611            testdir(a)
612    else:
613        for a in args:
614            roundtrip(a)
615
616if __name__=='__main__':
617    main(sys.argv[1:])
618
Note: See TracBrowser for help on using the repository browser.