Skip to content

Commit

Permalink
Merge pull request #7 from CodeVisionaries/add/multilines
Browse files Browse the repository at this point in the history
Add draft version of multi-line grammar
  • Loading branch information
gschnabel authored Oct 17, 2024
2 parents 2c8c401 + 2032a66 commit 7f6b3a3
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ To run the test suite, `pytest` is recommended, and can be done via:
```bash
pytest -v --maxfail=1 larktools/tests/test_suite.py
```

### Debugging

For grammar development using an alternative, less optimized parsing strategy can help avoiding rule conflicts: Use `earley` instead of `lalr`
8 changes: 7 additions & 1 deletion src/larktools/ebnf_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
start: assign_var
assign_var: VARNAME "=" arith_expr
assign_var: VARNAME "=" arith_expr
variable: VARNAME ("[" INDEX "]")*
VARNAME: LETTER (LETTER | DIGIT)*
Expand All @@ -26,6 +26,11 @@
// but without the fancy tree shaping directives explained at
// https://lark-parser.readthedocs.io/en/stable/tree_construction.html
line: arith_expr
multi_line_block: (line _NL? | _NL )*
arith_expr: sum
sum: product | addition | subtraction
addition: sum "+" product
Expand Down Expand Up @@ -63,4 +68,5 @@
%import common.WS_INLINE
%ignore WS_INLINE
%import common.NEWLINE -> _NL
"""
16 changes: 16 additions & 0 deletions src/larktools/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,22 @@ def eval_bracketed_arith_expr(node, env):
assert get_name(child) == "arith_expr"
return eval_arith_expr(child, env)

def eval_line(node, env):
# this is the content of a single line of input
child = get_children(node)[0]
child_name = get_name(child)
if child_name == "arith_expr":
return eval_arith_expr(child, env)

def eval_multi_line_block(node, env):
# this can be either an arithmetic expression or
# composed lines
children = get_children(node)
for child in children:
child_name = get_name(child)
assert child_name == "line"
res = eval_line(child, env)
return res

def eval_variable(node, env):
children = get_children(node)
Expand Down
32 changes: 32 additions & 0 deletions tests/test_syntax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pytest
from typing import Optional, Union

from lark import Lark

from larktools.ebnf_grammar import grammar
from larktools.evaluation import eval_multi_line_block


class SyntaxParser:
def __init__(self):
self.parser = Lark(grammar, parser="lalr", start="multi_line_block")
self.parse = self.parser.parse

def parse_and_eval(self, expression: str, env: Optional[Union[None, dict]] = None) -> Union[int, float]:
tree = self.parse(expression)
res = eval_multi_line_block(tree, {} if env is None else env)
return res


def _parse_and_assert(expression: str, expected: Union[int, float]) -> None:
parser = SyntaxParser()
res = parser.parse_and_eval(expression)
assert expected == res

def test_multi_line():
_parse_and_assert("5\n8", 8)
_parse_and_assert("\n\n\n8", 8)
_parse_and_assert("8\n\n\n", 8)
_parse_and_assert("5+5\n3+4\n1+2", 3)
_parse_and_assert("\n\n5\n\n3\n8", 8)

0 comments on commit 7f6b3a3

Please sign in to comment.