Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions bnf/apyds_bnf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
__all__ = ["parse", "unparse"]

from antlr4 import InputStream, CommonTokenStream
from antlr4.error.ErrorListener import ErrorListener
from .DspLexer import DspLexer
from .DspParser import DspParser
from .DspVisitor import DspVisitor
Expand All @@ -9,6 +10,11 @@
from .DsVisitor import DsVisitor


class ThrowingErrorListener(ErrorListener):
def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
raise Exception(f"line {line}:{column} {msg}")


class ParseVisitor(DspVisitor):
def visitRule_pool(self, ctx):
return "\n\n".join(self.visit(r) for r in ctx.rule_())
Expand Down Expand Up @@ -71,8 +77,12 @@ def visitBinary(self, ctx):
def parse(input: str) -> str:
chars = InputStream(input)
lexer = DspLexer(chars)
lexer.removeErrorListeners()
lexer.addErrorListener(ThrowingErrorListener())
tokens = CommonTokenStream(lexer)
parser = DspParser(tokens)
parser.removeErrorListeners()
parser.addErrorListener(ThrowingErrorListener())
tree = parser.rule_pool()
visitor = ParseVisitor()
return visitor.visit(tree)
Expand All @@ -81,8 +91,12 @@ def parse(input: str) -> str:
def unparse(input: str) -> str:
chars = InputStream(input)
lexer = DsLexer(chars)
lexer.removeErrorListeners()
lexer.addErrorListener(ThrowingErrorListener())
tokens = CommonTokenStream(lexer)
parser = DsParser(tokens)
parser.removeErrorListeners()
parser.addErrorListener(ThrowingErrorListener())
tree = parser.rule_pool()
visitor = UnparseVisitor()
return visitor.visit(tree)
36 changes: 36 additions & 0 deletions bnf/tests/test_parse_unparse.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from apyds_bnf import parse, unparse


Expand Down Expand Up @@ -143,3 +144,38 @@ def test_roundtrip_unparse_parse() -> None:
dsp_intermediate = unparse(ds_original)
ds_result = parse(dsp_intermediate)
assert ds_result == ds_original


def test_parse_error_missing_closing_parenthesis() -> None:
"""Test that parse throws error on missing closing parenthesis"""
dsp_input = "(a + b -> c"
with pytest.raises(Exception, match=r"line 1:7.*no viable alternative"):
parse(dsp_input)


def test_parse_error_bad_syntax() -> None:
"""Test that parse throws error on bad syntax"""
dsp_input = "a b c -> -> d"
with pytest.raises(Exception, match=r"line 1:2.*mismatched input"):
parse(dsp_input)


def test_parse_error_malformed_parentheses() -> None:
"""Test that parse throws error on malformed parentheses"""
dsp_input = "()()()"
with pytest.raises(Exception, match=r"line 1:1.*no viable alternative"):
parse(dsp_input)


def test_unparse_error_incomplete_binary() -> None:
"""Test that unparse throws error on incomplete binary expression"""
ds_input = "(binary"
with pytest.raises(Exception, match=r"line 1:7.*mismatched input"):
unparse(ds_input)


def test_unparse_error_malformed_function() -> None:
"""Test that unparse throws error on malformed function"""
ds_input = "(function"
with pytest.raises(Exception, match=r"line 1:9.*mismatched input"):
unparse(ds_input)