forked from jasder/antlr
Fix javascript intervals (#3083)
* run jest tests in CI * fix issue when merging sets of intervals * fix CI config * improve readability * more tests
This commit is contained in:
parent
1281fb770e
commit
84722e9fcc
|
@ -81,6 +81,7 @@ for:
|
||||||
- cinst nodejs.install
|
- cinst nodejs.install
|
||||||
- node --version
|
- node --version
|
||||||
- npm --version
|
- npm --version
|
||||||
|
- npm install -g yarn@v1.22.10
|
||||||
build_script:
|
build_script:
|
||||||
- cd runtime\JavaScript\
|
- cd runtime\JavaScript\
|
||||||
- npm install
|
- npm install
|
||||||
|
@ -88,6 +89,9 @@ for:
|
||||||
- cd ..\..
|
- cd ..\..
|
||||||
- mvn -q -DskipTests install --batch-mode
|
- mvn -q -DskipTests install --batch-mode
|
||||||
test_script:
|
test_script:
|
||||||
|
- cd runtime\JavaScript\
|
||||||
|
- yarn test
|
||||||
|
- cd ..\..
|
||||||
- cd runtime-testsuite
|
- 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"
|
- mvn -q -Dtest=javascript.* test -Dantlr-javascript-npm="C:\Program Files\nodejs\npm.cmd" -Dantlr-javascript-nodejs="C:\Program Files\nodejs\node.exe"
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,11 @@ echo "installing nodejs..."
|
||||||
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
|
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
|
||||||
sudo apt-get install -y nodejs
|
sudo apt-get install -y nodejs
|
||||||
echo node version: $(node --version)
|
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..."
|
echo "packaging javascript runtime..."
|
||||||
pushd runtime/JavaScript
|
pushd runtime/JavaScript
|
||||||
|
|
|
@ -2,7 +2,22 @@
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
declare -i RESULT=0
|
||||||
|
|
||||||
|
pushd runtime/JavaScript
|
||||||
|
|
||||||
|
echo "running jest tests..."
|
||||||
|
yarn test
|
||||||
|
RESULT+=$?
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
pushd runtime-testsuite
|
pushd runtime-testsuite
|
||||||
|
|
||||||
echo "running maven tests..."
|
echo "running maven tests..."
|
||||||
mvn -q -Dtest=javascript.* test
|
mvn -q -Dtest=javascript.* test
|
||||||
popd
|
RESULT+=$?
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
|
exit $RESULT
|
|
@ -26,7 +26,8 @@
|
||||||
"webpack-cli": "^3.3.12"
|
"webpack-cli": "^3.3.12"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack"
|
"build": "webpack",
|
||||||
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
|
|
|
@ -52,58 +52,55 @@ class IntervalSet {
|
||||||
this.addInterval(new Interval(l, h + 1));
|
this.addInterval(new Interval(l, h + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
addInterval(v) {
|
addInterval(toAdd) {
|
||||||
if (this.intervals === null) {
|
if (this.intervals === null) {
|
||||||
this.intervals = [];
|
this.intervals = [];
|
||||||
this.intervals.push(v);
|
this.intervals.push(toAdd);
|
||||||
} else {
|
} else {
|
||||||
// find insert pos
|
// find insert pos
|
||||||
for (let k = 0; k < this.intervals.length; k++) {
|
for (let pos = 0; pos < this.intervals.length; pos++) {
|
||||||
const i = this.intervals[k];
|
const existing = this.intervals[pos];
|
||||||
// distinct range -> insert
|
// distinct range -> insert
|
||||||
if (v.stop < i.start) {
|
if (toAdd.stop < existing.start) {
|
||||||
this.intervals.splice(k, 0, v);
|
this.intervals.splice(pos, 0, toAdd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// contiguous range -> adjust
|
// contiguous range -> adjust
|
||||||
else if (v.stop === i.start) {
|
else if (toAdd.stop === existing.start) {
|
||||||
this.intervals[k].start = v.start;
|
this.intervals[pos].start = toAdd.start;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// overlapping range -> adjust and reduce
|
// overlapping range -> adjust and reduce
|
||||||
else if (v.start <= i.stop) {
|
else if (toAdd.start <= existing.stop) {
|
||||||
this.intervals[k] = new Interval(Math.min(i.start, v.start), Math.max(i.stop, v.stop));
|
this.intervals[pos] = new Interval(Math.min(existing.start, toAdd.start), Math.max(existing.stop, toAdd.stop));
|
||||||
this.reduce(k);
|
this.reduce(pos);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// greater than any existing
|
// greater than any existing
|
||||||
this.intervals.push(v);
|
this.intervals.push(toAdd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addSet(other) {
|
addSet(other) {
|
||||||
if (other.intervals !== null) {
|
if (other.intervals !== null) {
|
||||||
for (let k = 0; k < other.intervals.length; k++) {
|
other.intervals.forEach( toAdd => this.addInterval(toAdd), this);
|
||||||
const i = other.intervals[k];
|
|
||||||
this.addInterval(new Interval(i.start, i.stop));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
reduce(k) {
|
reduce(pos) {
|
||||||
// only need to reduce if k is not the last
|
// only need to reduce if pos is not the last
|
||||||
if (k < this.intervals.length - 1) {
|
if (pos < this.intervals.length - 1) {
|
||||||
const l = this.intervals[k];
|
const current = this.intervals[pos];
|
||||||
const r = this.intervals[k + 1];
|
const next = this.intervals[pos + 1];
|
||||||
// if r contained in l
|
// if next contained in current
|
||||||
if (l.stop >= r.stop) {
|
if (current.stop >= next.stop) {
|
||||||
this.intervals = this.intervals.splice(k + 1, 1);
|
this.intervals.splice(pos + 1, 1);
|
||||||
this.reduce(k);
|
this.reduce(pos);
|
||||||
} else if (l.stop >= r.start) {
|
} else if (current.stop >= next.start) {
|
||||||
this.intervals[k] = new Interval(l.start, r.stop);
|
this.intervals[pos] = new Interval(current.start, next.stop);
|
||||||
this.intervals.splice(k + 1, 1);
|
this.intervals.splice(pos + 1, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,9 +108,8 @@ class IntervalSet {
|
||||||
complement(start, stop) {
|
complement(start, stop) {
|
||||||
const result = new IntervalSet();
|
const result = new IntervalSet();
|
||||||
result.addInterval(new Interval(start,stop+1));
|
result.addInterval(new Interval(start,stop+1));
|
||||||
for(let i=0; i<this.intervals.length; i++) {
|
if(this.intervals !== null)
|
||||||
result.removeRange(this.intervals[i]);
|
this.intervals.forEach(toRemove => result.removeRange(toRemove));
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,70 +126,70 @@ class IntervalSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRange(v) {
|
removeRange(toRemove) {
|
||||||
if(v.start===v.stop-1) {
|
if(toRemove.start===toRemove.stop-1) {
|
||||||
this.removeOne(v.start);
|
this.removeOne(toRemove.start);
|
||||||
} else if (this.intervals!==null) {
|
} else if (this.intervals !== null) {
|
||||||
let k = 0;
|
let pos = 0;
|
||||||
for(let n=0; n<this.intervals.length; n++) {
|
for(let n=0; n<this.intervals.length; n++) {
|
||||||
const i = this.intervals[k];
|
const existing = this.intervals[pos];
|
||||||
// intervals are ordered
|
// intervals are ordered
|
||||||
if (v.stop<=i.start) {
|
if (toRemove.stop<=existing.start) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check for including range, split it
|
// check for including range, split it
|
||||||
else if(v.start>i.start && v.stop<i.stop) {
|
else if(toRemove.start>existing.start && toRemove.stop<existing.stop) {
|
||||||
this.intervals[k] = new Interval(i.start, v.start);
|
this.intervals[pos] = new Interval(existing.start, toRemove.start);
|
||||||
const x = new Interval(v.stop, i.stop);
|
const x = new Interval(toRemove.stop, existing.stop);
|
||||||
this.intervals.splice(k, 0, x);
|
this.intervals.splice(pos, 0, x);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check for included range, remove it
|
// check for included range, remove it
|
||||||
else if(v.start<=i.start && v.stop>=i.stop) {
|
else if(toRemove.start<=existing.start && toRemove.stop>=existing.stop) {
|
||||||
this.intervals.splice(k, 1);
|
this.intervals.splice(pos, 1);
|
||||||
k = k - 1; // need another pass
|
pos = pos - 1; // need another pass
|
||||||
}
|
}
|
||||||
// check for lower boundary
|
// check for lower boundary
|
||||||
else if(v.start<i.stop) {
|
else if(toRemove.start<existing.stop) {
|
||||||
this.intervals[k] = new Interval(i.start, v.start);
|
this.intervals[pos] = new Interval(existing.start, toRemove.start);
|
||||||
}
|
}
|
||||||
// check for upper boundary
|
// check for upper boundary
|
||||||
else if(v.stop<i.stop) {
|
else if(toRemove.stop<existing.stop) {
|
||||||
this.intervals[k] = new Interval(v.stop, i.stop);
|
this.intervals[pos] = new Interval(toRemove.stop, existing.stop);
|
||||||
}
|
}
|
||||||
k += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeOne(v) {
|
removeOne(value) {
|
||||||
if (this.intervals !== null) {
|
if (this.intervals !== null) {
|
||||||
for (let k = 0; k < this.intervals.length; k++) {
|
for (let i = 0; i < this.intervals.length; i++) {
|
||||||
const i = this.intervals[k];
|
const existing = this.intervals[i];
|
||||||
// intervals is ordered
|
// intervals are ordered
|
||||||
if (v < i.start) {
|
if (value < existing.start) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check for single value range
|
// check for single value range
|
||||||
else if (v === i.start && v === i.stop - 1) {
|
else if (value === existing.start && value === existing.stop - 1) {
|
||||||
this.intervals.splice(k, 1);
|
this.intervals.splice(i, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check for lower boundary
|
// check for lower boundary
|
||||||
else if (v === i.start) {
|
else if (value === existing.start) {
|
||||||
this.intervals[k] = new Interval(i.start + 1, i.stop);
|
this.intervals[i] = new Interval(existing.start + 1, existing.stop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check for upper boundary
|
// check for upper boundary
|
||||||
else if (v === i.stop - 1) {
|
else if (value === existing.stop - 1) {
|
||||||
this.intervals[k] = new Interval(i.start, i.stop - 1);
|
this.intervals[i] = new Interval(existing.start, existing.stop - 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// split existing range
|
// split existing range
|
||||||
else if (v < i.stop - 1) {
|
else if (value < existing.stop - 1) {
|
||||||
const x = new Interval(i.start, v);
|
const replace = new Interval(existing.start, value);
|
||||||
i.start = v + 1;
|
existing.start = value + 1;
|
||||||
this.intervals.splice(k, 0, x);
|
this.intervals.splice(i, 0, replace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,15 +214,15 @@ class IntervalSet {
|
||||||
toCharString() {
|
toCharString() {
|
||||||
const names = [];
|
const names = [];
|
||||||
for (let i = 0; i < this.intervals.length; i++) {
|
for (let i = 0; i < this.intervals.length; i++) {
|
||||||
const v = this.intervals[i];
|
const existing = this.intervals[i];
|
||||||
if(v.stop===v.start+1) {
|
if(existing.stop===existing.start+1) {
|
||||||
if ( v.start===Token.EOF ) {
|
if ( existing.start===Token.EOF ) {
|
||||||
names.push("<EOF>");
|
names.push("<EOF>");
|
||||||
} else {
|
} else {
|
||||||
names.push("'" + String.fromCharCode(v.start) + "'");
|
names.push("'" + String.fromCharCode(existing.start) + "'");
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (names.length > 1) {
|
||||||
|
@ -239,15 +235,15 @@ class IntervalSet {
|
||||||
toIndexString() {
|
toIndexString() {
|
||||||
const names = [];
|
const names = [];
|
||||||
for (let i = 0; i < this.intervals.length; i++) {
|
for (let i = 0; i < this.intervals.length; i++) {
|
||||||
const v = this.intervals[i];
|
const existing = this.intervals[i];
|
||||||
if(v.stop===v.start+1) {
|
if(existing.stop===existing.start+1) {
|
||||||
if ( v.start===Token.EOF ) {
|
if ( existing.start===Token.EOF ) {
|
||||||
names.push("<EOF>");
|
names.push("<EOF>");
|
||||||
} else {
|
} else {
|
||||||
names.push(v.start.toString());
|
names.push(existing.start.toString());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
names.push(v.start.toString() + ".." + (v.stop-1).toString());
|
names.push(existing.start.toString() + ".." + (existing.stop-1).toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (names.length > 1) {
|
if (names.length > 1) {
|
||||||
|
@ -260,8 +256,8 @@ class IntervalSet {
|
||||||
toTokenString(literalNames, symbolicNames) {
|
toTokenString(literalNames, symbolicNames) {
|
||||||
const names = [];
|
const names = [];
|
||||||
for (let i = 0; i < this.intervals.length; i++) {
|
for (let i = 0; i < this.intervals.length; i++) {
|
||||||
const v = this.intervals[i];
|
const existing = this.intervals[i];
|
||||||
for (let j = v.start; j < v.stop; j++) {
|
for (let j = existing.start; j < existing.stop; j++) {
|
||||||
names.push(this.elementName(literalNames, symbolicNames, j));
|
names.push(this.elementName(literalNames, symbolicNames, j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,20 +268,18 @@ class IntervalSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
elementName(literalNames, symbolicNames, a) {
|
elementName(literalNames, symbolicNames, token) {
|
||||||
if (a === Token.EOF) {
|
if (token === Token.EOF) {
|
||||||
return "<EOF>";
|
return "<EOF>";
|
||||||
} else if (a === Token.EPSILON) {
|
} else if (token === Token.EPSILON) {
|
||||||
return "<EPSILON>";
|
return "<EPSILON>";
|
||||||
} else {
|
} else {
|
||||||
return literalNames[a] || symbolicNames[a];
|
return literalNames[token] || symbolicNames[token];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get length(){
|
get length(){
|
||||||
let len = 0;
|
return this.intervals.map( interval => interval.length ).reduce((acc, val) => acc + val);
|
||||||
this.intervals.map(function(i) {len += i.length;});
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
import antlr4 from "../antlr4/index.js";
|
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();
|
const s1 = new antlr4.IntervalSet();
|
||||||
s1.addOne(10);
|
s1.addOne(10);
|
||||||
expect(s1.toString()).toEqual("10");
|
expect(s1.toString()).toEqual("10");
|
||||||
|
@ -26,4 +33,26 @@ it("merges interval sets properly", () => {
|
||||||
merged.addSet(s3);
|
merged.addSet(s3);
|
||||||
expect(merged.toString()).toEqual("10..12");
|
expect(merged.toString()).toEqual("10..12");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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}");
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue