diff --git a/bnf/atsds_bnf/index.mjs b/bnf/atsds_bnf/index.mjs index 022df339..142e62de 100644 --- a/bnf/atsds_bnf/index.mjs +++ b/bnf/atsds_bnf/index.mjs @@ -1,4 +1,4 @@ -import { InputStream, CommonTokenStream } from "antlr4"; +import { InputStream, CommonTokenStream, ErrorListener } from "antlr4"; import DspLexer from "./DspLexer.js"; import DspParser from "./DspParser.js"; import DspVisitor from "./DspVisitor.js"; @@ -6,6 +6,12 @@ import DsLexer from "./DsLexer.js"; import DsParser from "./DsParser.js"; import DsVisitor from "./DsVisitor.js"; +class ThrowingErrorListener extends ErrorListener { + syntaxError(recognizer, offendingSymbol, line, column, msg, e) { + throw new Error(`line ${line}:${column} ${msg}`); + } +} + class ParseVisitor extends DspVisitor { visitRule_pool(ctx) { return ctx @@ -104,8 +110,12 @@ class UnparseVisitor extends DsVisitor { export function parse(input) { const chars = new InputStream(input); const lexer = new DspLexer(chars); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowingErrorListener()); const tokens = new CommonTokenStream(lexer); const parser = new DspParser(tokens); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowingErrorListener()); const tree = parser.rule_pool(); const visitor = new ParseVisitor(); return visitor.visit(tree); @@ -114,8 +124,12 @@ export function parse(input) { export function unparse(input) { const chars = new InputStream(input); const lexer = new DsLexer(chars); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowingErrorListener()); const tokens = new CommonTokenStream(lexer); const parser = new DsParser(tokens); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowingErrorListener()); const tree = parser.rule_pool(); const visitor = new UnparseVisitor(); return visitor.visit(tree); diff --git a/bnf/tests/test_parse_unparse.mjs b/bnf/tests/test_parse_unparse.mjs index d08de008..a44a27ed 100644 --- a/bnf/tests/test_parse_unparse.mjs +++ b/bnf/tests/test_parse_unparse.mjs @@ -143,3 +143,33 @@ test("roundtrip_unparse_parse", () => { const ds_result = parse(dsp_intermediate); expect(ds_result).toBe(ds_original); }); + +test("parse_error_missing_closing_parenthesis", () => { + // Test that parse throws error on missing closing parenthesis + const dsp_input = "(a + b -> c"; + expect(() => parse(dsp_input)).toThrow(/line 1:7 no viable alternative/); +}); + +test("parse_error_bad_syntax", () => { + // Test that parse throws error on bad syntax + const dsp_input = "a b c -> -> d"; + expect(() => parse(dsp_input)).toThrow(/line 1:2 mismatched input/); +}); + +test("parse_error_malformed_parentheses", () => { + // Test that parse throws error on malformed parentheses + const dsp_input = "()()()"; + expect(() => parse(dsp_input)).toThrow(/line 1:1 no viable alternative/); +}); + +test("unparse_error_incomplete_binary", () => { + // Test that unparse throws error on incomplete binary expression + const ds_input = "(binary"; + expect(() => unparse(ds_input)).toThrow(/line 1:7 mismatched input/); +}); + +test("unparse_error_malformed_function", () => { + // Test that unparse throws error on malformed function + const ds_input = "(function"; + expect(() => unparse(ds_input)).toThrow(/line 1:9 mismatched input/); +});