From e22fcc534f85bac8772de10ab0f07df99479d98c Mon Sep 17 00:00:00 2001 From: sir_onze Date: Mon, 12 Jun 2023 00:22:26 +0100 Subject: [PATCH 1/2] adding the hability to run code from the editor using antlr visitors to evaluate the code --- .gitignore | 2 + build.gradle | 2 +- package.json | 3 +- src/main/antlr/.gitignore | 1 + src/main/antlr/CalcParser.g4 | 2 +- src/main/css/style.css | 43 ++++++++++- src/main/html/index.html | 82 +++++++++++++++++++- src/main/javascript/EvalVisitor.js | 107 +++++++++++++++++++++++++++ src/main/javascript/InputsVisitor.js | 28 +++++++ src/main/typescript/ParserFacade.ts | 32 ++++++++ 10 files changed, 296 insertions(+), 6 deletions(-) create mode 100644 src/main/javascript/EvalVisitor.js create mode 100644 src/main/javascript/InputsVisitor.js diff --git a/.gitignore b/.gitignore index 91b377e..4319549 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .gradle node_modules dist +.DS_Store + diff --git a/build.gradle b/build.gradle index afcc828..3386e55 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,6 @@ task generateParser(type:JavaExec) { outputs.file("$GEN_JS_SRC/${parserName}.tokens") main = 'org.antlr.v4.Tool' classpath = sourceSets.main.runtimeClasspath - args = ['-Dlanguage=JavaScript', "${parserName}.g4", '-no-listener', '-no-visitor', '-o', '../../main-generated/javascript'] + args = ['-Dlanguage=JavaScript', "${parserName}.g4", '-no-listener','-visitor', '-o', '../../main-generated/javascript'] workingDir = ANTLR_SRC } diff --git a/package.json b/package.json index 6846229..709b300 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "monaco-editor": "^0.17.1" }, "scripts": { - "test": "mocha" + "test": "mocha", + "start": "./gradlew generateParser; cp ./src/main/javascript/EvalVisitor.js ./src/main-generated/javascript; cp ./src/main/javascript/InputsVisitor.js ./src/main-generated/javascript; tsc ; webpack ; cd server ; ../gradlew runServer" } } diff --git a/src/main/antlr/.gitignore b/src/main/antlr/.gitignore index 1f31450..a289686 100644 --- a/src/main/antlr/.gitignore +++ b/src/main/antlr/.gitignore @@ -1 +1,2 @@ *.tokens +.DS_Store diff --git a/src/main/antlr/CalcParser.g4 b/src/main/antlr/CalcParser.g4 index 9bd0dbb..f582fe0 100644 --- a/src/main/antlr/CalcParser.g4 +++ b/src/main/antlr/CalcParser.g4 @@ -27,7 +27,7 @@ calc: expression: NUMBER_LIT - | ID + | id=ID | LPAREN expression RPAREN | expression operator=(MUL|DIV) expression | expression operator=(MINUS|PLUS) expression diff --git a/src/main/css/style.css b/src/main/css/style.css index f485fe4..f9b8c0c 100644 --- a/src/main/css/style.css +++ b/src/main/css/style.css @@ -4,4 +4,45 @@ h2 { body { background-color: #eee; -} \ No newline at end of file +} + +.modal-content { + background-color: #fefefe; + margin: 15% auto; + padding: 20px; + border: 1px solid #888; + width: 30%; + left:30%; + top: 30%; + position: absolute; + z-index: 1; +} + +input[type="text"] { + width: 95%; + margin-bottom: 10px; +} + +.output{ + color: #7132a8; + font-size: 14px; + padding: 5px; + display: flex; + align-items: center; + gap: 2px; +} + +.output p::after { + content: ""; + width: 0.5px; + height: 14px; + background:#7132a8; + display: inline-block; + animation: cursor-blink 1.5s steps(2) infinite; +} + +@keyframes cursor-blink { + 0% { + opacity: 0; + } +} diff --git a/src/main/html/index.html b/src/main/html/index.html index cacabd1..936920a 100644 --- a/src/main/html/index.html +++ b/src/main/html/index.html @@ -9,14 +9,27 @@

Calc Editor

+
+ +
+ +
+
+

>>

+
diff --git a/src/main/javascript/EvalVisitor.js b/src/main/javascript/EvalVisitor.js new file mode 100644 index 0000000..63145d9 --- /dev/null +++ b/src/main/javascript/EvalVisitor.js @@ -0,0 +1,107 @@ +// Generated from CalcParser.g4 by ANTLR 4.7.2 +// jshint ignore: start +var antlr4 = require('antlr4/index'); +var CalcParser = require('../../main-generated/javascript/CalcParser').CalcParser; +// This class defines a complete generic visitor for a parse tree produced by CalcParser. + +let variablesMapping; +let errors=[]; +function EvalVisitor() { + antlr4.tree.ParseTreeVisitor.call(this); + return this; +} + +EvalVisitor.prototype = Object.create(antlr4.tree.ParseTreeVisitor.prototype); +EvalVisitor.prototype.constructor = EvalVisitor; + +// Visit a parse tree produced by CalcParser#compilationUnit. +EvalVisitor.prototype.visitCompilationUnit = function(ctx,variables) { + // stores the variable values to be evaluated + variablesMapping=variables; + errors=[]; + // stores the output values to be printed in the html + let outputVariables={}; + // visit all calc nodes + for (let i = 0; i < ctx.calcs.length; i++) { + this.visitCalc(ctx.calcs[i]) + } + // visit all output nodes + for (let i = 0; i < ctx.outputs.length; i++) { + outputVariables[this.visitOutput(ctx.outputs[i])]=true; + } + // filter variables for the ones to be showed in the output, for easier handling in the html + variablesMapping = variablesMapping.filter(elem=> outputVariables[elem.variable] && outputVariables[elem.variable]===true); + return {output:variablesMapping,errors:errors}; +}; + +// Visit a parse tree produced by CalcParser#output. +EvalVisitor.prototype.visitOutput = function(ctx) { + // Only return the variable name + return ctx.getChild(1).getText(); +}; + + +// Visit a parse tree produced by CalcParser#calc. +EvalVisitor.prototype.visitCalc = function(ctx) { + // Get the current variable name being analyzed + let variable=ctx.target.text; + // evaluate the current expression + let calculatedExpr = this.visitExpression(ctx.value); + let found=false; + // Look for the value in the variablesMapping to update or to add for use in next calcs + for (let i = 0; i Date: Mon, 12 Jun 2023 00:36:54 +0100 Subject: [PATCH 2/2] adding tests for visitor calls --- src/test/javascript/visitorTest.js | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/test/javascript/visitorTest.js diff --git a/src/test/javascript/visitorTest.js b/src/test/javascript/visitorTest.js new file mode 100644 index 0000000..5a2babf --- /dev/null +++ b/src/test/javascript/visitorTest.js @@ -0,0 +1,55 @@ +let assert = require('assert'); +let parserFacade = require('../../main-generated/javascript/ParserFacade.js'); + +describe('Inputs Visitor tests', function () { + let inputs = parserFacade.getInputs("input x\ninput y\n"); + + it('should return 3 inputs', function() { + assert.equal(inputs.length, 2); + }); + + it('should have x in position 0 and y in position 1', function() { + assert.equal(inputs[0].variable, "x"); + assert.equal(inputs[1].variable, "y"); + }); + + it('should have empty values', function() { + assert.equal(inputs[0].value, ""); + assert.equal(inputs[1].value, ""); + }); +}); + +describe('Eval/Expression Calculator Visitor tests', function () { + let output = parserFacade.calculateExpression("input x\ninput y\nz=x*y\noutput z\n",[{variable: "x",value:1},{variable: "y",value:2}]); + + it('should return 0 errors', function() { + assert.equal(output.errors.length,0); + }); + + it('should return variables from the output with correct values', function() { + assert.equal(output.output.length,1); + assert.equal(output.output[0].variable,'z'); + assert.equal(output.output[0].value,2); + }) +}); + +describe('Eval/Expression Calculator Visitor error tests', function () { + let output = parserFacade.calculateExpression("input x\ninput y\nz=x/y\noutput z\n",[{variable: "x",value:1},{variable: "y",value:0}]); + it('should return division by 0 error', function() { + assert.equal(output.errors.length,1); + assert.equal(output.errors[0].message,"Error: Division by 0 error."); + }); + + it('should return division by 0 error', function() { + output = parserFacade.calculateExpression("input x\ninput y\nz=x/y\noutput z\n",[{variable: "x",value:1},{variable: "y",value:0}]); + assert.equal(output.errors.length,1); + assert.equal(output.errors[0].message,"Error: Division by 0 error."); + }); + + + it('should return variable not defined error', function() { + output = parserFacade.calculateExpression("input x\ninput y\nz=x*k\noutput z\n",[{variable: "x",value:1},{variable: "y",value:0}]); + assert.equal(output.errors.length,1); + assert.equal(output.errors[0].message,"Error: k is not defined and does not have a value to be calculated"); + }); +}); \ No newline at end of file