[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