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
|
||||
- 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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
RESULT+=$?
|
||||
|
||||
popd
|
||||
|
||||
exit $RESULT
|
|
@ -26,7 +26,8 @@
|
|||
"webpack-cli": "^3.3.12"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack"
|
||||
"build": "webpack",
|
||||
"test": "jest"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
|
|
|
@ -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<this.intervals.length; i++) {
|
||||
result.removeRange(this.intervals[i]);
|
||||
}
|
||||
if(this.intervals !== null)
|
||||
this.intervals.forEach(toRemove => result.removeRange(toRemove));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -130,70 +126,70 @@ class IntervalSet {
|
|||
}
|
||||
}
|
||||
|
||||
removeRange(v) {
|
||||
if(v.start===v.stop-1) {
|
||||
this.removeOne(v.start);
|
||||
removeRange(toRemove) {
|
||||
if(toRemove.start===toRemove.stop-1) {
|
||||
this.removeOne(toRemove.start);
|
||||
} else if (this.intervals !== null) {
|
||||
let k = 0;
|
||||
let pos = 0;
|
||||
for(let n=0; n<this.intervals.length; n++) {
|
||||
const i = this.intervals[k];
|
||||
const existing = this.intervals[pos];
|
||||
// intervals are ordered
|
||||
if (v.stop<=i.start) {
|
||||
if (toRemove.stop<=existing.start) {
|
||||
return;
|
||||
}
|
||||
// check for including range, split it
|
||||
else if(v.start>i.start && v.stop<i.stop) {
|
||||
this.intervals[k] = new Interval(i.start, v.start);
|
||||
const x = new Interval(v.stop, i.stop);
|
||||
this.intervals.splice(k, 0, x);
|
||||
else if(toRemove.start>existing.start && toRemove.stop<existing.stop) {
|
||||
this.intervals[pos] = new Interval(existing.start, toRemove.start);
|
||||
const x = new Interval(toRemove.stop, existing.stop);
|
||||
this.intervals.splice(pos, 0, x);
|
||||
return;
|
||||
}
|
||||
// check for included range, remove it
|
||||
else if(v.start<=i.start && v.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<i.stop) {
|
||||
this.intervals[k] = new Interval(i.start, v.start);
|
||||
else if(toRemove.start<existing.stop) {
|
||||
this.intervals[pos] = new Interval(existing.start, toRemove.start);
|
||||
}
|
||||
// check for upper boundary
|
||||
else if(v.stop<i.stop) {
|
||||
this.intervals[k] = new Interval(v.stop, i.stop);
|
||||
else if(toRemove.stop<existing.stop) {
|
||||
this.intervals[pos] = new Interval(toRemove.stop, existing.stop);
|
||||
}
|
||||
k += 1;
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeOne(v) {
|
||||
removeOne(value) {
|
||||
if (this.intervals !== null) {
|
||||
for (let k = 0; k < this.intervals.length; k++) {
|
||||
const i = this.intervals[k];
|
||||
// intervals is ordered
|
||||
if (v < i.start) {
|
||||
for (let i = 0; i < this.intervals.length; i++) {
|
||||
const existing = this.intervals[i];
|
||||
// intervals are ordered
|
||||
if (value < existing.start) {
|
||||
return;
|
||||
}
|
||||
// check for single value range
|
||||
else if (v === i.start && v === i.stop - 1) {
|
||||
this.intervals.splice(k, 1);
|
||||
else if (value === existing.start && value === existing.stop - 1) {
|
||||
this.intervals.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
// check for lower boundary
|
||||
else if (v === i.start) {
|
||||
this.intervals[k] = new Interval(i.start + 1, i.stop);
|
||||
else if (value === existing.start) {
|
||||
this.intervals[i] = new Interval(existing.start + 1, existing.stop);
|
||||
return;
|
||||
}
|
||||
// check for upper boundary
|
||||
else if (v === i.stop - 1) {
|
||||
this.intervals[k] = new Interval(i.start, i.stop - 1);
|
||||
else if (value === existing.stop - 1) {
|
||||
this.intervals[i] = new Interval(existing.start, existing.stop - 1);
|
||||
return;
|
||||
}
|
||||
// split existing range
|
||||
else if (v < i.stop - 1) {
|
||||
const x = new Interval(i.start, v);
|
||||
i.start = v + 1;
|
||||
this.intervals.splice(k, 0, x);
|
||||
else if (value < existing.stop - 1) {
|
||||
const replace = new Interval(existing.start, value);
|
||||
existing.start = value + 1;
|
||||
this.intervals.splice(i, 0, replace);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -218,15 +214,15 @@ class IntervalSet {
|
|||
toCharString() {
|
||||
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("<EOF>");
|
||||
} 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("<EOF>");
|
||||
} 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 "<EOF>";
|
||||
} else if (a === Token.EPSILON) {
|
||||
} else if (token === Token.EPSILON) {
|
||||
return "<EPSILON>";
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
@ -27,3 +34,25 @@ it("merges interval sets properly", () => {
|
|||
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