[pypy-commit] pypy py3.7: progress on unparsing

cfbolz pypy.commits at gmail.com
Mon Jan 6 04:55:26 EST 2020


Author: Carl Friedrich Bolz-Tereick <cfbolz at gmx.de>
Branch: py3.7
Changeset: r98447:771817286c04
Date: 2020-01-05 11:04 +0100
http://bitbucket.org/pypy/pypy/changeset/771817286c04/

Log:	progress on unparsing

diff --git a/pypy/interpreter/astcompiler/test/test_unparse.py b/pypy/interpreter/astcompiler/test/test_unparse.py
--- a/pypy/interpreter/astcompiler/test/test_unparse.py
+++ b/pypy/interpreter/astcompiler/test/test_unparse.py
@@ -23,60 +23,114 @@
         assert isinstance(expr, ast.Expr)
         return expr.value
 
+    def check(self, expr, unparsed=None):
+        if unparsed is None:
+            unparsed = expr
+        ast = self.get_first_expr(expr)
+        assert unparse(self.space, ast) == unparsed
+        # should be stable
+        if expr != unparsed:
+            ast = self.get_first_expr(unparsed)
+            assert unparse(self.space, ast) == unparsed
+
     def test_num(self):
-        ast = self.get_first_expr("1")
-        assert unparse(self.space, ast) == "1"
-        ast = self.get_first_expr("1.63")
-        assert unparse(self.space, ast) == "1.63"
+        self.check("1")
+        self.check("1.64")
 
     def test_str(self):
-        ast = self.get_first_expr("u'a'")
-        assert unparse(self.space, ast) == "'a'"
+        self.check("u'a'", "'a'")
 
     def test_bytes(self):
-        ast = self.get_first_expr("b'a'")
-        assert unparse(self.space, ast) == "b'a'"
+        self.check("b'a'")
 
     def test_name(self):
-        ast = self.get_first_expr('a')
-        assert unparse(self.space, ast) == "a"
+        self.check('a')
 
     def test_unaryop(self):
-        ast = self.get_first_expr('+a')
-        assert unparse(self.space, ast) == "+a"
-        ast = self.get_first_expr('-a')
-        assert unparse(self.space, ast) == "-a"
-        ast = self.get_first_expr('~a')
-        assert unparse(self.space, ast) == "~a"
-        ast = self.get_first_expr('not a')
-        assert unparse(self.space, ast) == "not a"
+        self.check('+a')
+        self.check('-a')
+        self.check('~a')
+        self.check('not a')
 
     def test_binaryop(self):
-        ast = self.get_first_expr('b+a')
-        assert unparse(self.space, ast) == "b + a"
-        ast = self.get_first_expr('c*(b+a)')
-        assert unparse(self.space, ast) == "c * (b + a)"
-        ast = self.get_first_expr('c**(b+a)')
-        assert unparse(self.space, ast) == "c ** (b + a)"
-        ast = self.get_first_expr('2**2**3')
-        assert unparse(self.space, ast) == "2 ** 2 ** 3"
-        ast = self.get_first_expr('(2**2)**3')
-        assert unparse(self.space, ast) == "(2 ** 2) ** 3"
+        self.check('b+a', "b + a")
+        self.check('c*(b+a)', "c * (b + a)")
+        self.check('c**(b+a)', "c ** (b + a)")
+        self.check('2**2**3', "2 ** 2 ** 3")
+        self.check('(2**2)**3', "(2 ** 2) ** 3")
         
-        ast = self.get_first_expr('2 << 2 << 3')
-        assert unparse(self.space, ast) == "2 << 2 << 3"
-        ast = self.get_first_expr('2<<(2<<3)')
-        assert unparse(self.space, ast) == "2 << (2 << 3)"
+        self.check('2 << 2 << 3')
+        self.check('2<<(2<<3)', "2 << (2 << 3)")
 
     def test_compare(self):
-        ast = self.get_first_expr('b == 5 == c == d')
-        assert unparse(self.space, ast) == 'b == 5 == c == d'
+        self.check('b == 5 == c == d')
 
     def test_boolop(self):
-        ast = self.get_first_expr('b and a and c or d')
-        assert unparse(self.space, ast) == "b and a and c or d"
+        self.check('b and a and c or d')
 
     def test_if(self):
-        ast = self.get_first_expr('a if b else c')
-        assert unparse(self.space, ast) == "a if b else c"
+        self.check('a if b else c')
 
+    def test_list(self):
+        self.check('[]')
+        self.check('[1, 2, 3, 4]')
+
+    def test_tuple(self):
+        self.check('()')
+        self.check('(a,)')
+        self.check('([1, 2], a + 6, 3, 4)')
+
+    def test_sets(self):
+        self.check('{1}')
+        self.check('{(1, 2), a + 6, 3, 4}')
+
+    def test_dict(self):
+        self.check('{}')
+        self.check('{a: b, c: d}')
+        self.check('{1: 2, **x}')
+
+    def test_list_comprehension(self):
+        self.check('[a for x in y if b if c]')
+
+    def test_genexp(self):
+        self.check('(a for x in y for z in b)')
+
+    def test_set_comprehension(self):
+        self.check('{a for (x,) in y for z in b}')
+
+    def test_ellipsis(self):
+        self.check('...')
+
+    def test_index(self):
+        self.check('a[1]')
+        self.check('a[1:5]')
+        self.check('a[1:5,7:12,:,5]')
+
+    def test_yield(self):
+        self.check('(yield)')
+        self.check('(yield 4 + 6)')
+
+    def test_yield_from(self):
+        self.check('(yield from a)')
+
+    def test_call(self):
+        self.check('f()')
+        self.check('f(a)')
+        self.check('f(a, b, 1)')
+        self.check('f(a, b, 1, a=4, b=78)')
+        self.check('f(a, x=y, **b, **c)')
+        self.check('f(*a)')
+        self.check('f(x for y in z)')
+
+    def test_lambda(self):
+        self.check('lambda: 1')
+        self.check('lambda a: 1')
+        self.check('lambda a=1: 1')
+        self.check('lambda b, c: 1')
+        self.check('lambda *l: 1')
+        self.check('lambda *, m, l=5: 1')
+        self.check('lambda **foo: 1')
+
+    def test_fstring(self):
+        self.check('f"a{a + 2}b c{d}"')
+
diff --git a/pypy/interpreter/astcompiler/unparse.py b/pypy/interpreter/astcompiler/unparse.py
--- a/pypy/interpreter/astcompiler/unparse.py
+++ b/pypy/interpreter/astcompiler/unparse.py
@@ -2,24 +2,24 @@
 from pypy.interpreter.error import oefmt
 from pypy.interpreter.astcompiler import ast
 
-
-PRIORITY_TEST = 0                   # 'if'-'else', 'lambda'
-PRIORITY_OR = 1                     # 'or'
-PRIORITY_AND = 2                    # 'and'
-PRIORITY_NOT = 3                    # 'not'
-PRIORITY_CMP = 4                    # '<', '>', '==', '>=', '<=', '!=',
+PRIORITY_TUPLE = 0
+PRIORITY_TEST = 1                   # 'if'-'else', 'lambda'
+PRIORITY_OR = 2                     # 'or'
+PRIORITY_AND = 3                    # 'and'
+PRIORITY_NOT = 4                    # 'not'
+PRIORITY_CMP = 5                    # '<', '>', '==', '>=', '<=', '!=',
                                     #   'in', 'not in', 'is', 'is not'
-PRIORITY_EXPR = 5
-PRIORITY_BOR = PRIORITY_EXPR = 6    # '|'
-PRIORITY_BXOR = 7                   # '^'
-PRIORITY_BAND = 8                   # '&'
-PRIORITY_SHIFT = 9                  # '<<', '>>'
-PRIORITY_ARITH = 10                 # '+', '-'
-PRIORITY_TERM = 11                  # '*', '@', '/', '%', '//'
-PRIORITY_FACTOR = 12                # unary '+', '-', '~'
-PRIORITY_POWER = 13                 # '**'
-PRIORITY_AWAIT = 14                 # 'await'
-PRIORITY_ATOM = 15
+PRIORITY_EXPR = 6
+PRIORITY_BOR = PRIORITY_EXPR = 7    # '|'
+PRIORITY_BXOR = 8                   # '^'
+PRIORITY_BAND = 9                   # '&'
+PRIORITY_SHIFT = 10                 # '<<', '>>'
+PRIORITY_ARITH = 11                 # '+', '-'
+PRIORITY_TERM = 12                  # '*', '@', '/', '%', '//'
+PRIORITY_FACTOR = 13                # unary '+', '-', '~'
+PRIORITY_POWER = 14                 # '**'
+PRIORITY_AWAIT = 15                 # 'await'
+PRIORITY_ATOM = 16
 
 class Parenthesizer(object):
     def __init__(self, visitor, priority):
@@ -56,7 +56,10 @@
     def append_ascii(self, s):
         self.builder.append_utf8(s, len(s))
 
-    def append_expr(self, node, priority):
+    def append_utf8(self, s):
+        self.builder.append(s)
+
+    def append_expr(self, node, priority=PRIORITY_TEST):
         level = self.level
         self.level = priority
         try:
@@ -64,10 +67,18 @@
         finally:
             self.level = level
 
+    def append_if_not_first(self, first, s):
+        if not first:
+            self.append_ascii(s)
+        return False
+
     def default_visitor(self, node):
         raise oefmt(self.space.w_SystemError,
                     "%T is not an expression", node)
 
+    def visit_Ellipsis(self, node):
+        self.append_ascii('...')
+
     def visit_Num(self, node):
         w_str = self.space.str(node.n)
         self.append_w_str(w_str)
@@ -209,7 +220,202 @@
             self.append_ascii(" else ")
             self.append_expr(node.orelse, PRIORITY_TEST + 1)
 
+    def visit_List(self, node):
+        if node.elts is None:
+            self.append_ascii("[]")
+            return
+        self.append_ascii("[")
+        for i, elt in enumerate(node.elts):
+            if i > 0:
+                self.append_ascii(", ")
+            self.append_expr(elt)
+        self.append_ascii("]")
 
+    def visit_Tuple(self, node):
+        if node.elts is None:
+            self.append_ascii("()")
+            return
+        self.append_ascii("(")
+        for i, elt in enumerate(node.elts):
+            if i > 0:
+                self.append_ascii(", ")
+            self.append_expr(elt)
+        if len(node.elts) == 1:
+            self.append_ascii(",")
+        self.append_ascii(")")
+
+    def visit_Set(self, node):
+        self.append_ascii("{")
+        for i, elt in enumerate(node.elts):
+            if i > 0:
+                self.append_ascii(", ")
+            self.append_expr(elt)
+        self.append_ascii("}")
+
+    def visit_Dict(self, node):
+        if node.keys is None:
+            self.append_ascii("{}")
+            return
+        self.append_ascii("{")
+        for i, key in enumerate(node.keys):
+            value = node.values[i]
+            if i > 0:
+                self.append_ascii(", ")
+            if key is not None:
+                self.append_expr(key)
+                self.append_ascii(": ")
+                self.append_expr(value)
+            else:
+                self.append_ascii("**")
+                self.append_expr(value)
+        self.append_ascii("}")
+
+    def append_generators(self, generators):
+        for generator in generators:
+            if generator.is_async:
+                self.append_ascii(' async for ')
+            else:
+                self.append_ascii(' for ')
+            self.append_expr(generator.target, PRIORITY_TUPLE)
+            self.append_ascii(' in ')
+            self.append_expr(generator.iter, PRIORITY_TEST + 1)
+            if generator.ifs:
+                for if_ in generator.ifs:
+                    self.append_ascii(' if ')
+                    self.append_expr(if_, PRIORITY_TEST + 1)
+
+    def visit_ListComp(self, node):
+        self.append_ascii('[')
+        self.append_expr(node.elt)
+        self.append_generators(node.generators)
+        self.append_ascii(']')
+
+    def visit_GeneratorExp(self, node):
+        self.append_ascii('(')
+        self.append_expr(node.elt)
+        self.append_generators(node.generators)
+        self.append_ascii(')')
+
+    def visit_SetComp(self, node):
+        self.append_ascii('{')
+        self.append_expr(node.elt)
+        self.append_generators(node.generators)
+        self.append_ascii('}')
+
+    def visit_Subscript(self, node):
+        self.append_expr(node.value, PRIORITY_ATOM)
+        self.append_ascii('[')
+        self.append_expr(node.slice)
+        self.append_ascii(']')
+
+    def visit_Index(self, node):
+        self.append_expr(node.value)
+
+    def visit_Slice(self, node):
+        if node.lower:
+            self.append_expr(node.lower)
+        self.append_ascii(':')
+        if node.upper:
+            self.append_expr(node.upper)
+        if node.step:
+            self.append_ascii(':')
+            self.append_expr(node.upper)
+
+    def visit_ExtSlice(self, node):
+        for i, slice in enumerate(node.dims):
+            if i > 0:
+                self.append_ascii(',')
+            self.append_expr(slice)
+
+    def visit_Yield(self, node):
+        if node.value:
+            self.append_ascii("(yield ")
+            self.append_expr(node.value)
+            self.append_ascii(")")
+        else:
+            self.append_ascii("(yield)")
+
+    def visit_YieldFrom(self, node):
+        self.append_ascii("(yield from ")
+        self.append_expr(node.value)
+        self.append_ascii(")")
+
+    def visit_Call(self, node):
+        self.append_expr(node.func, PRIORITY_ATOM)
+        args = node.args
+        if (args and len(args) == 1
+                and not node.keywords
+                and isinstance(args[0], ast.GeneratorExp)):
+            self.append_expr(args[0])
+            return
+
+        self.append_ascii('(')
+        first = True
+        if args:
+            for i, arg in enumerate(args):
+                first = self.append_if_not_first(first, ', ')
+                self.append_expr(arg)
+        if node.keywords:
+            for i, keyword in enumerate(node.keywords):
+                first = self.append_if_not_first(first, ', ')
+                if keyword.arg is None:
+                    self.append_ascii('**')
+                else:
+                    self.append_utf8(keyword.arg)
+                    self.append_ascii('=')
+                self.append_expr(keyword.value)
+        self.append_ascii(')')
+
+    def visit_Starred(self, node):
+        self.append_ascii('*')
+        self.append_expr(node.value, PRIORITY_EXPR)
+
+    def visit_arg(self, node):
+        self.append_utf8(node.arg)
+        if node.annotation:
+            # is this reachable? don't think so!
+            self.append_ascii(': ')
+            self.append_expr(node.annotation)
+
+    def visit_Lambda(self, node):
+        with self.maybe_parenthesize(PRIORITY_TEST):
+            args = node.args
+            if not args.args and not args.vararg and not args.kwarg and not args.kwonlyargs:
+                self.append_ascii("lambda: ")
+            else:
+                self.append_ascii("lambda ")
+                first = True
+                if args.defaults:
+                    default_count = len(args.defaults)
+                else:
+                    default_count = 0
+                if args.args:
+                    for i, arg in enumerate(args.args):
+                        first = self.append_if_not_first(first, ', ')
+                        di = i - (len(args.args) - default_count)
+                        self.append_expr(arg)
+                        if di >= 0:
+                            self.append_ascii('=')
+                            self.append_expr(args.defaults[di])
+                if args.vararg or args.kwonlyargs:
+                    first = self.append_if_not_first(first, ', ')
+                    self.append_ascii('*')
+                    if args.vararg:
+                        self.append_expr(args.vararg)
+                if args.kwonlyargs:
+                    for i, arg in enumerate(args.kwonlyargs):
+                        first = self.append_if_not_first(first, ', ')
+                        di = i - (len(args.kwonlyargs) - default_count)
+                        self.append_expr(arg)
+                        default = args.kw_defaults[i]
+                        if default:
+                            self.append_ascii('=')
+                            self.append_expr(default)
+                if args.kwarg:
+                    self.append_ascii('**')
+                    self.append_expr(args.kwarg)
+                self.append_ascii(': ')
+            self.append_expr(node.body)
 
 def unparse(space, ast):
     visitor = UnparseVisitor(space)


More information about the pypy-commit mailing list