From 535acf441730c1271f215fc4d33af1ba12adeb96 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 20 May 2019 17:41:40 -0500 Subject: [PATCH] Add list parsing, improve and test tuple parsing --- pymbolic/parser.py | 51 ++++++++++++++++++++++++++++++++++++++----- test/test_pymbolic.py | 21 ++++++++++++++++-- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/pymbolic/parser.py b/pymbolic/parser.py index 5f69c06..9896b52 100644 --- a/pymbolic/parser.py +++ b/pymbolic/parser.py @@ -92,12 +92,20 @@ def _join_to_slice(left, right): return Slice((left, right)) -class FinalizedTuple(tuple): - """A tuple that may not have elements appended to it, because it was - terminated by a close-paren. +class FinalizedContainer(object): + """A base class for containers that may not have elements appended to it, + because they were terminated by a closing delimiter. """ +class FinalizedTuple(tuple, FinalizedContainer): + pass + + +class FinalizedList(list, FinalizedContainer): + pass + + class Parser(object): lex_table = [ (_equal, pytools.lex.RE(r"==")), @@ -225,11 +233,42 @@ class Parser(object): self.parse_expression(pstate, _PREC_UNARY)) elif pstate.is_next(_openpar): pstate.advance() - left_exp = self.parse_expression(pstate) + + if pstate.is_next(_closepar): + left_exp = () + else: + # This is parsing expressions separated by commas, so it + # will return a tuple. Kind of the lazy way out. + left_exp = self.parse_expression(pstate) + pstate.expect(_closepar) pstate.advance() if isinstance(left_exp, tuple): + # These could just be plain parentheses. + + # Finalization prevents things from being appended + # to containers after their closing delimiter. left_exp = FinalizedTuple(left_exp) + elif pstate.is_next(_openbracket): + pstate.advance() + + if pstate.is_next(_closebracket): + left_exp = () + else: + # This is parsing expressions separated by commas, so it + # will return a tuple. Kind of the lazy way out. + left_exp = self.parse_expression(pstate) + + pstate.expect(_closebracket) + pstate.advance() + + # Finalization prevents things from being appended + # to containers after their closing delimiter. + if isinstance(left_exp, tuple): + left_exp = FinalizedList(left_exp) + else: + left_exp = FinalizedList([left_exp]) + else: left_exp = self.parse_terminal(pstate) @@ -394,8 +433,8 @@ class Parser(object): left_exp = (left_exp,) else: new_el = self.parse_expression(pstate, _PREC_COMMA) - if isinstance(left_exp, tuple) \ - and not isinstance(left_exp, FinalizedTuple): + if isinstance(left_exp, (tuple, list)) \ + and not isinstance(left_exp, FinalizedContainer): left_exp = left_exp + (new_el,) else: left_exp = (left_exp, new_el) diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py index 4b358ab..687fb78 100644 --- a/test/test_pymbolic.py +++ b/test/test_pymbolic.py @@ -220,8 +220,25 @@ def test_parser(): print(repr(parse("3::1"))) print(repr(parse(":5:1"))) print(repr(parse("3:5:1"))) - print(repr(parse("g[i,k]+2.0*h[i,k]"))) - print(repr(parse("g[i,k]+(+2.0)*h[i,k]"))) + + def assert_parse_roundtrip(expr_str): + expr = parse(expr_str) + from pymbolic.mapper.stringifier import StringifyMapper + strified = StringifyMapper()(expr) + assert strified == expr_str, (strified, expr_str) + + assert_parse_roundtrip("()") + assert_parse_roundtrip("(3,)") + + assert_parse_roundtrip("[x + 3, 3, 5]") + # FIXME: trailing commas not allowed + # assert parse("[x + 3, 3, 5]") == parse("[x + 3, 3, 5]") + assert_parse_roundtrip("[]") + assert_parse_roundtrip("[x]") + + assert_parse_roundtrip("g[i, k] + 2.0*h[i, k]") + parse("g[i,k]+(+2.0)*h[i, k]") + print(repr(parse("a - b - c"))) print(repr(parse("-a - -b - -c"))) print(repr(parse("- - - a - - - - b - - - - - c"))) -- GitLab