From 555f0b695de99aa1f8f36267b80259e462c765d0 Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Wed, 7 Jun 2023 23:24:02 +0200 Subject: [PATCH 01/10] feat: Validate input redeclarations --- src/main/typescript/ParserFacade.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/typescript/ParserFacade.ts b/src/main/typescript/ParserFacade.ts index cc9000a..545acef 100644 --- a/src/main/typescript/ParserFacade.ts +++ b/src/main/typescript/ParserFacade.ts @@ -137,5 +137,24 @@ export function validate(input) : Error[] { parser._errHandler = new CalcErrorStrategy(); const tree = parser.compilationUnit(); + + const scope = []; + + for (const input of tree.inputs) { + + const inputIdentifier = input.ID().symbol; + + if (scope.some(x => x === inputIdentifier.text)) { + errors.push(createErrorAt(inputIdentifier, "input already declared")); + } + else { + scope.push(inputIdentifier.text); + } + } + return errors; } + +function createErrorAt(node, message) { + return new Error(node.line, node.line, node.column + 1, node.column + node.stop - node.start + 2, message); +} From ca0ad537067fc8dcc00ca5d1d5edbd447d2d8797 Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Wed, 7 Jun 2023 23:36:08 +0200 Subject: [PATCH 02/10] test: Validate input redeclarations --- src/test/javascript/parsingTest.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/javascript/parsingTest.js b/src/test/javascript/parsingTest.js index 9294653..bf0326e 100644 --- a/src/test/javascript/parsingTest.js +++ b/src/test/javascript/parsingTest.js @@ -131,3 +131,19 @@ describe('Unrecognized tokens cause errors', function () { ]); }); }); + +describe('Semantic validation', function () { + describe('should report input already declared', function () { + let input = "input i\ninput i\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(2, 2, 7, 8, "input already declared") + ]); + }); + describe('should report input already declared twice', function () { + let input = "input i\ninput i\ninput i\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(2, 2, 7, 8, "input already declared"), + new parserFacade.Error(3, 3, 7, 8, "input already declared") + ]); + }); +}); From 75418889fd0939fd89c8e6c6510ca8edff57ec9a Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Wed, 7 Jun 2023 23:46:14 +0200 Subject: [PATCH 03/10] feat: Validate identifiers used in expressions are declared --- src/main/typescript/ParserFacade.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/typescript/ParserFacade.ts b/src/main/typescript/ParserFacade.ts index 545acef..bd3e87c 100644 --- a/src/main/typescript/ParserFacade.ts +++ b/src/main/typescript/ParserFacade.ts @@ -138,7 +138,7 @@ export function validate(input) : Error[] { const tree = parser.compilationUnit(); - const scope = []; + const scope = []; // ? Should be a Set but can't use it unless typescript targets ES6+ for (const input of tree.inputs) { @@ -152,6 +152,30 @@ export function validate(input) : Error[] { } } + for (const calculation of tree.calcs) { + validateExpression(calculation.value); + + if (!scope.some(x => x === calculation.target.text)) { + scope.push(calculation.target.text); + } + } + + function validateExpression(expression) { + for (const child of expression.children) { + if (child.children) { + validateExpression(child); + } + else if (child.symbol && child.symbol.type == CalcLexer.ID) { + + const symbol = child.symbol; + + if (!scope.some(x => x == symbol.text)) { + errors.push(createErrorAt(symbol, "undeclared symbol")); + } + } + } + } + return errors; } From e2886acfac8578f585bd0c740f3afab40679b011 Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Thu, 8 Jun 2023 00:02:26 +0200 Subject: [PATCH 04/10] test: Validate identifiers used in expressions are declared --- src/test/javascript/parsingTest.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/test/javascript/parsingTest.js b/src/test/javascript/parsingTest.js index bf0326e..be52c8a 100644 --- a/src/test/javascript/parsingTest.js +++ b/src/test/javascript/parsingTest.js @@ -64,12 +64,12 @@ describe('Basic parsing of simple script', function () { describe('Validation of simple errors on single lines', function () { describe('should have recognize missing operand', function () { - parseAndCheckErrors("o = i + \n", [ + parseAndCheckErrors("o = 1 + \n", [ new parserFacade.Error(1, 1, 8, 9, "mismatched input '\\n' expecting {NUMBER_LIT, ID, '(', '-'}") ]); }); describe('should have recognize extra operator', function () { - parseAndCheckErrors("o = i +* 2 \n", [ + parseAndCheckErrors("o = 1 +* 2 \n", [ new parserFacade.Error(1, 1, 7, 8, "extraneous input '*' expecting {NUMBER_LIT, ID, '(', '-'}") ]); }); @@ -146,4 +146,16 @@ describe('Semantic validation', function () { new parserFacade.Error(3, 3, 7, 8, "input already declared") ]); }); + describe('should report undeclared symbol', function () { + let input = "o = i\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(1, 1, 5, 6, "undeclared symbol") + ]); + }); + describe('should report undeclared symbol in line 4', function () { + let input = "input i\no = i\no = o\no = u\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(4, 4, 5, 6, "undeclared symbol") + ]); + }); }); From b1445fb39ec3925e1db1f0dc9850b8d2c641ec13 Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Thu, 8 Jun 2023 00:05:10 +0200 Subject: [PATCH 05/10] feat: Validate output identifiers are declared --- src/main/typescript/ParserFacade.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/typescript/ParserFacade.ts b/src/main/typescript/ParserFacade.ts index bd3e87c..92b2d7a 100644 --- a/src/main/typescript/ParserFacade.ts +++ b/src/main/typescript/ParserFacade.ts @@ -160,6 +160,15 @@ export function validate(input) : Error[] { } } + for (const output of tree.outputs) { + + const outputIdentifier = output.ID().symbol; + + if (!scope.some(x => x === outputIdentifier.text)) { + errors.push(createErrorAt(outputIdentifier, "undeclared symbol")); + } + } + function validateExpression(expression) { for (const child of expression.children) { if (child.children) { From d4c4f6969c3838e6d4ffcf0251e1a345c8105e10 Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Thu, 8 Jun 2023 00:08:00 +0200 Subject: [PATCH 06/10] test: Validate output identifiers are declared --- src/test/javascript/parsingTest.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/javascript/parsingTest.js b/src/test/javascript/parsingTest.js index be52c8a..b641411 100644 --- a/src/test/javascript/parsingTest.js +++ b/src/test/javascript/parsingTest.js @@ -158,4 +158,16 @@ describe('Semantic validation', function () { new parserFacade.Error(4, 4, 5, 6, "undeclared symbol") ]); }); + describe('should report undeclared symbol for output', function () { + let input = "output o\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(1, 1, 8, 9, "undeclared symbol") + ]); + }); + describe('should report undeclared symbol for second output', function () { + let input = "input i\no = i\noutput o\noutput x\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(4, 4, 8, 9, "undeclared symbol") + ]); + }); }); From ab4eb5523d39ad24d4ade017598c658e89c1b0a2 Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Thu, 8 Jun 2023 00:12:20 +0200 Subject: [PATCH 07/10] fix: Handle incomplete code cases --- src/main/typescript/ParserFacade.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/typescript/ParserFacade.ts b/src/main/typescript/ParserFacade.ts index 92b2d7a..c4f2b18 100644 --- a/src/main/typescript/ParserFacade.ts +++ b/src/main/typescript/ParserFacade.ts @@ -142,6 +142,8 @@ export function validate(input) : Error[] { for (const input of tree.inputs) { + if (input.ID() === null) continue; + const inputIdentifier = input.ID().symbol; if (scope.some(x => x === inputIdentifier.text)) { @@ -162,6 +164,8 @@ export function validate(input) : Error[] { for (const output of tree.outputs) { + if (output.ID() === null) continue; + const outputIdentifier = output.ID().symbol; if (!scope.some(x => x === outputIdentifier.text)) { @@ -170,6 +174,8 @@ export function validate(input) : Error[] { } function validateExpression(expression) { + if (expression === null || expression.children === null) return; + for (const child of expression.children) { if (child.children) { validateExpression(child); From 7ef6f14c83ca0ab194c35f1d232e47d885f12cb8 Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Thu, 8 Jun 2023 00:51:02 +0200 Subject: [PATCH 08/10] test: Handle incomplete code cases --- src/test/javascript/parsingTest.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/javascript/parsingTest.js b/src/test/javascript/parsingTest.js index b641411..78731eb 100644 --- a/src/test/javascript/parsingTest.js +++ b/src/test/javascript/parsingTest.js @@ -171,3 +171,25 @@ describe('Semantic validation', function () { ]); }); }); + +describe('Semantic validation of examples being edited', function () { + describe('deleting input identifier', function () { + let input = "input a\ninput\ninput a\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(2, 2, 5, 6, "missing ID at '\\n'"), + new parserFacade.Error(3, 3, 7, 8, "input already declared") + ]); + }); + describe('deleting calculation value still declares the target', function () { + let input = "a = 1 + 1\nb = \nc = a + b\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(2, 2, 4, 5, "mismatched input '\\n' expecting {NUMBER_LIT, ID, '(', '-'}"), + ]); + }); + describe('deleting output identifier', function () { + let input = "input a\noutput\noutput a\n"; + parseAndCheckErrors(input, [ + new parserFacade.Error(2, 2, 6, 7, "missing ID at '\\n'"), + ]); + }); +}); From 031f86c5c1fc3d04f27495741646a94e0525195c Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Thu, 8 Jun 2023 00:51:27 +0200 Subject: [PATCH 09/10] fix: Handle incomplete code edge case --- src/main/typescript/ParserFacade.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/typescript/ParserFacade.ts b/src/main/typescript/ParserFacade.ts index c4f2b18..206eee1 100644 --- a/src/main/typescript/ParserFacade.ts +++ b/src/main/typescript/ParserFacade.ts @@ -168,6 +168,8 @@ export function validate(input) : Error[] { const outputIdentifier = output.ID().symbol; + if (outputIdentifier.text === "") continue; + if (!scope.some(x => x === outputIdentifier.text)) { errors.push(createErrorAt(outputIdentifier, "undeclared symbol")); } From 395b60f5d0b192383195fc93fa5d73372627c7d7 Mon Sep 17 00:00:00 2001 From: Martin Azpillaga Aldalur Date: Thu, 8 Jun 2023 00:55:17 +0200 Subject: [PATCH 10/10] docs: Specify editor's features in readme --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index a351e65..8823e1e 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,13 @@ It shows how to integrate ANTLR with monaco. ![Calc Example](./doc/images/calc_example.png) +## Features + +* Text editing provided by [monaco](https://github.com/microsoft/monaco-editor) +* Syntax highlighting for custom language +* Syntax error reporting with expected alternatives +* Semantic error reporting for undeclared and redeclared variables + ## Generating the lexer and the parser ```