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
16 changes: 15 additions & 1 deletion bnf/atsds_bnf/index.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
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";
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
Expand Down Expand Up @@ -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());
Comment on lines +113 to +118

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error listener setup code is duplicated in both parse() and unparse() functions. Consider extracting this into a helper function to reduce code duplication and improve maintainability. For example, you could create a function like setupThrowingErrorListener(lexer, parser) that removes default listeners and adds the throwing listener to both the lexer and parser.

Copilot uses AI. Check for mistakes.
const tree = parser.rule_pool();
const visitor = new ParseVisitor();
return visitor.visit(tree);
Expand All @@ -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);
Expand Down
30 changes: 30 additions & 0 deletions bnf/tests/test_parse_unparse.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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/);
});