From 94dd9dbf75d25b09659a77775f2ed3bab0a6c2bf Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Tue, 11 Jun 2019 16:11:45 -0500 Subject: [PATCH 01/11] makes str(If) python-like --- pymbolic/mapper/stringifier.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pymbolic/mapper/stringifier.py b/pymbolic/mapper/stringifier.py index 7c680ef..bf96b27 100644 --- a/pymbolic/mapper/stringifier.py +++ b/pymbolic/mapper/stringifier.py @@ -351,15 +351,15 @@ class StringifyMapper(pymbolic.mapper.Mapper): type_name, self.rec(expr.child, PREC_NONE, *args, **kwargs)) def map_if(self, expr, enclosing_prec, *args, **kwargs): - return "If(%s, %s, %s)" % ( - self.rec(expr.condition, PREC_NONE, *args, **kwargs), + return "(%s if %s else %s)" % ( self.rec(expr.then, PREC_NONE, *args, **kwargs), + self.rec(expr.condition, PREC_NONE, *args, **kwargs), self.rec(expr.else_, PREC_NONE, *args, **kwargs)) def map_if_positive(self, expr, enclosing_prec, *args, **kwargs): - return "If(%s > 0, %s, %s)" % ( - self.rec(expr.criterion, PREC_NONE, *args, **kwargs), + return "(%s if %s > 0 else %s)" % ( self.rec(expr.then, PREC_NONE, *args, **kwargs), + self.rec(expr.criterion, PREC_NONE, *args, **kwargs), self.rec(expr.else_, PREC_NONE, *args, **kwargs)) def map_min(self, expr, enclosing_prec, *args, **kwargs): -- GitLab From 9807368e1c282cd4cd5da96dc1cf27dbc86a43ce Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 10:49:35 -0500 Subject: [PATCH 02/11] adds parsing for if --- pymbolic/parser.py | 15 +++++++++++++++ test/test_pymbolic.py | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/pymbolic/parser.py b/pymbolic/parser.py index 9896b52..4955993 100644 --- a/pymbolic/parser.py +++ b/pymbolic/parser.py @@ -60,6 +60,8 @@ _rightshift = intern("rightshift") _and = intern("and") _or = intern("or") _not = intern("not") +_if = intern("if") +_else = intern("else") _bitwiseand = intern("bitwiseand") _bitwiseor = intern("bitwiseor") @@ -126,6 +128,8 @@ class Parser(object): (_and, pytools.lex.RE(r"and\b")), (_or, pytools.lex.RE(r"or\b")), (_not, pytools.lex.RE(r"not\b")), + (_if, pytools.lex.RE(r"if\b")), + (_else, pytools.lex.RE(r"else\b")), (_imaginary, (_float, pytools.lex.RE("j"))), (_float, ("|", @@ -316,6 +320,17 @@ class Parser(object): pstate.expect(_closebracket) pstate.advance() did_something = True + elif next_tag is _if and _PREC_CALL > min_precedence: + from pymbolic.primitives import If + then_expr = left_exp + pstate.advance() + pstate.expect_not_end() + condition = self.parse_expression(pstate) + pstate.expect(_else) + pstate.advance() + else_expr = self.parse_expression(pstate) + left_exp = If(condition, then_expr, else_expr) + did_something = True elif next_tag is _dot and _PREC_CALL > min_precedence: pstate.advance() pstate.expect(_identifier) diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py index 687fb78..8d55970 100644 --- a/test/test_pymbolic.py +++ b/test/test_pymbolic.py @@ -600,6 +600,14 @@ def test_make_sym_vector(): assert len(make_sym_vector("vec", [1, 2, 3])) == 3 +def test_parse_if(): + from pymbolic.primitives import If, Comparison, Variable + expected = If(Comparison(Variable('i'), '>=', 0), Variable('i'), + If(Comparison(Variable('i'), '<', -1), 0, 10)) + parsed = parse('i if i>=0 else (0 if i<-1 else 10)') + assert expected == parsed + + if __name__ == "__main__": import sys if len(sys.argv) > 1: -- GitLab From 92c711b7018c93c788ee97a79c6f718c0cf024ca Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 11:37:34 -0500 Subject: [PATCH 03/11] correct the precedence of If --- pymbolic/parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pymbolic/parser.py b/pymbolic/parser.py index 4955993..457ab9f 100644 --- a/pymbolic/parser.py +++ b/pymbolic/parser.py @@ -79,6 +79,7 @@ _PREC_BITWISE_AND = 130 _PREC_COMPARISON = 200 _PREC_SHIFT = 205 +_PREC_IF = 207 _PREC_PLUS = 210 _PREC_TIMES = 220 _PREC_POWER = 230 @@ -320,7 +321,7 @@ class Parser(object): pstate.expect(_closebracket) pstate.advance() did_something = True - elif next_tag is _if and _PREC_CALL > min_precedence: + elif next_tag is _if and _PREC_IF > min_precedence: from pymbolic.primitives import If then_expr = left_exp pstate.advance() -- GitLab From af3e50bac9fb97e0131255eb316ac30ac1b638c4 Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 11:38:06 -0500 Subject: [PATCH 04/11] adds a more harsh test --- test/test_pymbolic.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py index 8d55970..5f2368d 100644 --- a/test/test_pymbolic.py +++ b/test/test_pymbolic.py @@ -264,6 +264,12 @@ def test_parser(): assert parse("f(x,(y,z),z, name=15, name2=17)") == f( x, (y, z), z, name=15, name2=17) + from pymbolic.primitives import If, Comparison, Variable, Sum + assert (parse('5+i if i>=0 else (0 if i<-1 else 10)') == + If(Comparison(Variable('i'), '>=', 0), Sum((5, Variable('i'))), + If(Comparison(Variable('i'), '<', -1), 0, 10))) + assert_parse_roundtrip('(5 + i if i >= 0 else (0 if i < -1 else 10))') + # }}} @@ -600,14 +606,6 @@ def test_make_sym_vector(): assert len(make_sym_vector("vec", [1, 2, 3])) == 3 -def test_parse_if(): - from pymbolic.primitives import If, Comparison, Variable - expected = If(Comparison(Variable('i'), '>=', 0), Variable('i'), - If(Comparison(Variable('i'), '<', -1), 0, 10)) - parsed = parse('i if i>=0 else (0 if i<-1 else 10)') - assert expected == parsed - - if __name__ == "__main__": import sys if len(sys.argv) > 1: -- GitLab From 1356df32a199bcbf9b7ccb436bb57f8efe3825b9 Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 12:09:00 -0500 Subject: [PATCH 05/11] flake8 fixes --- test/test_pymbolic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py index 5f2368d..7b93254 100644 --- a/test/test_pymbolic.py +++ b/test/test_pymbolic.py @@ -265,8 +265,8 @@ def test_parser(): x, (y, z), z, name=15, name2=17) from pymbolic.primitives import If, Comparison, Variable, Sum - assert (parse('5+i if i>=0 else (0 if i<-1 else 10)') == - If(Comparison(Variable('i'), '>=', 0), Sum((5, Variable('i'))), + assert (parse('5+i if i>=0 else (0 if i<-1 else 10)') + == If(Comparison(Variable('i'), '>=', 0), Sum((5, Variable('i'))), If(Comparison(Variable('i'), '<', -1), 0, 10))) assert_parse_roundtrip('(5 + i if i >= 0 else (0 if i < -1 else 10))') -- GitLab From 5086d90f3c8e8f50619cf1382dc935ea745a9f0b Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 12:14:30 -0500 Subject: [PATCH 06/11] changes the precedence in accordance with python operator precedence --- pymbolic/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymbolic/parser.py b/pymbolic/parser.py index 457ab9f..4b0943e 100644 --- a/pymbolic/parser.py +++ b/pymbolic/parser.py @@ -70,6 +70,7 @@ _bitwisenot = intern("bitwisenot") _PREC_COMMA = 5 # must be > 1 (1 is used by fortran-to-cl) _PREC_SLICE = 10 +_PREC_IF = 75 _PREC_LOGICAL_OR = 80 _PREC_LOGICAL_AND = 90 @@ -79,7 +80,6 @@ _PREC_BITWISE_AND = 130 _PREC_COMPARISON = 200 _PREC_SHIFT = 205 -_PREC_IF = 207 _PREC_PLUS = 210 _PREC_TIMES = 220 _PREC_POWER = 230 -- GitLab From 8f6a06e3ad22c87f89569826a5ec61a38a444957 Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 12:49:19 -0500 Subject: [PATCH 07/11] adds the test cases suggested by mattwala --- test/test_pymbolic.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py index 7b93254..0e4e1bf 100644 --- a/test/test_pymbolic.py +++ b/test/test_pymbolic.py @@ -25,6 +25,7 @@ THE SOFTWARE. import pymbolic.primitives as prim import pytest from pymbolic import parse +from pytools.lex import ParseError from pymbolic.mapper import IdentityMapper @@ -270,6 +271,11 @@ def test_parser(): If(Comparison(Variable('i'), '<', -1), 0, 10))) assert_parse_roundtrip('(5 + i if i >= 0 else (0 if i < -1 else 10))') + with pytest.raises(ParseError): + parse("0 if 1 if 2 else 3 else 4") + + assert parse("0 if (1 if 2 else 3) else 4") == If(If(2, 1, 3), 0, 4) + # }}} -- GitLab From 7b541660dc0c9212df186a56b8f2036fd7d1b2ff Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 14:10:36 -0500 Subject: [PATCH 08/11] parses nested if statement correctly --- pymbolic/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymbolic/parser.py b/pymbolic/parser.py index 4b0943e..d0e49cd 100644 --- a/pymbolic/parser.py +++ b/pymbolic/parser.py @@ -326,7 +326,7 @@ class Parser(object): then_expr = left_exp pstate.advance() pstate.expect_not_end() - condition = self.parse_expression(pstate) + condition = self.parse_expression(pstate, _PREC_LOGICAL_OR) pstate.expect(_else) pstate.advance() else_expr = self.parse_expression(pstate) -- GitLab From 45ceb4b4c3fbd7394b5650a7db07f4a516b18ae4 Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 14:11:26 -0500 Subject: [PATCH 09/11] uses parenthesize_if_needed in map_if --- pymbolic/mapper/stringifier.py | 21 +++++++++++++-------- test/test_pymbolic.py | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/pymbolic/mapper/stringifier.py b/pymbolic/mapper/stringifier.py index bf96b27..ce6f0b8 100644 --- a/pymbolic/mapper/stringifier.py +++ b/pymbolic/mapper/stringifier.py @@ -69,6 +69,7 @@ PREC_BITWISE_OR = 7 PREC_COMPARISON = 6 PREC_LOGICAL_AND = 5 PREC_LOGICAL_OR = 4 +PREC_IF = 3 PREC_NONE = 0 @@ -351,16 +352,20 @@ class StringifyMapper(pymbolic.mapper.Mapper): type_name, self.rec(expr.child, PREC_NONE, *args, **kwargs)) def map_if(self, expr, enclosing_prec, *args, **kwargs): - return "(%s if %s else %s)" % ( - self.rec(expr.then, PREC_NONE, *args, **kwargs), - self.rec(expr.condition, PREC_NONE, *args, **kwargs), - self.rec(expr.else_, PREC_NONE, *args, **kwargs)) + return self.parenthesize_if_needed( + "%s if %s else %s" % ( + self.rec(expr.then, PREC_LOGICAL_OR, *args, **kwargs), + self.rec(expr.condition, PREC_LOGICAL_OR, *args, **kwargs), + self.rec(expr.else_, PREC_LOGICAL_OR, *args, **kwargs)), + enclosing_prec, PREC_IF) def map_if_positive(self, expr, enclosing_prec, *args, **kwargs): - return "(%s if %s > 0 else %s)" % ( - self.rec(expr.then, PREC_NONE, *args, **kwargs), - self.rec(expr.criterion, PREC_NONE, *args, **kwargs), - self.rec(expr.else_, PREC_NONE, *args, **kwargs)) + return self.parenthesize_if_needed( + "%s if %s > 0 else %s" % ( + self.rec(expr.then, PREC_LOGICAL_OR, *args, **kwargs), + self.rec(expr.criterion, PREC_LOGICAL_OR, *args, **kwargs), + self.rec(expr.else_, PREC_LOGICAL_OR, *args, **kwargs)), + enclosing_prec, PREC_IF) def map_min(self, expr, enclosing_prec, *args, **kwargs): what = type(expr).__name__.lower() diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py index 0e4e1bf..dd29ad9 100644 --- a/test/test_pymbolic.py +++ b/test/test_pymbolic.py @@ -269,7 +269,7 @@ def test_parser(): assert (parse('5+i if i>=0 else (0 if i<-1 else 10)') == If(Comparison(Variable('i'), '>=', 0), Sum((5, Variable('i'))), If(Comparison(Variable('i'), '<', -1), 0, 10))) - assert_parse_roundtrip('(5 + i if i >= 0 else (0 if i < -1 else 10))') + assert_parse_roundtrip('5 + i if i >= 0 else (0 if i < -1 else 10)') with pytest.raises(ParseError): parse("0 if 1 if 2 else 3 else 4") -- GitLab From 1c2624c4021f872b45decd93efcc360a4180cdec Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 15:41:15 -0500 Subject: [PATCH 10/11] handles minor bugs in ast interop --- pymbolic/interop/ast.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pymbolic/interop/ast.py b/pymbolic/interop/ast.py index 3b871ff..147a887 100644 --- a/pymbolic/interop/ast.py +++ b/pymbolic/interop/ast.py @@ -103,7 +103,7 @@ def _mult(x, y): def _neg(x): - return p.Product((-1, x),) + return -x class ASTToPymbolic(ASTMapper): @@ -144,14 +144,14 @@ class ASTToPymbolic(ASTMapper): def map_UnaryOp(self, expr): # noqa try: - op_constructor = self.unary_op_map[expr.op] + op_constructor = self.unary_op_map[type(expr.op)] except KeyError: raise NotImplementedError( "%s does not know how to map operator '%s'" % (type(self).__name__, type(expr.op).__name__)) - return op_constructor(self.rec(expr.left), self.rec(expr.right)) + return op_constructor(self.rec(expr.operand)) def map_IfExp(self, expr): # noqa # (expr test, expr body, expr orelse) -- GitLab From 2abe6b63b59120a68e5d96fc1edbe1edce6e50c3 Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Wed, 12 Jun 2019 15:41:49 -0500 Subject: [PATCH 11/11] adds the utility function parsed_same_as_python --- test/test_pymbolic.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py index dd29ad9..bc8b6ed 100644 --- a/test/test_pymbolic.py +++ b/test/test_pymbolic.py @@ -228,6 +228,21 @@ def test_parser(): strified = StringifyMapper()(expr) assert strified == expr_str, (strified, expr_str) + def assert_parsed_same_as_python(expr_str): + # makes sure that has only one line + expr_str, = expr_str.split('\n') + from pymbolic.interop.ast import ASTToPymbolic + import ast + ast2p = ASTToPymbolic() + try: + expr_parsed_by_python = ast2p(ast.parse(expr_str).body[0].value) + except SyntaxError: + with pytest.raises(ParseError): + parse(expr_str) + else: + expr_parsed_by_pymbolic = parse(expr_str) + assert expr_parsed_by_python == expr_parsed_by_pymbolic + assert_parse_roundtrip("()") assert_parse_roundtrip("(3,)") @@ -265,16 +280,9 @@ def test_parser(): assert parse("f(x,(y,z),z, name=15, name2=17)") == f( x, (y, z), z, name=15, name2=17) - from pymbolic.primitives import If, Comparison, Variable, Sum - assert (parse('5+i if i>=0 else (0 if i<-1 else 10)') - == If(Comparison(Variable('i'), '>=', 0), Sum((5, Variable('i'))), - If(Comparison(Variable('i'), '<', -1), 0, 10))) - assert_parse_roundtrip('5 + i if i >= 0 else (0 if i < -1 else 10)') - - with pytest.raises(ParseError): - parse("0 if 1 if 2 else 3 else 4") - - assert parse("0 if (1 if 2 else 3) else 4") == If(If(2, 1, 3), 0, 4) + assert_parsed_same_as_python('5+i if i>=0 else (0 if i<-1 else 10)') + assert_parsed_same_as_python("0 if 1 if 2 else 3 else 4") + assert_parsed_same_as_python("0 if (1 if 2 else 3) else 4") # }}} -- GitLab