diff --git a/.appveyor/workflow.yml b/.appveyor/workflow.yml index 96ec038701..8acdcbb36b 100644 --- a/.appveyor/workflow.yml +++ b/.appveyor/workflow.yml @@ -81,6 +81,7 @@ for: - cinst nodejs.install - node --version - npm --version + - npm install -g yarn@v1.22.10 build_script: - cd runtime\JavaScript\ - npm install @@ -88,6 +89,9 @@ for: - cd ..\.. - mvn -q -DskipTests install --batch-mode test_script: + - cd runtime\JavaScript\ + - yarn test + - cd ..\.. - cd runtime-testsuite - mvn -q -Dtest=javascript.* test -Dantlr-javascript-npm="C:\Program Files\nodejs\npm.cmd" -Dantlr-javascript-nodejs="C:\Program Files\nodejs\node.exe" diff --git a/.circleci/scripts/install-linux-javascript.sh b/.circleci/scripts/install-linux-javascript.sh index 1ecc3a437d..f862300f4a 100755 --- a/.circleci/scripts/install-linux-javascript.sh +++ b/.circleci/scripts/install-linux-javascript.sh @@ -7,7 +7,11 @@ echo "installing nodejs..." curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - sudo apt-get install -y nodejs echo node version: $(node --version) -echo "done installing nodejs..." +echo "done installing nodejs" + +echo "installing yarn..." +sudo npm install -g yarn@v1.22.10 +echo "done installing yarn" echo "packaging javascript runtime..." pushd runtime/JavaScript diff --git a/.circleci/scripts/run-tests-javascript.sh b/.circleci/scripts/run-tests-javascript.sh index b2390d4685..c06e242faf 100755 --- a/.circleci/scripts/run-tests-javascript.sh +++ b/.circleci/scripts/run-tests-javascript.sh @@ -2,7 +2,22 @@ set -euo pipefail +declare -i RESULT=0 + +pushd runtime/JavaScript + + echo "running jest tests..." + yarn test + RESULT+=$? + +popd + pushd runtime-testsuite + echo "running maven tests..." mvn -q -Dtest=javascript.* test -popd \ No newline at end of file + RESULT+=$? + +popd + +exit $RESULT \ No newline at end of file diff --git a/runtime/JavaScript/package.json b/runtime/JavaScript/package.json index 4a5d9b11f0..29e7d307f2 100644 --- a/runtime/JavaScript/package.json +++ b/runtime/JavaScript/package.json @@ -26,7 +26,8 @@ "webpack-cli": "^3.3.12" }, "scripts": { - "build": "webpack" + "build": "webpack", + "test": "jest" }, "engines": { "node": ">=14" diff --git a/runtime/JavaScript/src/antlr4/IntervalSet.js b/runtime/JavaScript/src/antlr4/IntervalSet.js index d1119b3093..be7c8b3b8b 100644 --- a/runtime/JavaScript/src/antlr4/IntervalSet.js +++ b/runtime/JavaScript/src/antlr4/IntervalSet.js @@ -52,58 +52,55 @@ class IntervalSet { this.addInterval(new Interval(l, h + 1)); } - addInterval(v) { + addInterval(toAdd) { if (this.intervals === null) { this.intervals = []; - this.intervals.push(v); + this.intervals.push(toAdd); } else { // find insert pos - for (let k = 0; k < this.intervals.length; k++) { - const i = this.intervals[k]; + for (let pos = 0; pos < this.intervals.length; pos++) { + const existing = this.intervals[pos]; // distinct range -> insert - if (v.stop < i.start) { - this.intervals.splice(k, 0, v); + if (toAdd.stop < existing.start) { + this.intervals.splice(pos, 0, toAdd); return; } // contiguous range -> adjust - else if (v.stop === i.start) { - this.intervals[k].start = v.start; + else if (toAdd.stop === existing.start) { + this.intervals[pos].start = toAdd.start; return; } // overlapping range -> adjust and reduce - else if (v.start <= i.stop) { - this.intervals[k] = new Interval(Math.min(i.start, v.start), Math.max(i.stop, v.stop)); - this.reduce(k); + else if (toAdd.start <= existing.stop) { + this.intervals[pos] = new Interval(Math.min(existing.start, toAdd.start), Math.max(existing.stop, toAdd.stop)); + this.reduce(pos); return; } } // greater than any existing - this.intervals.push(v); + this.intervals.push(toAdd); } } addSet(other) { if (other.intervals !== null) { - for (let k = 0; k < other.intervals.length; k++) { - const i = other.intervals[k]; - this.addInterval(new Interval(i.start, i.stop)); - } + other.intervals.forEach( toAdd => this.addInterval(toAdd), this); } return this; } - reduce(k) { - // only need to reduce if k is not the last - if (k < this.intervals.length - 1) { - const l = this.intervals[k]; - const r = this.intervals[k + 1]; - // if r contained in l - if (l.stop >= r.stop) { - this.intervals = this.intervals.splice(k + 1, 1); - this.reduce(k); - } else if (l.stop >= r.start) { - this.intervals[k] = new Interval(l.start, r.stop); - this.intervals.splice(k + 1, 1); + reduce(pos) { + // only need to reduce if pos is not the last + if (pos < this.intervals.length - 1) { + const current = this.intervals[pos]; + const next = this.intervals[pos + 1]; + // if next contained in current + if (current.stop >= next.stop) { + this.intervals.splice(pos + 1, 1); + this.reduce(pos); + } else if (current.stop >= next.start) { + this.intervals[pos] = new Interval(current.start, next.stop); + this.intervals.splice(pos + 1, 1); } } } @@ -111,9 +108,8 @@ class IntervalSet { complement(start, stop) { const result = new IntervalSet(); result.addInterval(new Interval(start,stop+1)); - for(let i=0; i result.removeRange(toRemove)); return result; } @@ -130,70 +126,70 @@ class IntervalSet { } } - removeRange(v) { - if(v.start===v.stop-1) { - this.removeOne(v.start); - } else if (this.intervals!==null) { - let k = 0; + removeRange(toRemove) { + if(toRemove.start===toRemove.stop-1) { + this.removeOne(toRemove.start); + } else if (this.intervals !== null) { + let pos = 0; for(let n=0; ni.start && v.stopexisting.start && toRemove.stop=i.stop) { - this.intervals.splice(k, 1); - k = k - 1; // need another pass + else if(toRemove.start<=existing.start && toRemove.stop>=existing.stop) { + this.intervals.splice(pos, 1); + pos = pos - 1; // need another pass } // check for lower boundary - else if(v.start"); } else { - names.push("'" + String.fromCharCode(v.start) + "'"); + names.push("'" + String.fromCharCode(existing.start) + "'"); } } else { - names.push("'" + String.fromCharCode(v.start) + "'..'" + String.fromCharCode(v.stop-1) + "'"); + names.push("'" + String.fromCharCode(existing.start) + "'..'" + String.fromCharCode(existing.stop-1) + "'"); } } if (names.length > 1) { @@ -239,15 +235,15 @@ class IntervalSet { toIndexString() { const names = []; for (let i = 0; i < this.intervals.length; i++) { - const v = this.intervals[i]; - if(v.stop===v.start+1) { - if ( v.start===Token.EOF ) { + const existing = this.intervals[i]; + if(existing.stop===existing.start+1) { + if ( existing.start===Token.EOF ) { names.push(""); } else { - names.push(v.start.toString()); + names.push(existing.start.toString()); } } else { - names.push(v.start.toString() + ".." + (v.stop-1).toString()); + names.push(existing.start.toString() + ".." + (existing.stop-1).toString()); } } if (names.length > 1) { @@ -260,8 +256,8 @@ class IntervalSet { toTokenString(literalNames, symbolicNames) { const names = []; for (let i = 0; i < this.intervals.length; i++) { - const v = this.intervals[i]; - for (let j = v.start; j < v.stop; j++) { + const existing = this.intervals[i]; + for (let j = existing.start; j < existing.stop; j++) { names.push(this.elementName(literalNames, symbolicNames, j)); } } @@ -272,20 +268,18 @@ class IntervalSet { } } - elementName(literalNames, symbolicNames, a) { - if (a === Token.EOF) { + elementName(literalNames, symbolicNames, token) { + if (token === Token.EOF) { return ""; - } else if (a === Token.EPSILON) { + } else if (token === Token.EPSILON) { return ""; } else { - return literalNames[a] || symbolicNames[a]; + return literalNames[token] || symbolicNames[token]; } } get length(){ - let len = 0; - this.intervals.map(function(i) {len += i.length;}); - return len; + return this.intervals.map( interval => interval.length ).reduce((acc, val) => acc + val); } } diff --git a/runtime/JavaScript/src/test/TestIntervalSet.test.js b/runtime/JavaScript/src/test/TestIntervalSet.test.js index 8bc6f37e21..8bdc9ec8f1 100644 --- a/runtime/JavaScript/src/test/TestIntervalSet.test.js +++ b/runtime/JavaScript/src/test/TestIntervalSet.test.js @@ -1,7 +1,14 @@ import antlr4 from "../antlr4/index.js"; -it("merges interval sets properly", () => { +it("computes interval set length", () => { + const s1 = new antlr4.IntervalSet(); + s1.addOne(20); + s1.addOne(154); + s1.addRange(169, 171); + expect(s1.length).toEqual(5); +}); +it("merges simple interval sets", () => { const s1 = new antlr4.IntervalSet(); s1.addOne(10); expect(s1.toString()).toEqual("10"); @@ -26,4 +33,26 @@ it("merges interval sets properly", () => { merged.addSet(s3); expect(merged.toString()).toEqual("10..12"); -}); \ No newline at end of file +}); + +it("merges complex interval sets", () => { + const s1 = new antlr4.IntervalSet(); + s1.addOne(20); + s1.addOne(141); + s1.addOne(144); + s1.addOne(154); + s1.addRange(169, 171); + s1.addOne(173); + expect(s1.toString()).toEqual("{20, 141, 144, 154, 169..171, 173}"); + const s2 = new antlr4.IntervalSet(); + s2.addRange(9, 14); + s2.addOne(53); + s2.addRange(55, 63); + s2.addRange(65, 72); + s2.addRange(74, 117); + s2.addRange(119, 152); + s2.addRange(154, 164); + expect(s2.toString()).toEqual("{9..14, 53, 55..63, 65..72, 74..117, 119..152, 154..164}"); + s1.addSet(s2); + expect(s1.toString()).toEqual("{9..14, 20, 53, 55..63, 65..72, 74..117, 119..152, 154..164, 169..171, 173}"); +});