完成世界书、骰子、apiconfig页面处理

This commit is contained in:
2026-04-30 01:35:10 +08:00
parent a3e3711b2b
commit ba9b925c32
4602 changed files with 785225 additions and 23 deletions

1
frontend/node_modules/chevrotain/BREAKING_CHANGES.md generated vendored Normal file
View File

@@ -0,0 +1 @@
See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html

1
frontend/node_modules/chevrotain/diagrams/README.md generated vendored Normal file
View File

@@ -0,0 +1 @@
See [online docs](https://chevrotain.io/docs/guide/generating_syntax_diagrams.html).

85
frontend/node_modules/chevrotain/diagrams/diagrams.css generated vendored Normal file
View File

@@ -0,0 +1,85 @@
svg.railroad-diagram path {
stroke-width: 3;
stroke: black;
fill: rgba(0, 0, 0, 0);
}
svg.railroad-diagram text {
font: bold 14px monospace;
text-anchor: middle;
}
svg.railroad-diagram text.label {
text-anchor: start;
}
svg.railroad-diagram text.comment {
font: italic 12px monospace;
}
svg.railroad-diagram g.non-terminal rect {
fill: hsl(223, 100%, 83%);
}
svg.railroad-diagram rect {
stroke-width: 3;
stroke: black;
fill: hsl(190, 100%, 83%);
}
.diagramHeader {
display: inline-block;
-webkit-touch-callout: default;
-webkit-user-select: text;
-khtml-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
font-weight: bold;
font-family: monospace;
font-size: 18px;
margin-bottom: -8px;
text-align: center;
}
.diagramHeaderDef {
background-color: lightgreen;
}
svg.railroad-diagram text {
-webkit-touch-callout: default;
-webkit-user-select: text;
-khtml-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
svg.railroad-diagram g.non-terminal rect.diagramRectUsage {
color: green;
fill: yellow;
stroke: 5;
}
svg.railroad-diagram g.terminal rect.diagramRectUsage {
color: green;
fill: yellow;
stroke: 5;
}
div {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
svg {
width: 100%;
}
svg.railroad-diagram g.non-terminal text {
cursor: pointer;
}

View File

@@ -0,0 +1,968 @@
/*
Railroad Diagrams
by Tab Atkins Jr. (and others)
http://xanthir.com
http://twitter.com/tabatkins
http://github.com/tabatkins/railroad-diagrams
This document and all associated files in the github project are licensed under CC0: http://creativecommons.org/publicdomain/zero/1.0/
This means you can reuse, remix, or otherwise appropriate this project for your own use WITHOUT RESTRICTION.
(The actual legal meaning can be found at the above link.)
Don't ask me for permission to use any part of this project, JUST USE IT.
I would appreciate attribution, but that is not required by the license.
*/
/*
This file uses a module pattern to avoid leaking names into the global scope.
The only accidental leakage is the name "temp".
The exported names can be found at the bottom of this file;
simply change the names in the array of strings to change what they are called in your application.
As well, several configuration constants are passed into the module function at the bottom of this file.
At runtime, these constants can be found on the Diagram class.
*/
(function (options) {
function subclassOf(baseClass, superClass) {
baseClass.prototype = Object.create(superClass.prototype);
baseClass.prototype.$super = superClass.prototype;
}
function unnull(/* children */) {
return [].slice.call(arguments).reduce(function (sofar, x) {
return sofar !== undefined ? sofar : x;
});
}
function determineGaps(outer, inner) {
var diff = outer - inner;
switch (Diagram.INTERNAL_ALIGNMENT) {
case "left":
return [0, diff];
break;
case "right":
return [diff, 0];
break;
case "center":
default:
return [diff / 2, diff / 2];
break;
}
}
function wrapString(value) {
return typeof value == "string" ? new Terminal(value) : value;
}
function stackAtIllegalPosition(items) {
/* The height of the last line of the Stack is determined by the last child and
therefore any element outside the Stack could overlap with other elements.
If the Stack is the last element no overlap can occur. */
for (var i = 0; i < items.length; i++) {
if (items[i] instanceof Stack && i !== items.length - 1) {
return true;
}
}
return false;
}
function SVG(name, attrs, text) {
attrs = attrs || {};
text = text || "";
var el = document.createElementNS("http://www.w3.org/2000/svg", name);
for (var attr in attrs) {
if (attr === "xlink:href")
el.setAttributeNS("http://www.w3.org/1999/xlink", "href", attrs[attr]);
else el.setAttribute(attr, attrs[attr]);
}
el.textContent = text;
return el;
}
function FakeSVG(tagName, attrs, text) {
if (!(this instanceof FakeSVG)) return new FakeSVG(tagName, attrs, text);
if (text) this.children = text;
else this.children = [];
this.tagName = tagName;
this.attrs = unnull(attrs, {});
return this;
}
FakeSVG.prototype.format = function (x, y, width) {
// Virtual
};
FakeSVG.prototype.addTo = function (parent) {
if (parent instanceof FakeSVG) {
parent.children.push(this);
return this;
} else {
var svg = this.toSVG();
parent.appendChild(svg);
return svg;
}
};
FakeSVG.prototype.escapeString = function (string) {
// Escape markdown and HTML special characters
return string.replace(/[*_\`\[\]<&]/g, function (charString) {
return "&#" + charString.charCodeAt(0) + ";";
});
};
FakeSVG.prototype.toSVG = function () {
var el = SVG(this.tagName, this.attrs);
if (typeof this.children == "string") {
el.textContent = this.children;
} else {
this.children.forEach(function (e) {
el.appendChild(e.toSVG());
});
}
return el;
};
FakeSVG.prototype.toString = function () {
var str = "<" + this.tagName;
var group = this.tagName == "g" || this.tagName == "svg";
for (var attr in this.attrs) {
str +=
" " +
attr +
'="' +
(this.attrs[attr] + "").replace(/&/g, "&amp;").replace(/"/g, "&quot;") +
'"';
}
str += ">";
if (group) str += "\n";
if (typeof this.children == "string") {
str += FakeSVG.prototype.escapeString(this.children);
} else {
this.children.forEach(function (e) {
str += e;
});
}
str += "</" + this.tagName + ">\n";
return str;
};
function Path(x, y) {
if (!(this instanceof Path)) return new Path(x, y);
FakeSVG.call(this, "path");
this.attrs.d = "M" + x + " " + y;
}
subclassOf(Path, FakeSVG);
Path.prototype.m = function (x, y) {
this.attrs.d += "m" + x + " " + y;
return this;
};
Path.prototype.h = function (val) {
this.attrs.d += "h" + val;
return this;
};
Path.prototype.right = Path.prototype.h;
Path.prototype.left = function (val) {
return this.h(-val);
};
Path.prototype.v = function (val) {
this.attrs.d += "v" + val;
return this;
};
Path.prototype.down = Path.prototype.v;
Path.prototype.up = function (val) {
return this.v(-val);
};
Path.prototype.arc = function (sweep) {
var x = Diagram.ARC_RADIUS;
var y = Diagram.ARC_RADIUS;
if (sweep[0] == "e" || sweep[1] == "w") {
x *= -1;
}
if (sweep[0] == "s" || sweep[1] == "n") {
y *= -1;
}
if (sweep == "ne" || sweep == "es" || sweep == "sw" || sweep == "wn") {
var cw = 1;
} else {
var cw = 0;
}
this.attrs.d +=
"a" +
Diagram.ARC_RADIUS +
" " +
Diagram.ARC_RADIUS +
" 0 0 " +
cw +
" " +
x +
" " +
y;
return this;
};
Path.prototype.format = function () {
// All paths in this library start/end horizontally.
// The extra .5 ensures a minor overlap, so there's no seams in bad rasterizers.
this.attrs.d += "h.5";
return this;
};
function Diagram(items) {
if (!(this instanceof Diagram))
return new Diagram([].slice.call(arguments));
FakeSVG.call(this, "svg", { class: Diagram.DIAGRAM_CLASS });
if (stackAtIllegalPosition(items)) {
throw new RangeError(
"Stack() must only occur at the very last position of Diagram().",
);
}
this.items = items.map(wrapString);
this.items.unshift(new Start());
this.items.push(new End());
this.width =
this.items.reduce(function (sofar, el) {
return sofar + el.width + (el.needsSpace ? 20 : 0);
}, 0) + 1;
this.height = this.items.reduce(function (sofar, el) {
return sofar + el.height;
}, 0);
this.up = Math.max.apply(
null,
this.items.map(function (x) {
return x.up;
}),
);
this.down = Math.max.apply(
null,
this.items.map(function (x) {
return x.down;
}),
);
this.formatted = false;
}
subclassOf(Diagram, FakeSVG);
for (var option in options) {
Diagram[option] = options[option];
}
Diagram.prototype.format = function (paddingt, paddingr, paddingb, paddingl) {
paddingt = unnull(paddingt, 20);
paddingr = unnull(paddingr, paddingt, 20);
paddingb = unnull(paddingb, paddingt, 20);
paddingl = unnull(paddingl, paddingr, 20);
var x = paddingl;
var y = paddingt;
y += this.up;
var g = FakeSVG(
"g",
Diagram.STROKE_ODD_PIXEL_LENGTH ? { transform: "translate(.5 .5)" } : {},
);
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
if (item.needsSpace) {
Path(x, y).h(10).addTo(g);
x += 10;
}
item.format(x, y, item.width + item.offsetX).addTo(g);
x += item.width + item.offsetX;
y += item.height;
if (item.needsSpace) {
Path(x, y).h(10).addTo(g);
x += 10;
}
}
this.attrs.width = this.width + paddingl + paddingr;
this.attrs.height = this.up + this.height + this.down + paddingt + paddingb;
this.attrs.viewBox = "0 0 " + this.attrs.width + " " + this.attrs.height;
g.addTo(this);
this.formatted = true;
return this;
};
Diagram.prototype.addTo = function (parent) {
var scriptTag = document.getElementsByTagName("script");
scriptTag = scriptTag[scriptTag.length - 1];
var parentTag = scriptTag.parentNode;
parent = parent || parentTag;
return this.$super.addTo.call(this, parent);
};
Diagram.prototype.toSVG = function () {
if (!this.formatted) {
this.format();
}
return this.$super.toSVG.call(this);
};
Diagram.prototype.toString = function () {
if (!this.formatted) {
this.format();
}
return this.$super.toString.call(this);
};
function ComplexDiagram() {
var diagram = new Diagram([].slice.call(arguments));
var items = diagram.items;
items.shift();
items.pop();
items.unshift(new Start(false));
items.push(new End(false));
diagram.items = items;
return diagram;
}
function Sequence(items) {
if (!(this instanceof Sequence))
return new Sequence([].slice.call(arguments));
FakeSVG.call(this, "g");
if (stackAtIllegalPosition(items)) {
throw new RangeError(
"Stack() must only occur at the very last position of Sequence().",
);
}
this.items = items.map(wrapString);
this.width = this.items.reduce(function (sofar, el) {
return sofar + el.width + (el.needsSpace ? 20 : 0);
}, 0);
this.offsetX = 0;
this.height = this.items.reduce(function (sofar, el) {
return sofar + el.height;
}, 0);
this.up = this.items.reduce(function (sofar, el) {
return Math.max(sofar, el.up);
}, 0);
this.down = this.items.reduce(function (sofar, el) {
return Math.max(sofar, el.down);
}, 0);
}
subclassOf(Sequence, FakeSVG);
Sequence.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width);
Path(x, y).h(gaps[0]).addTo(this);
Path(x + gaps[0] + this.width, y + this.height)
.h(gaps[1])
.addTo(this);
x += gaps[0];
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
if (item.needsSpace) {
Path(x, y).h(10).addTo(this);
x += 10;
}
item.format(x, y, item.width).addTo(this);
x += item.width;
y += item.height;
if (item.needsSpace) {
Path(x, y).h(10).addTo(this);
x += 10;
}
}
return this;
};
function Stack(items) {
if (!(this instanceof Stack)) return new Stack([].slice.call(arguments));
FakeSVG.call(this, "g");
if (stackAtIllegalPosition(items)) {
throw new RangeError(
"Stack() must only occur at the very last position of Stack().",
);
}
if (items.length === 0) {
throw new RangeError("Stack() must have at least one child.");
}
this.items = items.map(wrapString);
this.width = this.items.reduce(function (sofar, el) {
return Math.max(sofar, el.width + (el.needsSpace ? 20 : 0));
}, 0);
if (this.items.length > 1) {
this.width += Diagram.ARC_RADIUS * 2;
}
this.up = this.items[0].up;
this.down = this.items[this.items.length - 1].down;
this.height = 0;
for (var i = 0; i < this.items.length; i++) {
this.height += this.items[i].height;
if (i !== this.items.length - 1) {
this.height +=
Math.max(this.items[i].down, Diagram.VERTICAL_SEPARATION) +
Math.max(this.items[i + 1].up, Diagram.VERTICAL_SEPARATION) +
Diagram.ARC_RADIUS * 4;
}
}
if (this.items.length === 0) {
this.offsetX = 0;
} else {
// the value is usually negative because the linebreak resets the x value for the next element
this.offsetX = -(
this.width -
this.items[this.items.length - 1].width -
this.items[this.items.length - 1].offsetX -
(this.items[this.items.length - 1].needsSpace ? 20 : 0)
);
if (this.items.length > 1) {
this.offsetX += Diagram.ARC_RADIUS * 2;
}
}
}
subclassOf(Stack, FakeSVG);
Stack.prototype.format = function (x, y, width) {
var xIntitial = x;
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
if (item.needsSpace) {
Path(x, y).h(10).addTo(this);
x += 10;
}
item
.format(
x,
y,
Math.max(item.width + item.offsetX, Diagram.ARC_RADIUS * 2),
)
.addTo(this);
x += Math.max(item.width + item.offsetX, Diagram.ARC_RADIUS * 2);
y += item.height;
if (item.needsSpace) {
Path(x, y).h(10).addTo(this);
x += 10;
}
if (i !== this.items.length - 1) {
Path(x, y)
.arc("ne")
.down(Math.max(item.down, Diagram.VERTICAL_SEPARATION))
.arc("es")
.left(x - xIntitial - Diagram.ARC_RADIUS * 2)
.arc("nw")
.down(Math.max(this.items[i + 1].up, Diagram.VERTICAL_SEPARATION))
.arc("ws")
.addTo(this);
y +=
Math.max(item.down, Diagram.VERTICAL_SEPARATION) +
Math.max(this.items[i + 1].up, Diagram.VERTICAL_SEPARATION) +
Diagram.ARC_RADIUS * 4;
x = xIntitial + Diagram.ARC_RADIUS * 2;
}
}
Path(x, y)
.h(width - (this.width + this.offsetX))
.addTo(this);
return this;
};
function Choice(normal, items) {
if (!(this instanceof Choice))
return new Choice(normal, [].slice.call(arguments, 1));
FakeSVG.call(this, "g");
if (typeof normal !== "number" || normal !== Math.floor(normal)) {
throw new TypeError("The first argument of Choice() must be an integer.");
} else if (normal < 0 || normal >= items.length) {
throw new RangeError(
"The first argument of Choice() must be an index for one of the items.",
);
} else {
this.normal = normal;
}
this.items = items.map(wrapString);
this.width =
this.items.reduce(function (sofar, el) {
return Math.max(sofar, el.width);
}, 0) +
Diagram.ARC_RADIUS * 4;
this.offsetX = 0;
this.height = this.items[normal].height;
this.up = this.down = 0;
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
if (i < normal) {
this.up += Math.max(
Diagram.ARC_RADIUS,
item.up + item.height + item.down + Diagram.VERTICAL_SEPARATION,
);
}
if (i == normal) {
this.up += Math.max(Diagram.ARC_RADIUS, item.up);
this.down += Math.max(Diagram.ARC_RADIUS, item.down);
}
if (i > normal) {
this.down += Math.max(
Diagram.ARC_RADIUS,
Diagram.VERTICAL_SEPARATION + item.up + item.down + item.height,
);
}
}
}
subclassOf(Choice, FakeSVG);
Choice.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width);
Path(x, y).h(gaps[0]).addTo(this);
Path(x + gaps[0] + this.width, y + this.height)
.h(gaps[1])
.addTo(this);
x += gaps[0];
var last = this.items.length - 1;
var innerWidth = this.width - Diagram.ARC_RADIUS * 4;
// Do the elements that curve above
for (var i = this.normal - 1; i >= 0; i--) {
var item = this.items[i];
if (i == this.normal - 1) {
var distanceFromY = Math.max(
Diagram.ARC_RADIUS * 2,
this.items[i + 1].up +
Diagram.VERTICAL_SEPARATION +
item.height +
item.down,
);
}
Path(x, y)
.arc("se")
.up(distanceFromY - Diagram.ARC_RADIUS * 2)
.arc("wn")
.addTo(this);
item
.format(x + Diagram.ARC_RADIUS * 2, y - distanceFromY, innerWidth)
.addTo(this);
Path(
x + Diagram.ARC_RADIUS * 2 + innerWidth,
y - distanceFromY + item.height,
)
.arc("ne")
.down(
distanceFromY -
item.height +
this.items[this.normal].height -
Diagram.ARC_RADIUS * 2,
)
.arc("ws")
.addTo(this);
distanceFromY += Math.max(
Diagram.ARC_RADIUS,
item.up +
Diagram.VERTICAL_SEPARATION +
(i == 0 ? 0 : this.items[i - 1].down + this.items[i - 1].height),
);
}
// Do the straight-line path.
Path(x, y)
.right(Diagram.ARC_RADIUS * 2)
.addTo(this);
this.items[this.normal]
.format(x + Diagram.ARC_RADIUS * 2, y, innerWidth)
.addTo(this);
Path(x + Diagram.ARC_RADIUS * 2 + innerWidth, y + this.height)
.right(Diagram.ARC_RADIUS * 2)
.addTo(this);
// Do the elements that curve below
for (var i = this.normal + 1; i <= last; i++) {
var item = this.items[i];
if (i == this.normal + 1) {
var distanceFromY = Math.max(
Diagram.ARC_RADIUS * 2,
this.items[i - 1].height +
this.items[i - 1].down +
Diagram.VERTICAL_SEPARATION +
item.up,
);
}
Path(x, y)
.arc("ne")
.down(distanceFromY - Diagram.ARC_RADIUS * 2)
.arc("ws")
.addTo(this);
item
.format(x + Diagram.ARC_RADIUS * 2, y + distanceFromY, innerWidth)
.addTo(this);
Path(
x + Diagram.ARC_RADIUS * 2 + innerWidth,
y + distanceFromY + item.height,
)
.arc("se")
.up(
distanceFromY -
Diagram.ARC_RADIUS * 2 +
item.height -
this.items[this.normal].height,
)
.arc("wn")
.addTo(this);
distanceFromY += Math.max(
Diagram.ARC_RADIUS,
item.height +
item.down +
Diagram.VERTICAL_SEPARATION +
(i == last ? 0 : this.items[i + 1].up),
);
}
return this;
};
function Optional(item, skip) {
if (skip === undefined) return Choice(1, Skip(), item);
else if (skip === "skip") return Choice(0, Skip(), item);
else throw "Unknown value for Optional()'s 'skip' argument.";
}
function OneOrMore(item, rep) {
if (!(this instanceof OneOrMore)) return new OneOrMore(item, rep);
FakeSVG.call(this, "g");
rep = rep || new Skip();
this.item = wrapString(item);
this.rep = wrapString(rep);
this.width =
Math.max(this.item.width, this.rep.width) + Diagram.ARC_RADIUS * 2;
this.offsetX = 0;
this.height = this.item.height;
this.up = this.item.up;
this.down = Math.max(
Diagram.ARC_RADIUS * 2,
this.item.down +
Diagram.VERTICAL_SEPARATION +
this.rep.up +
this.rep.height +
this.rep.down,
);
}
subclassOf(OneOrMore, FakeSVG);
OneOrMore.prototype.needsSpace = true;
OneOrMore.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width);
Path(x, y).h(gaps[0]).addTo(this);
Path(x + gaps[0] + this.width, y + this.height)
.h(gaps[1])
.addTo(this);
x += gaps[0];
// Draw item
Path(x, y).right(Diagram.ARC_RADIUS).addTo(this);
this.item
.format(x + Diagram.ARC_RADIUS, y, this.width - Diagram.ARC_RADIUS * 2)
.addTo(this);
Path(x + this.width - Diagram.ARC_RADIUS, y + this.height)
.right(Diagram.ARC_RADIUS)
.addTo(this);
// Draw repeat arc
var distanceFromY = Math.max(
Diagram.ARC_RADIUS * 2,
this.item.height +
this.item.down +
Diagram.VERTICAL_SEPARATION +
this.rep.up,
);
Path(x + Diagram.ARC_RADIUS, y)
.arc("nw")
.down(distanceFromY - Diagram.ARC_RADIUS * 2)
.arc("ws")
.addTo(this);
this.rep
.format(
x + Diagram.ARC_RADIUS,
y + distanceFromY,
this.width - Diagram.ARC_RADIUS * 2,
)
.addTo(this);
Path(
x + this.width - Diagram.ARC_RADIUS,
y + distanceFromY + this.rep.height,
)
.arc("se")
.up(
distanceFromY -
Diagram.ARC_RADIUS * 2 +
this.rep.height -
this.item.height,
)
.arc("en")
.addTo(this);
return this;
};
function ZeroOrMore(item, rep, skip) {
return Optional(OneOrMore(item, rep), skip);
}
function Start(simpleType) {
if (!(this instanceof Start)) return new Start();
FakeSVG.call(this, "path");
this.width = 20;
this.height = 0;
this.offsetX = 0;
this.up = 10;
this.down = 10;
this.simpleType = simpleType;
}
subclassOf(Start, FakeSVG);
Start.prototype.format = function (x, y) {
if (this.simpleType === false) {
this.attrs.d = "M " + x + " " + (y - 10) + " v 20 m 0 -10 h 20.5";
} else {
this.attrs.d =
"M " + x + " " + (y - 10) + " v 20 m 10 -20 v 20 m -10 -10 h 20.5";
}
return this;
};
function End(simpleType) {
if (!(this instanceof End)) return new End();
FakeSVG.call(this, "path");
this.width = 20;
this.height = 0;
this.offsetX = 0;
this.up = 10;
this.down = 10;
this.simpleType = simpleType;
}
subclassOf(End, FakeSVG);
End.prototype.format = function (x, y) {
if (this.simpleType === false) {
this.attrs.d = "M " + x + " " + y + " h 20 m 0 -10 v 20";
} else {
this.attrs.d = "M " + x + " " + y + " h 20 m -10 -10 v 20 m 10 -20 v 20";
}
return this;
};
function Terminal(
text,
href,
title,
occurrenceIdx,
topRuleName,
dslRuleName,
tokenName,
) {
if (!(this instanceof Terminal))
return new Terminal(
text,
href,
title,
occurrenceIdx,
topRuleName,
dslRuleName,
tokenName,
);
FakeSVG.call(this, "g", { class: "terminal" });
this.text = text;
this.label = text;
this.href = href;
this.title = title;
this.occurrenceIdx = occurrenceIdx;
this.topRuleName = topRuleName;
this.dslRuleName = dslRuleName;
this.tokenName = tokenName;
this.width =
text.length * 8 +
20; /* Assume that each char is .5em, and that the em is 16px */
this.height = 0;
this.offsetX = 0;
this.up = 11;
this.down = 11;
}
subclassOf(Terminal, FakeSVG);
Terminal.prototype.needsSpace = true;
Terminal.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width);
Path(x, y).h(gaps[0]).addTo(this);
Path(x + gaps[0] + this.width, y)
.h(gaps[1])
.addTo(this);
x += gaps[0];
FakeSVG("rect", {
x: x,
y: y - 11,
width: this.width,
height: this.up + this.down,
rx: 10,
ry: 10,
}).addTo(this);
var text = FakeSVG(
"text",
{
x: x + this.width / 2,
y: y + 4,
occurrenceIdx: this.occurrenceIdx,
topRuleName: this.topRuleName,
dslRuleName: this.dslRuleName,
tokenName: this.tokenName,
label: this.label,
},
this.text,
);
var title = FakeSVG("title", {}, this.title);
if (this.href)
FakeSVG("a", { "xlink:href": this.href }, [text]).addTo(this);
else {
text.addTo(this);
if (this.title !== undefined) {
title.addTo(this);
}
}
return this;
};
function NonTerminal(text, href, occurrenceIdx, topRuleName) {
if (!(this instanceof NonTerminal))
return new NonTerminal(text, href, occurrenceIdx, topRuleName);
FakeSVG.call(this, "g", { class: "non-terminal" });
this.text = text;
this.ruleName = text;
this.href = href;
this.occurrenceIdx = occurrenceIdx;
this.topRuleName = topRuleName;
this.width = text.length * 8 + 20;
this.height = 0;
this.offsetX = 0;
this.up = 11;
this.down = 11;
}
subclassOf(NonTerminal, FakeSVG);
NonTerminal.prototype.needsSpace = true;
NonTerminal.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width);
Path(x, y).h(gaps[0]).addTo(this);
Path(x + gaps[0] + this.width, y)
.h(gaps[1])
.addTo(this);
x += gaps[0];
FakeSVG("rect", {
x: x,
y: y - 11,
width: this.width,
height: this.up + this.down,
}).addTo(this);
var text = FakeSVG(
"text",
{
x: x + this.width / 2,
y: y + 4,
occurrenceIdx: this.occurrenceIdx,
topRuleName: this.topRuleName,
ruleName: this.ruleName,
},
this.text,
);
if (this.href)
FakeSVG("a", { "xlink:href": this.href }, [text]).addTo(this);
else text.addTo(this);
return this;
};
function Comment(text) {
if (!(this instanceof Comment)) return new Comment(text);
FakeSVG.call(this, "g");
this.text = text;
this.width = text.length * 7 + 10;
this.height = 0;
this.offsetX = 0;
this.up = 11;
this.down = 11;
}
subclassOf(Comment, FakeSVG);
Comment.prototype.needsSpace = true;
Comment.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width);
Path(x, y).h(gaps[0]).addTo(this);
Path(x + gaps[0] + this.width, y + this.height)
.h(gaps[1])
.addTo(this);
x += gaps[0];
FakeSVG(
"text",
{
x: x + this.width / 2,
y: y + 5,
class: "comment",
},
this.text,
).addTo(this);
return this;
};
function Skip() {
if (!(this instanceof Skip)) return new Skip();
FakeSVG.call(this, "g");
this.width = 0;
this.height = 0;
this.offsetX = 0;
this.up = 0;
this.down = 0;
}
subclassOf(Skip, FakeSVG);
Skip.prototype.format = function (x, y, width) {
Path(x, y).right(width).addTo(this);
return this;
};
var root;
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
root = {};
define([], function () {
return root;
});
} else if (typeof exports === "object") {
// CommonJS for node
root = exports;
} else {
// Browser globals (root is window.railroad)
this.railroad = {};
root = this.railroad;
}
var temp = [
Diagram,
ComplexDiagram,
Sequence,
Stack,
Choice,
Optional,
OneOrMore,
ZeroOrMore,
Terminal,
NonTerminal,
Comment,
Skip,
];
/*
These are the names that the internal classes are exported as.
If you would like different names, adjust them here.
*/
[
"Diagram",
"ComplexDiagram",
"Sequence",
"Stack",
"Choice",
"Optional",
"OneOrMore",
"ZeroOrMore",
"Terminal",
"NonTerminal",
"Comment",
"Skip",
].forEach(function (e, i) {
root[e] = temp[i];
});
}).call(this, {
VERTICAL_SEPARATION: 8,
ARC_RADIUS: 10,
DIAGRAM_CLASS: "railroad-diagram",
STROKE_ODD_PIXEL_LENGTH: true,
INTERNAL_ALIGNMENT: "center",
});

9620
frontend/node_modules/chevrotain/lib/chevrotain.mjs generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
const NAME = "name";
export function defineNameProp(obj, nameValue) {
Object.defineProperty(obj, NAME, {
enumerable: false,
configurable: true,
writable: false,
value: nameValue,
});
}
//# sourceMappingURL=lang_extensions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/parse/constants.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,MAAM,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC"}

View File

@@ -0,0 +1,56 @@
import { flatten, map, uniq } from "lodash-es";
import { isBranchingProd, isOptionalProd, isSequenceProd, NonTerminal, Terminal, } from "@chevrotain/gast";
export function first(prod) {
/* istanbul ignore else */
if (prod instanceof NonTerminal) {
// this could in theory cause infinite loops if
// (1) prod A refs prod B.
// (2) prod B refs prod A
// (3) AB can match the empty set
// in other words a cycle where everything is optional so the first will keep
// looking ahead for the next optional part and will never exit
// currently there is no safeguard for this unique edge case because
// (1) not sure a grammar in which this can happen is useful for anything (productive)
return first(prod.referencedRule);
}
else if (prod instanceof Terminal) {
return firstForTerminal(prod);
}
else if (isSequenceProd(prod)) {
return firstForSequence(prod);
}
else if (isBranchingProd(prod)) {
return firstForBranching(prod);
}
else {
throw Error("non exhaustive match");
}
}
export function firstForSequence(prod) {
let firstSet = [];
const seq = prod.definition;
let nextSubProdIdx = 0;
let hasInnerProdsRemaining = seq.length > nextSubProdIdx;
let currSubProd;
// so we enter the loop at least once (if the definition is not empty
let isLastInnerProdOptional = true;
// scan a sequence until it's end or until we have found a NONE optional production in it
while (hasInnerProdsRemaining && isLastInnerProdOptional) {
currSubProd = seq[nextSubProdIdx];
isLastInnerProdOptional = isOptionalProd(currSubProd);
firstSet = firstSet.concat(first(currSubProd));
nextSubProdIdx = nextSubProdIdx + 1;
hasInnerProdsRemaining = seq.length > nextSubProdIdx;
}
return uniq(firstSet);
}
export function firstForBranching(prod) {
const allAlternativesFirsts = map(prod.definition, (innerProd) => {
return first(innerProd);
});
return uniq(flatten(allAlternativesFirsts));
}
export function firstForTerminal(terminal) {
return [terminal.terminalType];
}
//# sourceMappingURL=first.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"first.js","sourceRoot":"","sources":["../../../../src/parse/grammar/first.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EACL,eAAe,EACf,cAAc,EACd,cAAc,EACd,WAAW,EACX,QAAQ,GACT,MAAM,kBAAkB,CAAC;AAG1B,MAAM,UAAU,KAAK,CAAC,IAAiB;IACrC,0BAA0B;IAC1B,IAAI,IAAI,YAAY,WAAW,EAAE,CAAC;QAChC,+CAA+C;QAC/C,0BAA0B;QAC1B,yBAAyB;QACzB,iCAAiC;QACjC,6EAA6E;QAC7E,+DAA+D;QAC/D,oEAAoE;QACpE,sFAAsF;QACtF,OAAO,KAAK,CAAe,IAAK,CAAC,cAAc,CAAC,CAAC;IACnD,CAAC;SAAM,IAAI,IAAI,YAAY,QAAQ,EAAE,CAAC;QACpC,OAAO,gBAAgB,CAAW,IAAI,CAAC,CAAC;IAC1C,CAAC;SAAM,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;SAAM,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAEhC;IACC,IAAI,QAAQ,GAAgB,EAAE,CAAC;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;IAC5B,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,sBAAsB,GAAG,GAAG,CAAC,MAAM,GAAG,cAAc,CAAC;IACzD,IAAI,WAAW,CAAC;IAChB,qEAAqE;IACrE,IAAI,uBAAuB,GAAG,IAAI,CAAC;IACnC,yFAAyF;IACzF,OAAO,sBAAsB,IAAI,uBAAuB,EAAE,CAAC;QACzD,WAAW,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC;QAClC,uBAAuB,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;QACtD,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;QAC/C,cAAc,GAAG,cAAc,GAAG,CAAC,CAAC;QACpC,sBAAsB,GAAG,GAAG,CAAC,MAAM,GAAG,cAAc,CAAC;IACvD,CAAC;IAED,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAEjC;IACC,MAAM,qBAAqB,GAAkB,GAAG,CAC9C,IAAI,CAAC,UAAU,EACf,CAAC,SAAS,EAAE,EAAE;QACZ,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC,CACF,CAAC;IACF,OAAO,IAAI,CAAC,OAAO,CAAY,qBAAqB,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AACjC,CAAC"}

View File

@@ -0,0 +1,26 @@
// Lookahead keys are 32Bit integers in the form
// TTTTTTTT-ZZZZZZZZZZZZ-YYYY-XXXXXXXX
// XXXX -> Occurrence Index bitmap.
// YYYY -> DSL Method Type bitmap.
// ZZZZZZZZZZZZZZZ -> Rule short Index bitmap.
// TTTTTTTTT -> alternation alternative index bitmap
export const BITS_FOR_METHOD_TYPE = 4;
export const BITS_FOR_OCCURRENCE_IDX = 8;
export const BITS_FOR_RULE_IDX = 12;
// TODO: validation, this means that there may at most 2^8 --> 256 alternatives for an alternation.
export const BITS_FOR_ALT_IDX = 8;
// short string used as part of mapping keys.
// being short improves the performance when composing KEYS for maps out of these
// The 5 - 8 bits (16 possible values, are reserved for the DSL method indices)
export const OR_IDX = 1 << BITS_FOR_OCCURRENCE_IDX;
export const OPTION_IDX = 2 << BITS_FOR_OCCURRENCE_IDX;
export const MANY_IDX = 3 << BITS_FOR_OCCURRENCE_IDX;
export const AT_LEAST_ONE_IDX = 4 << BITS_FOR_OCCURRENCE_IDX;
export const MANY_SEP_IDX = 5 << BITS_FOR_OCCURRENCE_IDX;
export const AT_LEAST_ONE_SEP_IDX = 6 << BITS_FOR_OCCURRENCE_IDX;
// this actually returns a number, but it is always used as a string (object prop key)
export function getKeyForAutomaticLookahead(ruleIdx, dslMethodIdx, occurrence) {
return occurrence | dslMethodIdx | ruleIdx;
}
const BITS_START_FOR_ALT_IDX = 32 - BITS_FOR_ALT_IDX;
//# sourceMappingURL=keys.js.map

View File

@@ -0,0 +1,471 @@
import { every, flatten, forEach, has, isEmpty, map, reduce } from "lodash-es";
import { possiblePathsFrom } from "./interpreter.js";
import { RestWalker } from "./rest.js";
import { tokenStructuredMatcher, tokenStructuredMatcherNoCategories, } from "../../scan/tokens.js";
import { Alternation, Alternative as AlternativeGAST, GAstVisitor, Option, Repetition, RepetitionMandatory, RepetitionMandatoryWithSeparator, RepetitionWithSeparator, } from "@chevrotain/gast";
export var PROD_TYPE;
(function (PROD_TYPE) {
PROD_TYPE[PROD_TYPE["OPTION"] = 0] = "OPTION";
PROD_TYPE[PROD_TYPE["REPETITION"] = 1] = "REPETITION";
PROD_TYPE[PROD_TYPE["REPETITION_MANDATORY"] = 2] = "REPETITION_MANDATORY";
PROD_TYPE[PROD_TYPE["REPETITION_MANDATORY_WITH_SEPARATOR"] = 3] = "REPETITION_MANDATORY_WITH_SEPARATOR";
PROD_TYPE[PROD_TYPE["REPETITION_WITH_SEPARATOR"] = 4] = "REPETITION_WITH_SEPARATOR";
PROD_TYPE[PROD_TYPE["ALTERNATION"] = 5] = "ALTERNATION";
})(PROD_TYPE || (PROD_TYPE = {}));
export function getProdType(prod) {
/* istanbul ignore else */
if (prod instanceof Option || prod === "Option") {
return PROD_TYPE.OPTION;
}
else if (prod instanceof Repetition || prod === "Repetition") {
return PROD_TYPE.REPETITION;
}
else if (prod instanceof RepetitionMandatory ||
prod === "RepetitionMandatory") {
return PROD_TYPE.REPETITION_MANDATORY;
}
else if (prod instanceof RepetitionMandatoryWithSeparator ||
prod === "RepetitionMandatoryWithSeparator") {
return PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR;
}
else if (prod instanceof RepetitionWithSeparator ||
prod === "RepetitionWithSeparator") {
return PROD_TYPE.REPETITION_WITH_SEPARATOR;
}
else if (prod instanceof Alternation || prod === "Alternation") {
return PROD_TYPE.ALTERNATION;
}
else {
throw Error("non exhaustive match");
}
}
export function getLookaheadPaths(options) {
const { occurrence, rule, prodType, maxLookahead } = options;
const type = getProdType(prodType);
if (type === PROD_TYPE.ALTERNATION) {
return getLookaheadPathsForOr(occurrence, rule, maxLookahead);
}
else {
return getLookaheadPathsForOptionalProd(occurrence, rule, type, maxLookahead);
}
}
export function buildLookaheadFuncForOr(occurrence, ruleGrammar, maxLookahead, hasPredicates, dynamicTokensEnabled, laFuncBuilder) {
const lookAheadPaths = getLookaheadPathsForOr(occurrence, ruleGrammar, maxLookahead);
const tokenMatcher = areTokenCategoriesNotUsed(lookAheadPaths)
? tokenStructuredMatcherNoCategories
: tokenStructuredMatcher;
return laFuncBuilder(lookAheadPaths, hasPredicates, tokenMatcher, dynamicTokensEnabled);
}
/**
* When dealing with an Optional production (OPTION/MANY/2nd iteration of AT_LEAST_ONE/...) we need to compare
* the lookahead "inside" the production and the lookahead immediately "after" it in the same top level rule (context free).
*
* Example: given a production:
* ABC(DE)?DF
*
* The optional '(DE)?' should only be entered if we see 'DE'. a single Token 'D' is not sufficient to distinguish between the two
* alternatives.
*
* @returns A Lookahead function which will return true IFF the parser should parse the Optional production.
*/
export function buildLookaheadFuncForOptionalProd(occurrence, ruleGrammar, k, dynamicTokensEnabled, prodType, lookaheadBuilder) {
const lookAheadPaths = getLookaheadPathsForOptionalProd(occurrence, ruleGrammar, prodType, k);
const tokenMatcher = areTokenCategoriesNotUsed(lookAheadPaths)
? tokenStructuredMatcherNoCategories
: tokenStructuredMatcher;
return lookaheadBuilder(lookAheadPaths[0], tokenMatcher, dynamicTokensEnabled);
}
export function buildAlternativesLookAheadFunc(alts, hasPredicates, tokenMatcher, dynamicTokensEnabled) {
const numOfAlts = alts.length;
const areAllOneTokenLookahead = every(alts, (currAlt) => {
return every(currAlt, (currPath) => {
return currPath.length === 1;
});
});
// This version takes into account the predicates as well.
if (hasPredicates) {
/**
* @returns {number} - The chosen alternative index
*/
return function (orAlts) {
// unfortunately the predicates must be extracted every single time
// as they cannot be cached due to references to parameters(vars) which are no longer valid.
// note that in the common case of no predicates, no cpu time will be wasted on this (see else block)
const predicates = map(orAlts, (currAlt) => currAlt.GATE);
for (let t = 0; t < numOfAlts; t++) {
const currAlt = alts[t];
const currNumOfPaths = currAlt.length;
const currPredicate = predicates[t];
if (currPredicate !== undefined && currPredicate.call(this) === false) {
// if the predicate does not match there is no point in checking the paths
continue;
}
nextPath: for (let j = 0; j < currNumOfPaths; j++) {
const currPath = currAlt[j];
const currPathLength = currPath.length;
for (let i = 0; i < currPathLength; i++) {
const nextToken = this.LA(i + 1);
if (tokenMatcher(nextToken, currPath[i]) === false) {
// mismatch in current path
// try the next pth
continue nextPath;
}
}
// found a full path that matches.
// this will also work for an empty ALT as the loop will be skipped
return t;
}
// none of the paths for the current alternative matched
// try the next alternative
}
// none of the alternatives could be matched
return undefined;
};
}
else if (areAllOneTokenLookahead && !dynamicTokensEnabled) {
// optimized (common) case of all the lookaheads paths requiring only
// a single token lookahead. These Optimizations cannot work if dynamically defined Tokens are used.
const singleTokenAlts = map(alts, (currAlt) => {
return flatten(currAlt);
});
const choiceToAlt = reduce(singleTokenAlts, (result, currAlt, idx) => {
forEach(currAlt, (currTokType) => {
if (!has(result, currTokType.tokenTypeIdx)) {
result[currTokType.tokenTypeIdx] = idx;
}
forEach(currTokType.categoryMatches, (currExtendingType) => {
if (!has(result, currExtendingType)) {
result[currExtendingType] = idx;
}
});
});
return result;
}, {});
/**
* @returns {number} - The chosen alternative index
*/
return function () {
const nextToken = this.LA(1);
return choiceToAlt[nextToken.tokenTypeIdx];
};
}
else {
// optimized lookahead without needing to check the predicates at all.
// this causes code duplication which is intentional to improve performance.
/**
* @returns {number} - The chosen alternative index
*/
return function () {
for (let t = 0; t < numOfAlts; t++) {
const currAlt = alts[t];
const currNumOfPaths = currAlt.length;
nextPath: for (let j = 0; j < currNumOfPaths; j++) {
const currPath = currAlt[j];
const currPathLength = currPath.length;
for (let i = 0; i < currPathLength; i++) {
const nextToken = this.LA(i + 1);
if (tokenMatcher(nextToken, currPath[i]) === false) {
// mismatch in current path
// try the next pth
continue nextPath;
}
}
// found a full path that matches.
// this will also work for an empty ALT as the loop will be skipped
return t;
}
// none of the paths for the current alternative matched
// try the next alternative
}
// none of the alternatives could be matched
return undefined;
};
}
}
export function buildSingleAlternativeLookaheadFunction(alt, tokenMatcher, dynamicTokensEnabled) {
const areAllOneTokenLookahead = every(alt, (currPath) => {
return currPath.length === 1;
});
const numOfPaths = alt.length;
// optimized (common) case of all the lookaheads paths requiring only
// a single token lookahead.
if (areAllOneTokenLookahead && !dynamicTokensEnabled) {
const singleTokensTypes = flatten(alt);
if (singleTokensTypes.length === 1 &&
isEmpty(singleTokensTypes[0].categoryMatches)) {
const expectedTokenType = singleTokensTypes[0];
const expectedTokenUniqueKey = expectedTokenType.tokenTypeIdx;
return function () {
return this.LA(1).tokenTypeIdx === expectedTokenUniqueKey;
};
}
else {
const choiceToAlt = reduce(singleTokensTypes, (result, currTokType, idx) => {
result[currTokType.tokenTypeIdx] = true;
forEach(currTokType.categoryMatches, (currExtendingType) => {
result[currExtendingType] = true;
});
return result;
}, []);
return function () {
const nextToken = this.LA(1);
return choiceToAlt[nextToken.tokenTypeIdx] === true;
};
}
}
else {
return function () {
nextPath: for (let j = 0; j < numOfPaths; j++) {
const currPath = alt[j];
const currPathLength = currPath.length;
for (let i = 0; i < currPathLength; i++) {
const nextToken = this.LA(i + 1);
if (tokenMatcher(nextToken, currPath[i]) === false) {
// mismatch in current path
// try the next pth
continue nextPath;
}
}
// found a full path that matches.
return true;
}
// none of the paths matched
return false;
};
}
}
class RestDefinitionFinderWalker extends RestWalker {
constructor(topProd, targetOccurrence, targetProdType) {
super();
this.topProd = topProd;
this.targetOccurrence = targetOccurrence;
this.targetProdType = targetProdType;
}
startWalking() {
this.walk(this.topProd);
return this.restDef;
}
checkIsTarget(node, expectedProdType, currRest, prevRest) {
if (node.idx === this.targetOccurrence &&
this.targetProdType === expectedProdType) {
this.restDef = currRest.concat(prevRest);
return true;
}
// performance optimization, do not iterate over the entire Grammar ast after we have found the target
return false;
}
walkOption(optionProd, currRest, prevRest) {
if (!this.checkIsTarget(optionProd, PROD_TYPE.OPTION, currRest, prevRest)) {
super.walkOption(optionProd, currRest, prevRest);
}
}
walkAtLeastOne(atLeastOneProd, currRest, prevRest) {
if (!this.checkIsTarget(atLeastOneProd, PROD_TYPE.REPETITION_MANDATORY, currRest, prevRest)) {
super.walkOption(atLeastOneProd, currRest, prevRest);
}
}
walkAtLeastOneSep(atLeastOneSepProd, currRest, prevRest) {
if (!this.checkIsTarget(atLeastOneSepProd, PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR, currRest, prevRest)) {
super.walkOption(atLeastOneSepProd, currRest, prevRest);
}
}
walkMany(manyProd, currRest, prevRest) {
if (!this.checkIsTarget(manyProd, PROD_TYPE.REPETITION, currRest, prevRest)) {
super.walkOption(manyProd, currRest, prevRest);
}
}
walkManySep(manySepProd, currRest, prevRest) {
if (!this.checkIsTarget(manySepProd, PROD_TYPE.REPETITION_WITH_SEPARATOR, currRest, prevRest)) {
super.walkOption(manySepProd, currRest, prevRest);
}
}
}
/**
* Returns the definition of a target production in a top level level rule.
*/
class InsideDefinitionFinderVisitor extends GAstVisitor {
constructor(targetOccurrence, targetProdType, targetRef) {
super();
this.targetOccurrence = targetOccurrence;
this.targetProdType = targetProdType;
this.targetRef = targetRef;
this.result = [];
}
checkIsTarget(node, expectedProdName) {
if (node.idx === this.targetOccurrence &&
this.targetProdType === expectedProdName &&
(this.targetRef === undefined || node === this.targetRef)) {
this.result = node.definition;
}
}
visitOption(node) {
this.checkIsTarget(node, PROD_TYPE.OPTION);
}
visitRepetition(node) {
this.checkIsTarget(node, PROD_TYPE.REPETITION);
}
visitRepetitionMandatory(node) {
this.checkIsTarget(node, PROD_TYPE.REPETITION_MANDATORY);
}
visitRepetitionMandatoryWithSeparator(node) {
this.checkIsTarget(node, PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR);
}
visitRepetitionWithSeparator(node) {
this.checkIsTarget(node, PROD_TYPE.REPETITION_WITH_SEPARATOR);
}
visitAlternation(node) {
this.checkIsTarget(node, PROD_TYPE.ALTERNATION);
}
}
function initializeArrayOfArrays(size) {
const result = new Array(size);
for (let i = 0; i < size; i++) {
result[i] = [];
}
return result;
}
/**
* A sort of hash function between a Path in the grammar and a string.
* Note that this returns multiple "hashes" to support the scenario of token categories.
* - A single path with categories may match multiple **actual** paths.
*/
function pathToHashKeys(path) {
let keys = [""];
for (let i = 0; i < path.length; i++) {
const tokType = path[i];
const longerKeys = [];
for (let j = 0; j < keys.length; j++) {
const currShorterKey = keys[j];
longerKeys.push(currShorterKey + "_" + tokType.tokenTypeIdx);
for (let t = 0; t < tokType.categoryMatches.length; t++) {
const categoriesKeySuffix = "_" + tokType.categoryMatches[t];
longerKeys.push(currShorterKey + categoriesKeySuffix);
}
}
keys = longerKeys;
}
return keys;
}
/**
* Imperative style due to being called from a hot spot
*/
function isUniquePrefixHash(altKnownPathsKeys, searchPathKeys, idx) {
for (let currAltIdx = 0; currAltIdx < altKnownPathsKeys.length; currAltIdx++) {
// We only want to test vs the other alternatives
if (currAltIdx === idx) {
continue;
}
const otherAltKnownPathsKeys = altKnownPathsKeys[currAltIdx];
for (let searchIdx = 0; searchIdx < searchPathKeys.length; searchIdx++) {
const searchKey = searchPathKeys[searchIdx];
if (otherAltKnownPathsKeys[searchKey] === true) {
return false;
}
}
}
// None of the SearchPathKeys were found in any of the other alternatives
return true;
}
export function lookAheadSequenceFromAlternatives(altsDefs, k) {
const partialAlts = map(altsDefs, (currAlt) => possiblePathsFrom([currAlt], 1));
const finalResult = initializeArrayOfArrays(partialAlts.length);
const altsHashes = map(partialAlts, (currAltPaths) => {
const dict = {};
forEach(currAltPaths, (item) => {
const keys = pathToHashKeys(item.partialPath);
forEach(keys, (currKey) => {
dict[currKey] = true;
});
});
return dict;
});
let newData = partialAlts;
// maxLookahead loop
for (let pathLength = 1; pathLength <= k; pathLength++) {
const currDataset = newData;
newData = initializeArrayOfArrays(currDataset.length);
// alternatives loop
for (let altIdx = 0; altIdx < currDataset.length; altIdx++) {
const currAltPathsAndSuffixes = currDataset[altIdx];
// paths in current alternative loop
for (let currPathIdx = 0; currPathIdx < currAltPathsAndSuffixes.length; currPathIdx++) {
const currPathPrefix = currAltPathsAndSuffixes[currPathIdx].partialPath;
const suffixDef = currAltPathsAndSuffixes[currPathIdx].suffixDef;
const prefixKeys = pathToHashKeys(currPathPrefix);
const isUnique = isUniquePrefixHash(altsHashes, prefixKeys, altIdx);
// End of the line for this path.
if (isUnique || isEmpty(suffixDef) || currPathPrefix.length === k) {
const currAltResult = finalResult[altIdx];
// TODO: Can we implement a containsPath using Maps/Dictionaries?
if (containsPath(currAltResult, currPathPrefix) === false) {
currAltResult.push(currPathPrefix);
// Update all new keys for the current path.
for (let j = 0; j < prefixKeys.length; j++) {
const currKey = prefixKeys[j];
altsHashes[altIdx][currKey] = true;
}
}
}
// Expand longer paths
else {
const newPartialPathsAndSuffixes = possiblePathsFrom(suffixDef, pathLength + 1, currPathPrefix);
newData[altIdx] = newData[altIdx].concat(newPartialPathsAndSuffixes);
// Update keys for new known paths
forEach(newPartialPathsAndSuffixes, (item) => {
const prefixKeys = pathToHashKeys(item.partialPath);
forEach(prefixKeys, (key) => {
altsHashes[altIdx][key] = true;
});
});
}
}
}
}
return finalResult;
}
export function getLookaheadPathsForOr(occurrence, ruleGrammar, k, orProd) {
const visitor = new InsideDefinitionFinderVisitor(occurrence, PROD_TYPE.ALTERNATION, orProd);
ruleGrammar.accept(visitor);
return lookAheadSequenceFromAlternatives(visitor.result, k);
}
export function getLookaheadPathsForOptionalProd(occurrence, ruleGrammar, prodType, k) {
const insideDefVisitor = new InsideDefinitionFinderVisitor(occurrence, prodType);
ruleGrammar.accept(insideDefVisitor);
const insideDef = insideDefVisitor.result;
const afterDefWalker = new RestDefinitionFinderWalker(ruleGrammar, occurrence, prodType);
const afterDef = afterDefWalker.startWalking();
const insideFlat = new AlternativeGAST({ definition: insideDef });
const afterFlat = new AlternativeGAST({ definition: afterDef });
return lookAheadSequenceFromAlternatives([insideFlat, afterFlat], k);
}
export function containsPath(alternative, searchPath) {
compareOtherPath: for (let i = 0; i < alternative.length; i++) {
const otherPath = alternative[i];
if (otherPath.length !== searchPath.length) {
continue;
}
for (let j = 0; j < otherPath.length; j++) {
const searchTok = searchPath[j];
const otherTok = otherPath[j];
const matchingTokens = searchTok === otherTok ||
otherTok.categoryMatchesMap[searchTok.tokenTypeIdx] !== undefined;
if (matchingTokens === false) {
continue compareOtherPath;
}
}
return true;
}
return false;
}
export function isStrictPrefixOfPath(prefix, other) {
return (prefix.length < other.length &&
every(prefix, (tokType, idx) => {
const otherTokType = other[idx];
return (tokType === otherTokType ||
otherTokType.categoryMatchesMap[tokType.tokenTypeIdx]);
}));
}
export function areTokenCategoriesNotUsed(lookAheadPaths) {
return every(lookAheadPaths, (singleAltPaths) => every(singleAltPaths, (singlePath) => every(singlePath, (token) => isEmpty(token.categoryMatches))));
}
//# sourceMappingURL=lookahead.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../../src/parse/grammar/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAqB,MAAM,kBAAkB,CAAC;AAMlE,MAAM,UAAU,cAAc,CAC5B,SAA+B,EAC/B,cAAoD;IAEpD,MAAM,WAAW,GAAG,IAAI,sBAAsB,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAC1E,WAAW,CAAC,WAAW,EAAE,CAAC;IAC1B,OAAO,WAAW,CAAC,MAAM,CAAC;AAC5B,CAAC;AAED,MAAM,OAAO,sBAAuB,SAAQ,WAAW;IAIrD,YACU,aAAmC,EACnC,cAAoD;QAE5D,KAAK,EAAE,CAAC;QAHA,kBAAa,GAAb,aAAa,CAAsB;QACnC,mBAAc,GAAd,cAAc,CAAsC;QALvD,WAAM,GAA0C,EAAE,CAAC;IAQ1D,CAAC;IAEM,WAAW;QAChB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE;YAC3C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,gBAAgB,CAAC,IAAiB;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAErD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,sBAAsB,CACpD,IAAI,CAAC,YAAY,EACjB,IAAI,CACL,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACf,OAAO,EAAE,GAAG;gBACZ,IAAI,EAAE,yBAAyB,CAAC,sBAAsB;gBACtD,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI;gBAChC,iBAAiB,EAAE,IAAI,CAAC,eAAe;aACxC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,cAAc,GAAG,GAAG,CAAC;QAC5B,CAAC;IACH,CAAC;CACF"}

View File

@@ -0,0 +1,103 @@
import { drop, forEach } from "lodash-es";
import { Alternation, Alternative, NonTerminal, Option, Repetition, RepetitionMandatory, RepetitionMandatoryWithSeparator, RepetitionWithSeparator, Terminal, } from "@chevrotain/gast";
/**
* A Grammar Walker that computes the "remaining" grammar "after" a productions in the grammar.
*/
export class RestWalker {
walk(prod, prevRest = []) {
forEach(prod.definition, (subProd, index) => {
const currRest = drop(prod.definition, index + 1);
/* istanbul ignore else */
if (subProd instanceof NonTerminal) {
this.walkProdRef(subProd, currRest, prevRest);
}
else if (subProd instanceof Terminal) {
this.walkTerminal(subProd, currRest, prevRest);
}
else if (subProd instanceof Alternative) {
this.walkFlat(subProd, currRest, prevRest);
}
else if (subProd instanceof Option) {
this.walkOption(subProd, currRest, prevRest);
}
else if (subProd instanceof RepetitionMandatory) {
this.walkAtLeastOne(subProd, currRest, prevRest);
}
else if (subProd instanceof RepetitionMandatoryWithSeparator) {
this.walkAtLeastOneSep(subProd, currRest, prevRest);
}
else if (subProd instanceof RepetitionWithSeparator) {
this.walkManySep(subProd, currRest, prevRest);
}
else if (subProd instanceof Repetition) {
this.walkMany(subProd, currRest, prevRest);
}
else if (subProd instanceof Alternation) {
this.walkOr(subProd, currRest, prevRest);
}
else {
throw Error("non exhaustive match");
}
});
}
walkTerminal(terminal, currRest, prevRest) { }
walkProdRef(refProd, currRest, prevRest) { }
walkFlat(flatProd, currRest, prevRest) {
// ABCDEF => after the D the rest is EF
const fullOrRest = currRest.concat(prevRest);
this.walk(flatProd, fullOrRest);
}
walkOption(optionProd, currRest, prevRest) {
// ABC(DE)?F => after the (DE)? the rest is F
const fullOrRest = currRest.concat(prevRest);
this.walk(optionProd, fullOrRest);
}
walkAtLeastOne(atLeastOneProd, currRest, prevRest) {
// ABC(DE)+F => after the (DE)+ the rest is (DE)?F
const fullAtLeastOneRest = [
new Option({ definition: atLeastOneProd.definition }),
].concat(currRest, prevRest);
this.walk(atLeastOneProd, fullAtLeastOneRest);
}
walkAtLeastOneSep(atLeastOneSepProd, currRest, prevRest) {
// ABC DE(,DE)* F => after the (,DE)+ the rest is (,DE)?F
const fullAtLeastOneSepRest = restForRepetitionWithSeparator(atLeastOneSepProd, currRest, prevRest);
this.walk(atLeastOneSepProd, fullAtLeastOneSepRest);
}
walkMany(manyProd, currRest, prevRest) {
// ABC(DE)*F => after the (DE)* the rest is (DE)?F
const fullManyRest = [
new Option({ definition: manyProd.definition }),
].concat(currRest, prevRest);
this.walk(manyProd, fullManyRest);
}
walkManySep(manySepProd, currRest, prevRest) {
// ABC (DE(,DE)*)? F => after the (,DE)* the rest is (,DE)?F
const fullManySepRest = restForRepetitionWithSeparator(manySepProd, currRest, prevRest);
this.walk(manySepProd, fullManySepRest);
}
walkOr(orProd, currRest, prevRest) {
// ABC(D|E|F)G => when finding the (D|E|F) the rest is G
const fullOrRest = currRest.concat(prevRest);
// walk all different alternatives
forEach(orProd.definition, (alt) => {
// wrapping each alternative in a single definition wrapper
// to avoid errors in computing the rest of that alternative in the invocation to computeInProdFollows
// (otherwise for OR([alt1,alt2]) alt2 will be considered in 'rest' of alt1
const prodWrapper = new Alternative({ definition: [alt] });
this.walk(prodWrapper, fullOrRest);
});
}
}
function restForRepetitionWithSeparator(repSepProd, currRest, prevRest) {
const repSepRest = [
new Option({
definition: [
new Terminal({ terminalType: repSepProd.separator }),
].concat(repSepProd.definition),
}),
];
const fullRepSepRest = repSepRest.concat(currRest, prevRest);
return fullRepSepRest;
}
//# sourceMappingURL=rest.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"rest.js","sourceRoot":"","sources":["../../../../src/parse/grammar/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,MAAM,EACN,UAAU,EACV,mBAAmB,EACnB,gCAAgC,EAChC,uBAAuB,EACvB,QAAQ,GACT,MAAM,kBAAkB,CAAC;AAG1B;;GAEG;AACH,MAAM,OAAgB,UAAU;IAC9B,IAAI,CAAC,IAAmC,EAAE,WAAkB,EAAE;QAC5D,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,OAAoB,EAAE,KAAK,EAAE,EAAE;YACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YAClD,0BAA0B;YAC1B,IAAI,OAAO,YAAY,WAAW,EAAE,CAAC;gBACnC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAChD,CAAC;iBAAM,IAAI,OAAO,YAAY,QAAQ,EAAE,CAAC;gBACvC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACjD,CAAC;iBAAM,IAAI,OAAO,YAAY,WAAW,EAAE,CAAC;gBAC1C,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC7C,CAAC;iBAAM,IAAI,OAAO,YAAY,MAAM,EAAE,CAAC;gBACrC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC/C,CAAC;iBAAM,IAAI,OAAO,YAAY,mBAAmB,EAAE,CAAC;gBAClD,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACnD,CAAC;iBAAM,IAAI,OAAO,YAAY,gCAAgC,EAAE,CAAC;gBAC/D,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACtD,CAAC;iBAAM,IAAI,OAAO,YAAY,uBAAuB,EAAE,CAAC;gBACtD,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAChD,CAAC;iBAAM,IAAI,OAAO,YAAY,UAAU,EAAE,CAAC;gBACzC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC7C,CAAC;iBAAM,IAAI,OAAO,YAAY,WAAW,EAAE,CAAC;gBAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACN,MAAM,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACtC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,YAAY,CACV,QAAkB,EAClB,QAAuB,EACvB,QAAuB,IAChB,CAAC;IAEV,WAAW,CACT,OAAoB,EACpB,QAAuB,EACvB,QAAuB,IAChB,CAAC;IAEV,QAAQ,CACN,QAAqB,EACrB,QAAuB,EACvB,QAAuB;QAEvB,uCAAuC;QACvC,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAO,UAAU,CAAC,CAAC;IACvC,CAAC;IAED,UAAU,CACR,UAAkB,EAClB,QAAuB,EACvB,QAAuB;QAEvB,6CAA6C;QAC7C,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAO,UAAU,CAAC,CAAC;IACzC,CAAC;IAED,cAAc,CACZ,cAAmC,EACnC,QAAuB,EACvB,QAAuB;QAEvB,kDAAkD;QAClD,MAAM,kBAAkB,GAAkB;YACxC,IAAI,MAAM,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,UAAU,EAAE,CAAC;SACtD,CAAC,MAAM,CAAM,QAAQ,EAAO,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IAChD,CAAC;IAED,iBAAiB,CACf,iBAAmD,EACnD,QAAuB,EACvB,QAAuB;QAEvB,yDAAyD;QACzD,MAAM,qBAAqB,GAAG,8BAA8B,CAC1D,iBAAiB,EACjB,QAAQ,EACR,QAAQ,CACT,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;IACtD,CAAC;IAED,QAAQ,CACN,QAAoB,EACpB,QAAuB,EACvB,QAAuB;QAEvB,kDAAkD;QAClD,MAAM,YAAY,GAAkB;YAClC,IAAI,MAAM,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC;SAChD,CAAC,MAAM,CAAM,QAAQ,EAAO,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACpC,CAAC;IAED,WAAW,CACT,WAAoC,EACpC,QAAuB,EACvB,QAAuB;QAEvB,4DAA4D;QAC5D,MAAM,eAAe,GAAG,8BAA8B,CACpD,WAAW,EACX,QAAQ,EACR,QAAQ,CACT,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,CACJ,MAAmB,EACnB,QAAuB,EACvB,QAAuB;QAEvB,wDAAwD;QACxD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,kCAAkC;QAClC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE;YACjC,2DAA2D;YAC3D,sGAAsG;YACtG,2EAA2E;YAC3E,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,IAAI,CAAC,WAAW,EAAO,UAAU,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED,SAAS,8BAA8B,CACrC,UAAmC,EACnC,QAAuB,EACvB,QAAuB;IAEvB,MAAM,UAAU,GAAG;QACjB,IAAI,MAAM,CAAC;YACT,UAAU,EAAE;gBACV,IAAI,QAAQ,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,SAAS,EAAE,CAAgB;aACpE,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;SAChC,CAAgB;KAClB,CAAC;IACF,MAAM,cAAc,GAAkB,UAAU,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC5E,OAAO,cAAc,CAAC;AACxB,CAAC"}

View File

@@ -0,0 +1,295 @@
import { forEach, has, isArray, isFunction, last as peek, some, } from "lodash-es";
import { Alternation, Alternative, NonTerminal, Option, Repetition, RepetitionMandatory, RepetitionMandatoryWithSeparator, RepetitionWithSeparator, Rule, Terminal, } from "@chevrotain/gast";
import { Lexer } from "../../../scan/lexer_public.js";
import { augmentTokenTypes, hasShortKeyProperty, } from "../../../scan/tokens.js";
import { createToken, createTokenInstance, } from "../../../scan/tokens_public.js";
import { END_OF_FILE } from "../parser.js";
import { BITS_FOR_OCCURRENCE_IDX } from "../../grammar/keys.js";
const RECORDING_NULL_OBJECT = {
description: "This Object indicates the Parser is during Recording Phase",
};
Object.freeze(RECORDING_NULL_OBJECT);
const HANDLE_SEPARATOR = true;
const MAX_METHOD_IDX = Math.pow(2, BITS_FOR_OCCURRENCE_IDX) - 1;
const RFT = createToken({ name: "RECORDING_PHASE_TOKEN", pattern: Lexer.NA });
augmentTokenTypes([RFT]);
const RECORDING_PHASE_TOKEN = createTokenInstance(RFT, "This IToken indicates the Parser is in Recording Phase\n\t" +
"" +
"See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details",
// Using "-1" instead of NaN (as in EOF) because an actual number is less likely to
// cause errors if the output of LA or CONSUME would be (incorrectly) used during the recording phase.
-1, -1, -1, -1, -1, -1);
Object.freeze(RECORDING_PHASE_TOKEN);
const RECORDING_PHASE_CSTNODE = {
name: "This CSTNode indicates the Parser is in Recording Phase\n\t" +
"See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details",
children: {},
};
/**
* This trait handles the creation of the GAST structure for Chevrotain Grammars
*/
export class GastRecorder {
initGastRecorder(config) {
this.recordingProdStack = [];
this.RECORDING_PHASE = false;
}
enableRecording() {
this.RECORDING_PHASE = true;
this.TRACE_INIT("Enable Recording", () => {
/**
* Warning Dark Voodoo Magic upcoming!
* We are "replacing" the public parsing DSL methods API
* With **new** alternative implementations on the Parser **instance**
*
* So far this is the only way I've found to avoid performance regressions during parsing time.
* - Approx 30% performance regression was measured on Chrome 75 Canary when attempting to replace the "internal"
* implementations directly instead.
*/
for (let i = 0; i < 10; i++) {
const idx = i > 0 ? i : "";
this[`CONSUME${idx}`] = function (arg1, arg2) {
return this.consumeInternalRecord(arg1, i, arg2);
};
this[`SUBRULE${idx}`] = function (arg1, arg2) {
return this.subruleInternalRecord(arg1, i, arg2);
};
this[`OPTION${idx}`] = function (arg1) {
return this.optionInternalRecord(arg1, i);
};
this[`OR${idx}`] = function (arg1) {
return this.orInternalRecord(arg1, i);
};
this[`MANY${idx}`] = function (arg1) {
this.manyInternalRecord(i, arg1);
};
this[`MANY_SEP${idx}`] = function (arg1) {
this.manySepFirstInternalRecord(i, arg1);
};
this[`AT_LEAST_ONE${idx}`] = function (arg1) {
this.atLeastOneInternalRecord(i, arg1);
};
this[`AT_LEAST_ONE_SEP${idx}`] = function (arg1) {
this.atLeastOneSepFirstInternalRecord(i, arg1);
};
}
// DSL methods with the idx(suffix) as an argument
this[`consume`] = function (idx, arg1, arg2) {
return this.consumeInternalRecord(arg1, idx, arg2);
};
this[`subrule`] = function (idx, arg1, arg2) {
return this.subruleInternalRecord(arg1, idx, arg2);
};
this[`option`] = function (idx, arg1) {
return this.optionInternalRecord(arg1, idx);
};
this[`or`] = function (idx, arg1) {
return this.orInternalRecord(arg1, idx);
};
this[`many`] = function (idx, arg1) {
this.manyInternalRecord(idx, arg1);
};
this[`atLeastOne`] = function (idx, arg1) {
this.atLeastOneInternalRecord(idx, arg1);
};
this.ACTION = this.ACTION_RECORD;
this.BACKTRACK = this.BACKTRACK_RECORD;
this.LA = this.LA_RECORD;
});
}
disableRecording() {
this.RECORDING_PHASE = false;
// By deleting these **instance** properties, any future invocation
// will be deferred to the original methods on the **prototype** object
// This seems to get rid of any incorrect optimizations that V8 may
// do during the recording phase.
this.TRACE_INIT("Deleting Recording methods", () => {
const that = this;
for (let i = 0; i < 10; i++) {
const idx = i > 0 ? i : "";
delete that[`CONSUME${idx}`];
delete that[`SUBRULE${idx}`];
delete that[`OPTION${idx}`];
delete that[`OR${idx}`];
delete that[`MANY${idx}`];
delete that[`MANY_SEP${idx}`];
delete that[`AT_LEAST_ONE${idx}`];
delete that[`AT_LEAST_ONE_SEP${idx}`];
}
delete that[`consume`];
delete that[`subrule`];
delete that[`option`];
delete that[`or`];
delete that[`many`];
delete that[`atLeastOne`];
delete that.ACTION;
delete that.BACKTRACK;
delete that.LA;
});
}
// Parser methods are called inside an ACTION?
// Maybe try/catch/finally on ACTIONS while disabling the recorders state changes?
// @ts-expect-error -- noop place holder
ACTION_RECORD(impl) {
// NO-OP during recording
}
// Executing backtracking logic will break our recording logic assumptions
BACKTRACK_RECORD(grammarRule, args) {
return () => true;
}
// LA is part of the official API and may be used for custom lookahead logic
// by end users who may forget to wrap it in ACTION or inside a GATE
LA_RECORD(howMuch) {
// We cannot use the RECORD_PHASE_TOKEN here because someone may depend
// On LA return EOF at the end of the input so an infinite loop may occur.
return END_OF_FILE;
}
topLevelRuleRecord(name, def) {
try {
const newTopLevelRule = new Rule({ definition: [], name: name });
newTopLevelRule.name = name;
this.recordingProdStack.push(newTopLevelRule);
def.call(this);
this.recordingProdStack.pop();
return newTopLevelRule;
}
catch (originalError) {
if (originalError.KNOWN_RECORDER_ERROR !== true) {
try {
originalError.message =
originalError.message +
'\n\t This error was thrown during the "grammar recording phase" For more info see:\n\t' +
"https://chevrotain.io/docs/guide/internals.html#grammar-recording";
}
catch (mutabilityError) {
// We may not be able to modify the original error object
throw originalError;
}
}
throw originalError;
}
}
// Implementation of parsing DSL
optionInternalRecord(actionORMethodDef, occurrence) {
return recordProd.call(this, Option, actionORMethodDef, occurrence);
}
atLeastOneInternalRecord(occurrence, actionORMethodDef) {
recordProd.call(this, RepetitionMandatory, actionORMethodDef, occurrence);
}
atLeastOneSepFirstInternalRecord(occurrence, options) {
recordProd.call(this, RepetitionMandatoryWithSeparator, options, occurrence, HANDLE_SEPARATOR);
}
manyInternalRecord(occurrence, actionORMethodDef) {
recordProd.call(this, Repetition, actionORMethodDef, occurrence);
}
manySepFirstInternalRecord(occurrence, options) {
recordProd.call(this, RepetitionWithSeparator, options, occurrence, HANDLE_SEPARATOR);
}
orInternalRecord(altsOrOpts, occurrence) {
return recordOrProd.call(this, altsOrOpts, occurrence);
}
subruleInternalRecord(ruleToCall, occurrence, options) {
assertMethodIdxIsValid(occurrence);
if (!ruleToCall || has(ruleToCall, "ruleName") === false) {
const error = new Error(`<SUBRULE${getIdxSuffix(occurrence)}> argument is invalid` +
` expecting a Parser method reference but got: <${JSON.stringify(ruleToCall)}>` +
`\n inside top level rule: <${this.recordingProdStack[0].name}>`);
error.KNOWN_RECORDER_ERROR = true;
throw error;
}
const prevProd = peek(this.recordingProdStack);
const ruleName = ruleToCall.ruleName;
const newNoneTerminal = new NonTerminal({
idx: occurrence,
nonTerminalName: ruleName,
label: options === null || options === void 0 ? void 0 : options.LABEL,
// The resolving of the `referencedRule` property will be done once all the Rule's GASTs have been created
referencedRule: undefined,
});
prevProd.definition.push(newNoneTerminal);
return this.outputCst
? RECORDING_PHASE_CSTNODE
: RECORDING_NULL_OBJECT;
}
consumeInternalRecord(tokType, occurrence, options) {
assertMethodIdxIsValid(occurrence);
if (!hasShortKeyProperty(tokType)) {
const error = new Error(`<CONSUME${getIdxSuffix(occurrence)}> argument is invalid` +
` expecting a TokenType reference but got: <${JSON.stringify(tokType)}>` +
`\n inside top level rule: <${this.recordingProdStack[0].name}>`);
error.KNOWN_RECORDER_ERROR = true;
throw error;
}
const prevProd = peek(this.recordingProdStack);
const newNoneTerminal = new Terminal({
idx: occurrence,
terminalType: tokType,
label: options === null || options === void 0 ? void 0 : options.LABEL,
});
prevProd.definition.push(newNoneTerminal);
return RECORDING_PHASE_TOKEN;
}
}
function recordProd(prodConstructor, mainProdArg, occurrence, handleSep = false) {
assertMethodIdxIsValid(occurrence);
const prevProd = peek(this.recordingProdStack);
const grammarAction = isFunction(mainProdArg) ? mainProdArg : mainProdArg.DEF;
const newProd = new prodConstructor({ definition: [], idx: occurrence });
if (handleSep) {
newProd.separator = mainProdArg.SEP;
}
if (has(mainProdArg, "MAX_LOOKAHEAD")) {
newProd.maxLookahead = mainProdArg.MAX_LOOKAHEAD;
}
this.recordingProdStack.push(newProd);
grammarAction.call(this);
prevProd.definition.push(newProd);
this.recordingProdStack.pop();
return RECORDING_NULL_OBJECT;
}
function recordOrProd(mainProdArg, occurrence) {
assertMethodIdxIsValid(occurrence);
const prevProd = peek(this.recordingProdStack);
// Only an array of alternatives
const hasOptions = isArray(mainProdArg) === false;
const alts = hasOptions === false ? mainProdArg : mainProdArg.DEF;
const newOrProd = new Alternation({
definition: [],
idx: occurrence,
ignoreAmbiguities: hasOptions && mainProdArg.IGNORE_AMBIGUITIES === true,
});
if (has(mainProdArg, "MAX_LOOKAHEAD")) {
newOrProd.maxLookahead = mainProdArg.MAX_LOOKAHEAD;
}
const hasPredicates = some(alts, (currAlt) => isFunction(currAlt.GATE));
newOrProd.hasPredicates = hasPredicates;
prevProd.definition.push(newOrProd);
forEach(alts, (currAlt) => {
const currAltFlat = new Alternative({ definition: [] });
newOrProd.definition.push(currAltFlat);
if (has(currAlt, "IGNORE_AMBIGUITIES")) {
currAltFlat.ignoreAmbiguities = currAlt.IGNORE_AMBIGUITIES; // assumes end user provides the correct config value/type
}
// **implicit** ignoreAmbiguities due to usage of gate
else if (has(currAlt, "GATE")) {
currAltFlat.ignoreAmbiguities = true;
}
this.recordingProdStack.push(currAltFlat);
currAlt.ALT.call(this);
this.recordingProdStack.pop();
});
return RECORDING_NULL_OBJECT;
}
function getIdxSuffix(idx) {
return idx === 0 ? "" : `${idx}`;
}
function assertMethodIdxIsValid(idx) {
if (idx < 0 || idx > MAX_METHOD_IDX) {
const error = new Error(
// The stack trace will contain all the needed details
`Invalid DSL Method idx value: <${idx}>\n\t` +
`Idx value must be a none negative value smaller than ${MAX_METHOD_IDX + 1}`);
error.KNOWN_RECORDER_ERROR = true;
throw error;
}
}
//# sourceMappingURL=gast_recorder.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
import { END_OF_FILE } from "../parser.js";
/**
* Trait responsible abstracting over the interaction with Lexer output (Token vector).
*
* This could be generalized to support other kinds of lexers, e.g.
* - Just in Time Lexing / Lexer-Less parsing.
* - Streaming Lexer.
*/
export class LexerAdapter {
initLexerAdapter() {
this.tokVector = [];
this.tokVectorLength = 0;
this.currIdx = -1;
}
set input(newInput) {
// @ts-ignore - `this parameter` not supported in setters/getters
// - https://www.typescriptlang.org/docs/handbook/functions.html#this-parameters
if (this.selfAnalysisDone !== true) {
throw Error(`Missing <performSelfAnalysis> invocation at the end of the Parser's constructor.`);
}
// @ts-ignore - `this parameter` not supported in setters/getters
// - https://www.typescriptlang.org/docs/handbook/functions.html#this-parameters
this.reset();
this.tokVector = newInput;
this.tokVectorLength = newInput.length;
}
get input() {
return this.tokVector;
}
// skips a token and returns the next token
SKIP_TOKEN() {
if (this.currIdx <= this.tokVector.length - 2) {
this.consumeToken();
return this.LA(1);
}
else {
return END_OF_FILE;
}
}
// Lexer (accessing Token vector) related methods which can be overridden to implement lazy lexers
// or lexers dependent on parser context.
LA(howMuch) {
const soughtIdx = this.currIdx + howMuch;
if (soughtIdx < 0 || this.tokVectorLength <= soughtIdx) {
return END_OF_FILE;
}
else {
return this.tokVector[soughtIdx];
}
}
consumeToken() {
this.currIdx++;
}
exportLexerState() {
return this.currIdx;
}
importLexerState(newState) {
this.currIdx = newState;
}
resetLexerState() {
this.currIdx = -1;
}
moveToTerminatedState() {
this.currIdx = this.tokVector.length - 1;
}
getLexerPosition() {
return this.exportLexerState();
}
}
//# sourceMappingURL=lexer_adapter.js.map

View File

@@ -0,0 +1,4 @@
import { CstParser as CstParserConstructorImpel, EmbeddedActionsParser as EmbeddedActionsParserConstructorImpl, } from "../parser.js";
export const CstParser = (CstParserConstructorImpel);
export const EmbeddedActionsParser = EmbeddedActionsParserConstructorImpl;
//# sourceMappingURL=parser_traits.js.map

View File

@@ -0,0 +1,334 @@
import { includes, values } from "lodash-es";
import { isRecognitionException } from "../../exceptions_public.js";
import { DEFAULT_RULE_CONFIG, ParserDefinitionErrorType } from "../parser.js";
import { defaultGrammarValidatorErrorProvider } from "../../errors_public.js";
import { validateRuleIsOverridden } from "../../grammar/checks.js";
import { serializeGrammar } from "@chevrotain/gast";
/**
* This trait is responsible for implementing the public API
* for defining Chevrotain parsers, i.e:
* - CONSUME
* - RULE
* - OPTION
* - ...
*/
export class RecognizerApi {
ACTION(impl) {
return impl.call(this);
}
consume(idx, tokType, options) {
return this.consumeInternal(tokType, idx, options);
}
subrule(idx, ruleToCall, options) {
return this.subruleInternal(ruleToCall, idx, options);
}
option(idx, actionORMethodDef) {
return this.optionInternal(actionORMethodDef, idx);
}
or(idx, altsOrOpts) {
return this.orInternal(altsOrOpts, idx);
}
many(idx, actionORMethodDef) {
return this.manyInternal(idx, actionORMethodDef);
}
atLeastOne(idx, actionORMethodDef) {
return this.atLeastOneInternal(idx, actionORMethodDef);
}
CONSUME(tokType, options) {
return this.consumeInternal(tokType, 0, options);
}
CONSUME1(tokType, options) {
return this.consumeInternal(tokType, 1, options);
}
CONSUME2(tokType, options) {
return this.consumeInternal(tokType, 2, options);
}
CONSUME3(tokType, options) {
return this.consumeInternal(tokType, 3, options);
}
CONSUME4(tokType, options) {
return this.consumeInternal(tokType, 4, options);
}
CONSUME5(tokType, options) {
return this.consumeInternal(tokType, 5, options);
}
CONSUME6(tokType, options) {
return this.consumeInternal(tokType, 6, options);
}
CONSUME7(tokType, options) {
return this.consumeInternal(tokType, 7, options);
}
CONSUME8(tokType, options) {
return this.consumeInternal(tokType, 8, options);
}
CONSUME9(tokType, options) {
return this.consumeInternal(tokType, 9, options);
}
SUBRULE(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 0, options);
}
SUBRULE1(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 1, options);
}
SUBRULE2(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 2, options);
}
SUBRULE3(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 3, options);
}
SUBRULE4(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 4, options);
}
SUBRULE5(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 5, options);
}
SUBRULE6(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 6, options);
}
SUBRULE7(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 7, options);
}
SUBRULE8(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 8, options);
}
SUBRULE9(ruleToCall, options) {
return this.subruleInternal(ruleToCall, 9, options);
}
OPTION(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 0);
}
OPTION1(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 1);
}
OPTION2(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 2);
}
OPTION3(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 3);
}
OPTION4(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 4);
}
OPTION5(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 5);
}
OPTION6(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 6);
}
OPTION7(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 7);
}
OPTION8(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 8);
}
OPTION9(actionORMethodDef) {
return this.optionInternal(actionORMethodDef, 9);
}
OR(altsOrOpts) {
return this.orInternal(altsOrOpts, 0);
}
OR1(altsOrOpts) {
return this.orInternal(altsOrOpts, 1);
}
OR2(altsOrOpts) {
return this.orInternal(altsOrOpts, 2);
}
OR3(altsOrOpts) {
return this.orInternal(altsOrOpts, 3);
}
OR4(altsOrOpts) {
return this.orInternal(altsOrOpts, 4);
}
OR5(altsOrOpts) {
return this.orInternal(altsOrOpts, 5);
}
OR6(altsOrOpts) {
return this.orInternal(altsOrOpts, 6);
}
OR7(altsOrOpts) {
return this.orInternal(altsOrOpts, 7);
}
OR8(altsOrOpts) {
return this.orInternal(altsOrOpts, 8);
}
OR9(altsOrOpts) {
return this.orInternal(altsOrOpts, 9);
}
MANY(actionORMethodDef) {
this.manyInternal(0, actionORMethodDef);
}
MANY1(actionORMethodDef) {
this.manyInternal(1, actionORMethodDef);
}
MANY2(actionORMethodDef) {
this.manyInternal(2, actionORMethodDef);
}
MANY3(actionORMethodDef) {
this.manyInternal(3, actionORMethodDef);
}
MANY4(actionORMethodDef) {
this.manyInternal(4, actionORMethodDef);
}
MANY5(actionORMethodDef) {
this.manyInternal(5, actionORMethodDef);
}
MANY6(actionORMethodDef) {
this.manyInternal(6, actionORMethodDef);
}
MANY7(actionORMethodDef) {
this.manyInternal(7, actionORMethodDef);
}
MANY8(actionORMethodDef) {
this.manyInternal(8, actionORMethodDef);
}
MANY9(actionORMethodDef) {
this.manyInternal(9, actionORMethodDef);
}
MANY_SEP(options) {
this.manySepFirstInternal(0, options);
}
MANY_SEP1(options) {
this.manySepFirstInternal(1, options);
}
MANY_SEP2(options) {
this.manySepFirstInternal(2, options);
}
MANY_SEP3(options) {
this.manySepFirstInternal(3, options);
}
MANY_SEP4(options) {
this.manySepFirstInternal(4, options);
}
MANY_SEP5(options) {
this.manySepFirstInternal(5, options);
}
MANY_SEP6(options) {
this.manySepFirstInternal(6, options);
}
MANY_SEP7(options) {
this.manySepFirstInternal(7, options);
}
MANY_SEP8(options) {
this.manySepFirstInternal(8, options);
}
MANY_SEP9(options) {
this.manySepFirstInternal(9, options);
}
AT_LEAST_ONE(actionORMethodDef) {
this.atLeastOneInternal(0, actionORMethodDef);
}
AT_LEAST_ONE1(actionORMethodDef) {
return this.atLeastOneInternal(1, actionORMethodDef);
}
AT_LEAST_ONE2(actionORMethodDef) {
this.atLeastOneInternal(2, actionORMethodDef);
}
AT_LEAST_ONE3(actionORMethodDef) {
this.atLeastOneInternal(3, actionORMethodDef);
}
AT_LEAST_ONE4(actionORMethodDef) {
this.atLeastOneInternal(4, actionORMethodDef);
}
AT_LEAST_ONE5(actionORMethodDef) {
this.atLeastOneInternal(5, actionORMethodDef);
}
AT_LEAST_ONE6(actionORMethodDef) {
this.atLeastOneInternal(6, actionORMethodDef);
}
AT_LEAST_ONE7(actionORMethodDef) {
this.atLeastOneInternal(7, actionORMethodDef);
}
AT_LEAST_ONE8(actionORMethodDef) {
this.atLeastOneInternal(8, actionORMethodDef);
}
AT_LEAST_ONE9(actionORMethodDef) {
this.atLeastOneInternal(9, actionORMethodDef);
}
AT_LEAST_ONE_SEP(options) {
this.atLeastOneSepFirstInternal(0, options);
}
AT_LEAST_ONE_SEP1(options) {
this.atLeastOneSepFirstInternal(1, options);
}
AT_LEAST_ONE_SEP2(options) {
this.atLeastOneSepFirstInternal(2, options);
}
AT_LEAST_ONE_SEP3(options) {
this.atLeastOneSepFirstInternal(3, options);
}
AT_LEAST_ONE_SEP4(options) {
this.atLeastOneSepFirstInternal(4, options);
}
AT_LEAST_ONE_SEP5(options) {
this.atLeastOneSepFirstInternal(5, options);
}
AT_LEAST_ONE_SEP6(options) {
this.atLeastOneSepFirstInternal(6, options);
}
AT_LEAST_ONE_SEP7(options) {
this.atLeastOneSepFirstInternal(7, options);
}
AT_LEAST_ONE_SEP8(options) {
this.atLeastOneSepFirstInternal(8, options);
}
AT_LEAST_ONE_SEP9(options) {
this.atLeastOneSepFirstInternal(9, options);
}
RULE(name, implementation, config = DEFAULT_RULE_CONFIG) {
if (includes(this.definedRulesNames, name)) {
const errMsg = defaultGrammarValidatorErrorProvider.buildDuplicateRuleNameError({
topLevelRule: name,
grammarName: this.className,
});
const error = {
message: errMsg,
type: ParserDefinitionErrorType.DUPLICATE_RULE_NAME,
ruleName: name,
};
this.definitionErrors.push(error);
}
this.definedRulesNames.push(name);
const ruleImplementation = this.defineRule(name, implementation, config);
this[name] = ruleImplementation;
return ruleImplementation;
}
OVERRIDE_RULE(name, impl, config = DEFAULT_RULE_CONFIG) {
const ruleErrors = validateRuleIsOverridden(name, this.definedRulesNames, this.className);
this.definitionErrors = this.definitionErrors.concat(ruleErrors);
const ruleImplementation = this.defineRule(name, impl, config);
this[name] = ruleImplementation;
return ruleImplementation;
}
BACKTRACK(grammarRule, args) {
return function () {
// save org state
this.isBackTrackingStack.push(1);
const orgState = this.saveRecogState();
try {
grammarRule.apply(this, args);
// if no exception was thrown we have succeed parsing the rule.
return true;
}
catch (e) {
if (isRecognitionException(e)) {
return false;
}
else {
throw e;
}
}
finally {
this.reloadRecogState(orgState);
this.isBackTrackingStack.pop();
}
};
}
// GAST export APIs
getGAstProductions() {
return this.gastProductionsCache;
}
getSerializedGastProductions() {
return serializeGrammar(values(this.gastProductionsCache));
}
}
//# sourceMappingURL=recognizer_api.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,191 @@
import { addNoneTerminalToCst, addTerminalToCst, setNodeLocationFull, setNodeLocationOnlyOffset, } from "../../cst/cst.js";
import { has, isUndefined, keys, noop } from "lodash-es";
import { createBaseSemanticVisitorConstructor, createBaseVisitorConstructorWithDefaults, } from "../../cst/cst_visitor.js";
import { DEFAULT_PARSER_CONFIG } from "../parser.js";
/**
* This trait is responsible for the CST building logic.
*/
export class TreeBuilder {
initTreeBuilder(config) {
this.CST_STACK = [];
// outputCst is no longer exposed/defined in the pubic API
this.outputCst = config.outputCst;
this.nodeLocationTracking = has(config, "nodeLocationTracking")
? config.nodeLocationTracking // assumes end user provides the correct config value/type
: DEFAULT_PARSER_CONFIG.nodeLocationTracking;
if (!this.outputCst) {
this.cstInvocationStateUpdate = noop;
this.cstFinallyStateUpdate = noop;
this.cstPostTerminal = noop;
this.cstPostNonTerminal = noop;
this.cstPostRule = noop;
}
else {
if (/full/i.test(this.nodeLocationTracking)) {
if (this.recoveryEnabled) {
this.setNodeLocationFromToken = setNodeLocationFull;
this.setNodeLocationFromNode = setNodeLocationFull;
this.cstPostRule = noop;
this.setInitialNodeLocation = this.setInitialNodeLocationFullRecovery;
}
else {
this.setNodeLocationFromToken = noop;
this.setNodeLocationFromNode = noop;
this.cstPostRule = this.cstPostRuleFull;
this.setInitialNodeLocation = this.setInitialNodeLocationFullRegular;
}
}
else if (/onlyOffset/i.test(this.nodeLocationTracking)) {
if (this.recoveryEnabled) {
this.setNodeLocationFromToken = setNodeLocationOnlyOffset;
this.setNodeLocationFromNode = setNodeLocationOnlyOffset;
this.cstPostRule = noop;
this.setInitialNodeLocation =
this.setInitialNodeLocationOnlyOffsetRecovery;
}
else {
this.setNodeLocationFromToken = noop;
this.setNodeLocationFromNode = noop;
this.cstPostRule = this.cstPostRuleOnlyOffset;
this.setInitialNodeLocation =
this.setInitialNodeLocationOnlyOffsetRegular;
}
}
else if (/none/i.test(this.nodeLocationTracking)) {
this.setNodeLocationFromToken = noop;
this.setNodeLocationFromNode = noop;
this.cstPostRule = noop;
this.setInitialNodeLocation = noop;
}
else {
throw Error(`Invalid <nodeLocationTracking> config option: "${config.nodeLocationTracking}"`);
}
}
}
setInitialNodeLocationOnlyOffsetRecovery(cstNode) {
cstNode.location = {
startOffset: NaN,
endOffset: NaN,
};
}
setInitialNodeLocationOnlyOffsetRegular(cstNode) {
cstNode.location = {
// without error recovery the starting Location of a new CstNode is guaranteed
// To be the next Token's startOffset (for valid inputs).
// For invalid inputs there won't be any CSTOutput so this potential
// inaccuracy does not matter
startOffset: this.LA(1).startOffset,
endOffset: NaN,
};
}
setInitialNodeLocationFullRecovery(cstNode) {
cstNode.location = {
startOffset: NaN,
startLine: NaN,
startColumn: NaN,
endOffset: NaN,
endLine: NaN,
endColumn: NaN,
};
}
/**
* @see setInitialNodeLocationOnlyOffsetRegular for explanation why this work
* @param cstNode
*/
setInitialNodeLocationFullRegular(cstNode) {
const nextToken = this.LA(1);
cstNode.location = {
startOffset: nextToken.startOffset,
startLine: nextToken.startLine,
startColumn: nextToken.startColumn,
endOffset: NaN,
endLine: NaN,
endColumn: NaN,
};
}
cstInvocationStateUpdate(fullRuleName) {
const cstNode = {
name: fullRuleName,
children: Object.create(null),
};
this.setInitialNodeLocation(cstNode);
this.CST_STACK.push(cstNode);
}
cstFinallyStateUpdate() {
this.CST_STACK.pop();
}
cstPostRuleFull(ruleCstNode) {
// casts to `required<CstNodeLocation>` are safe because `cstPostRuleFull` should only be invoked when full location is enabled
const prevToken = this.LA(0);
const loc = ruleCstNode.location;
// If this condition is true it means we consumed at least one Token
// In this CstNode.
if (loc.startOffset <= prevToken.startOffset === true) {
loc.endOffset = prevToken.endOffset;
loc.endLine = prevToken.endLine;
loc.endColumn = prevToken.endColumn;
}
// "empty" CstNode edge case
else {
loc.startOffset = NaN;
loc.startLine = NaN;
loc.startColumn = NaN;
}
}
cstPostRuleOnlyOffset(ruleCstNode) {
const prevToken = this.LA(0);
// `location' is not null because `cstPostRuleOnlyOffset` will only be invoked when location tracking is enabled.
const loc = ruleCstNode.location;
// If this condition is true it means we consumed at least one Token
// In this CstNode.
if (loc.startOffset <= prevToken.startOffset === true) {
loc.endOffset = prevToken.endOffset;
}
// "empty" CstNode edge case
else {
loc.startOffset = NaN;
}
}
cstPostTerminal(key, consumedToken) {
const rootCst = this.CST_STACK[this.CST_STACK.length - 1];
addTerminalToCst(rootCst, consumedToken, key);
// This is only used when **both** error recovery and CST Output are enabled.
this.setNodeLocationFromToken(rootCst.location, consumedToken);
}
cstPostNonTerminal(ruleCstResult, ruleName) {
const preCstNode = this.CST_STACK[this.CST_STACK.length - 1];
addNoneTerminalToCst(preCstNode, ruleName, ruleCstResult);
// This is only used when **both** error recovery and CST Output are enabled.
this.setNodeLocationFromNode(preCstNode.location, ruleCstResult.location);
}
getBaseCstVisitorConstructor() {
if (isUndefined(this.baseCstVisitorConstructor)) {
const newBaseCstVisitorConstructor = createBaseSemanticVisitorConstructor(this.className, keys(this.gastProductionsCache));
this.baseCstVisitorConstructor = newBaseCstVisitorConstructor;
return newBaseCstVisitorConstructor;
}
return this.baseCstVisitorConstructor;
}
getBaseCstVisitorConstructorWithDefaults() {
if (isUndefined(this.baseCstVisitorWithDefaultsConstructor)) {
const newConstructor = createBaseVisitorConstructorWithDefaults(this.className, keys(this.gastProductionsCache), this.getBaseCstVisitorConstructor());
this.baseCstVisitorWithDefaultsConstructor = newConstructor;
return newConstructor;
}
return this.baseCstVisitorWithDefaultsConstructor;
}
getLastExplicitRuleShortName() {
const ruleStack = this.RULE_STACK;
return ruleStack[ruleStack.length - 1];
}
getPreviousExplicitRuleShortName() {
const ruleStack = this.RULE_STACK;
return ruleStack[ruleStack.length - 2];
}
getLastExplicitRuleOccurrenceIndex() {
const occurrenceStack = this.RULE_OCCURRENCE_STACK;
return occurrenceStack[occurrenceStack.length - 1];
}
}
//# sourceMappingURL=tree_builder.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"version":3,"file":"apply_mixins.js","sourceRoot":"","sources":["../../../../../src/parse/parser/utils/apply_mixins.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,WAAgB,EAAE,SAAgB;IAC5D,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QAC7B,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC;QACrC,MAAM,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YACzD,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;gBAC/B,OAAO;YACT,CAAC;YAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,wBAAwB,CACxD,SAAS,EACT,QAAQ,CACT,CAAC;YACF,mBAAmB;YACnB,IACE,kBAAkB;gBAClB,CAAC,kBAAkB,CAAC,GAAG,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAClD,CAAC;gBACD,MAAM,CAAC,cAAc,CACnB,WAAW,CAAC,SAAS,EACrB,QAAQ,EACR,kBAAkB,CACnB,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,WAAW,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACjE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,87 @@
import { has, isString, isUndefined } from "lodash-es";
import { Lexer } from "./lexer_public.js";
import { augmentTokenTypes, tokenStructuredMatcher } from "./tokens.js";
export function tokenLabel(tokType) {
if (hasTokenLabel(tokType)) {
return tokType.LABEL;
}
else {
return tokType.name;
}
}
export function tokenName(tokType) {
return tokType.name;
}
export function hasTokenLabel(obj) {
return isString(obj.LABEL) && obj.LABEL !== "";
}
const PARENT = "parent";
const CATEGORIES = "categories";
const LABEL = "label";
const GROUP = "group";
const PUSH_MODE = "push_mode";
const POP_MODE = "pop_mode";
const LONGER_ALT = "longer_alt";
const LINE_BREAKS = "line_breaks";
const START_CHARS_HINT = "start_chars_hint";
export function createToken(config) {
return createTokenInternal(config);
}
function createTokenInternal(config) {
const pattern = config.pattern;
const tokenType = {};
tokenType.name = config.name;
if (!isUndefined(pattern)) {
tokenType.PATTERN = pattern;
}
if (has(config, PARENT)) {
throw ("The parent property is no longer supported.\n" +
"See: https://github.com/chevrotain/chevrotain/issues/564#issuecomment-349062346 for details.");
}
if (has(config, CATEGORIES)) {
// casting to ANY as this will be fixed inside `augmentTokenTypes``
tokenType.CATEGORIES = config[CATEGORIES];
}
augmentTokenTypes([tokenType]);
if (has(config, LABEL)) {
tokenType.LABEL = config[LABEL];
}
if (has(config, GROUP)) {
tokenType.GROUP = config[GROUP];
}
if (has(config, POP_MODE)) {
tokenType.POP_MODE = config[POP_MODE];
}
if (has(config, PUSH_MODE)) {
tokenType.PUSH_MODE = config[PUSH_MODE];
}
if (has(config, LONGER_ALT)) {
tokenType.LONGER_ALT = config[LONGER_ALT];
}
if (has(config, LINE_BREAKS)) {
tokenType.LINE_BREAKS = config[LINE_BREAKS];
}
if (has(config, START_CHARS_HINT)) {
tokenType.START_CHARS_HINT = config[START_CHARS_HINT];
}
return tokenType;
}
export const EOF = createToken({ name: "EOF", pattern: Lexer.NA });
augmentTokenTypes([EOF]);
export function createTokenInstance(tokType, image, startOffset, endOffset, startLine, endLine, startColumn, endColumn) {
return {
image,
startOffset,
endOffset,
startLine,
endLine,
startColumn,
endColumn,
tokenTypeIdx: tokType.tokenTypeIdx,
tokenType: tokType,
};
}
export function tokenMatcher(token, tokType) {
return tokenStructuredMatcher(token, tokType);
}
//# sourceMappingURL=tokens_public.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"tokens_public.js","sourceRoot":"","sources":["../../../src/scan/tokens_public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGxE,MAAM,UAAU,UAAU,CAAC,OAAkB;IAC3C,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,KAAK,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAkB;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,GAAc;IAEd,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,MAAM,GAAG,QAAQ,CAAC;AACxB,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,KAAK,GAAG,OAAO,CAAC;AACtB,MAAM,KAAK,GAAG,OAAO,CAAC;AACtB,MAAM,SAAS,GAAG,WAAW,CAAC;AAC9B,MAAM,QAAQ,GAAG,UAAU,CAAC;AAC5B,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,WAAW,GAAG,aAAa,CAAC;AAClC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAE5C,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC9C,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAoB;IAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAE/B,MAAM,SAAS,GAAmB,EAAE,CAAC;IACrC,SAAS,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IAE7B,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;IAC9B,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;QACxB,MAAM,CACJ,+CAA+C;YAC/C,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC;QAC5B,mEAAmE;QACnE,SAAS,CAAC,UAAU,GAAQ,MAAM,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;IAED,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAE/B,IAAI,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;QACvB,SAAS,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;QACvB,SAAS,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC1B,SAAS,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC;QAC7B,SAAS,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,CAAC;QAClC,SAAS,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;AACnE,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAEzB,MAAM,UAAU,mBAAmB,CACjC,OAAkB,EAClB,KAAa,EACb,WAAmB,EACnB,SAAiB,EACjB,SAAiB,EACjB,OAAe,EACf,WAAmB,EACnB,SAAiB;IAEjB,OAAO;QACL,KAAK;QACL,WAAW;QACX,SAAS;QACT,SAAS;QACT,OAAO;QACP,WAAW;QACX,SAAS;QACT,YAAY,EAAQ,OAAQ,CAAC,YAAY;QACzC,SAAS,EAAE,OAAO;KACnB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,OAAkB;IAC5D,OAAO,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC"}

View File

@@ -0,0 +1 @@
{"version":3,"file":"range.js","sourceRoot":"","sources":["../../../src/text/range.ts"],"names":[],"mappings":"AAeA,MAAM,OAAO,KAAK;IAChB,YACS,KAAa,EACb,GAAW;QADX,UAAK,GAAL,KAAK,CAAQ;QACb,QAAG,GAAH,GAAG,CAAQ;QAElB,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,OAAO,IAAI,CAAC,KAAK,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC;IAC9C,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC;IAC5D,CAAC;IAED,kBAAkB,CAAC,KAAa;QAC9B,OAAO,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,qBAAqB,CAAC,KAAa;QACjC,OAAO,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IAC1D,CAAC;IAED,0BAA0B,CAAC,KAAa;QACtC,OAAO,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,GAAW;IACrD,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,CAAC;AACrC,CAAC"}

View File

@@ -0,0 +1 @@
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,mDAAmD;AACnD,iEAAiE;AACjE,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}

95
frontend/node_modules/chevrotain/package.json generated vendored Normal file
View File

@@ -0,0 +1,95 @@
{
"name": "chevrotain",
"version": "11.1.2",
"description": "Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers",
"keywords": [
"parser",
"syntax",
"lexical",
"analysis",
"grammar",
"lexer",
"tokenizer",
"generator",
"compiler",
"fault",
"tolerant"
],
"bugs": {
"url": "https://github.com/Chevrotain/chevrotain/issues"
},
"license": "Apache-2.0",
"author": {
"name": "Shahar Soel"
},
"type": "module",
"types": "./chevrotain.d.ts",
"exports": {
".": {
"import": "./lib/src/api.js",
"require": "./lib/src/api.js",
"types": "./chevrotain.d.ts"
}
},
"files": [
"chevrotain.d.ts",
"lib/chevrotain.min.mjs",
"lib/chevrotain.mjs",
"lib/src/**/*.js",
"lib/src/**/*.map",
"src/**/*.ts",
"diagrams/**/*.*",
"BREAKING_CHANGES.md",
"CHANGELOG.md"
],
"repository": {
"type": "git",
"url": "git://github.com/Chevrotain/chevrotain.git"
},
"homepage": "https://chevrotain.io/docs/",
"scripts": {
"---------- CI FLOWS --------": "",
"ci": "pnpm run build test",
"build": "npm-run-all clean compile bundle",
"test": "npm-run-all coverage",
"version": "node ./scripts/version-update.js",
"---------- DEV FLOWS --------": "",
"watch": "tsc -w",
"unit-tests": "mocha --enable-source-maps",
"quick-build": "tsc && npm-run-all run bundle",
"---------- BUILD STEPS --------": "",
"clean": "shx rm -rf coverage dev lib temp",
"compile": "tsc",
"compile:watch": "tsc -w",
"coverage": "c8 mocha --enable-source-maps",
"---------- BUNDLING --------": "",
"bundle": "npm-run-all bundle:**",
"bundle:esm:regular": "esbuild ./lib/src/api.js --bundle --sourcemap --format=esm --outfile=lib/chevrotain.mjs",
"bundle:esm:min": "esbuild ./lib/src/api.js --bundle --minify --format=esm --sourcemap --outfile=lib/chevrotain.min.mjs"
},
"dependencies": {
"@chevrotain/cst-dts-gen": "11.1.2",
"@chevrotain/gast": "11.1.2",
"@chevrotain/regexp-to-ast": "11.1.2",
"@chevrotain/types": "11.1.2",
"@chevrotain/utils": "11.1.2",
"lodash-es": "4.17.23"
},
"devDependencies": {
"@types/jsdom": "28.0.0",
"@types/lodash-es": "4.17.12",
"@types/sinon": "21.0.0",
"@types/sinon-chai": "4.0.0",
"error-stack-parser": "2.1.4",
"esbuild": "0.27.3",
"gen-esm-wrapper": "1.1.3",
"gitty": "3.7.2",
"jsdom": "28.1.0",
"jsonfile": "6.2.0",
"require-from-string": "2.0.2",
"sinon": "21.0.1",
"sinon-chai": "4.0.1",
"xregexp": "5.1.2"
},
"gitHead": "3a0ee6abc9ef3b22448cc72ec54ec4987e44ab9b"
}

87
frontend/node_modules/chevrotain/src/api.ts generated vendored Normal file
View File

@@ -0,0 +1,87 @@
/* istanbul ignore file - tricky to import some things from this module during testing */
// semantic version
export { VERSION } from "./version.js";
export {
CstParser,
EmbeddedActionsParser,
ParserDefinitionErrorType,
EMPTY_ALT,
} from "./parse/parser/parser.js";
export { Lexer, LexerDefinitionErrorType } from "./scan/lexer_public.js";
// Tokens utilities
export {
createToken,
createTokenInstance,
EOF,
tokenLabel,
tokenMatcher,
tokenName,
} from "./scan/tokens_public.js";
// Lookahead
export { getLookaheadPaths } from "./parse/grammar/lookahead.js";
export { LLkLookaheadStrategy } from "./parse/grammar/llk_lookahead.js";
// Other Utilities
export { defaultParserErrorProvider } from "./parse/errors_public.js";
export {
EarlyExitException,
isRecognitionException,
MismatchedTokenException,
NotAllInputParsedException,
NoViableAltException,
} from "./parse/exceptions_public.js";
export { defaultLexerErrorProvider } from "./scan/lexer_errors_public.js";
// grammar reflection API
export {
Alternation,
Alternative,
NonTerminal,
Option,
Repetition,
RepetitionMandatory,
RepetitionMandatoryWithSeparator,
RepetitionWithSeparator,
Rule,
Terminal,
} from "@chevrotain/gast";
// GAST Utilities
export {
serializeGrammar,
serializeProduction,
GAstVisitor,
} from "@chevrotain/gast";
export { generateCstDts } from "@chevrotain/cst-dts-gen";
/* istanbul ignore next */
export function clearCache() {
console.warn(
"The clearCache function was 'soft' removed from the Chevrotain API." +
"\n\t It performs no action other than printing this message." +
"\n\t Please avoid using it as it will be completely removed in the future",
);
}
export { createSyntaxDiagramsCode } from "./diagrams/render_public.js";
export class Parser {
constructor() {
throw new Error(
"The Parser class has been deprecated, use CstParser or EmbeddedActionsParser instead.\t\n" +
"See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_7-0-0",
);
}
}

View File

@@ -0,0 +1,2 @@
// TODO: can this be removed? where is it used?
export const IN = "_~IN~_";

View File

@@ -0,0 +1,70 @@
import { flatten, map, uniq } from "lodash-es";
import {
isBranchingProd,
isOptionalProd,
isSequenceProd,
NonTerminal,
Terminal,
} from "@chevrotain/gast";
import { IProduction, TokenType } from "@chevrotain/types";
export function first(prod: IProduction): TokenType[] {
/* istanbul ignore else */
if (prod instanceof NonTerminal) {
// this could in theory cause infinite loops if
// (1) prod A refs prod B.
// (2) prod B refs prod A
// (3) AB can match the empty set
// in other words a cycle where everything is optional so the first will keep
// looking ahead for the next optional part and will never exit
// currently there is no safeguard for this unique edge case because
// (1) not sure a grammar in which this can happen is useful for anything (productive)
return first((<NonTerminal>prod).referencedRule);
} else if (prod instanceof Terminal) {
return firstForTerminal(<Terminal>prod);
} else if (isSequenceProd(prod)) {
return firstForSequence(prod);
} else if (isBranchingProd(prod)) {
return firstForBranching(prod);
} else {
throw Error("non exhaustive match");
}
}
export function firstForSequence(prod: {
definition: IProduction[];
}): TokenType[] {
let firstSet: TokenType[] = [];
const seq = prod.definition;
let nextSubProdIdx = 0;
let hasInnerProdsRemaining = seq.length > nextSubProdIdx;
let currSubProd;
// so we enter the loop at least once (if the definition is not empty
let isLastInnerProdOptional = true;
// scan a sequence until it's end or until we have found a NONE optional production in it
while (hasInnerProdsRemaining && isLastInnerProdOptional) {
currSubProd = seq[nextSubProdIdx];
isLastInnerProdOptional = isOptionalProd(currSubProd);
firstSet = firstSet.concat(first(currSubProd));
nextSubProdIdx = nextSubProdIdx + 1;
hasInnerProdsRemaining = seq.length > nextSubProdIdx;
}
return uniq(firstSet);
}
export function firstForBranching(prod: {
definition: IProduction[];
}): TokenType[] {
const allAlternativesFirsts: TokenType[][] = map(
prod.definition,
(innerProd) => {
return first(innerProd);
},
);
return uniq(flatten<TokenType>(allAlternativesFirsts));
}
export function firstForTerminal(terminal: Terminal): TokenType[] {
return [terminal.terminalType];
}

View File

@@ -0,0 +1,50 @@
import { Rule } from "@chevrotain/gast";
import { defaults, forEach } from "lodash-es";
import { resolveGrammar as orgResolveGrammar } from "../resolver.js";
import { validateGrammar as orgValidateGrammar } from "../checks.js";
import {
defaultGrammarResolverErrorProvider,
defaultGrammarValidatorErrorProvider,
} from "../../errors_public.js";
import { TokenType } from "@chevrotain/types";
import {
IGrammarResolverErrorMessageProvider,
IGrammarValidatorErrorMessageProvider,
IParserDefinitionError,
} from "../types.js";
type ResolveGrammarOpts = {
rules: Rule[];
errMsgProvider?: IGrammarResolverErrorMessageProvider;
};
export function resolveGrammar(
options: ResolveGrammarOpts,
): IParserDefinitionError[] {
const actualOptions: Required<ResolveGrammarOpts> = defaults(options, {
errMsgProvider: defaultGrammarResolverErrorProvider,
});
const topRulesTable: { [ruleName: string]: Rule } = {};
forEach(options.rules, (rule) => {
topRulesTable[rule.name] = rule;
});
return orgResolveGrammar(topRulesTable, actualOptions.errMsgProvider);
}
export function validateGrammar(options: {
rules: Rule[];
tokenTypes: TokenType[];
grammarName: string;
errMsgProvider: IGrammarValidatorErrorMessageProvider;
}): IParserDefinitionError[] {
options = defaults(options, {
errMsgProvider: defaultGrammarValidatorErrorProvider,
});
return orgValidateGrammar(
options.rules,
options.tokenTypes,
options.errMsgProvider,
options.grammarName,
);
}

View File

@@ -0,0 +1,623 @@
import {
clone,
drop,
dropRight,
first as _first,
forEach,
isEmpty,
last,
} from "lodash-es";
import { first } from "./first.js";
import { RestWalker } from "./rest.js";
import { TokenMatcher } from "../parser/parser.js";
import {
Alternation,
Alternative,
NonTerminal,
Option,
Repetition,
RepetitionMandatory,
RepetitionMandatoryWithSeparator,
RepetitionWithSeparator,
Rule,
Terminal,
} from "@chevrotain/gast";
import {
IGrammarPath,
IProduction,
ISyntacticContentAssistPath,
IToken,
ITokenGrammarPath,
TokenType,
} from "@chevrotain/types";
export abstract class AbstractNextPossibleTokensWalker extends RestWalker {
protected possibleTokTypes: TokenType[] = [];
protected ruleStack: string[];
protected occurrenceStack: number[];
protected nextProductionName = "";
protected nextProductionOccurrence = 0;
protected found = false;
protected isAtEndOfPath = false;
constructor(
protected topProd: Rule,
protected path: IGrammarPath,
) {
super();
}
startWalking(): TokenType[] {
this.found = false;
if (this.path.ruleStack[0] !== this.topProd.name) {
throw Error("The path does not start with the walker's top Rule!");
}
// immutable for the win
this.ruleStack = clone(this.path.ruleStack).reverse(); // intelij bug requires assertion
this.occurrenceStack = clone(this.path.occurrenceStack).reverse(); // intelij bug requires assertion
// already verified that the first production is valid, we now seek the 2nd production
this.ruleStack.pop();
this.occurrenceStack.pop();
this.updateExpectedNext();
this.walk(this.topProd);
return this.possibleTokTypes;
}
walk(
prod: { definition: IProduction[] },
prevRest: IProduction[] = [],
): void {
// stop scanning once we found the path
if (!this.found) {
super.walk(prod, prevRest);
}
}
walkProdRef(
refProd: NonTerminal,
currRest: IProduction[],
prevRest: IProduction[],
): void {
// found the next production, need to keep walking in it
if (
refProd.referencedRule.name === this.nextProductionName &&
refProd.idx === this.nextProductionOccurrence
) {
const fullRest = currRest.concat(prevRest);
this.updateExpectedNext();
this.walk(refProd.referencedRule, <any>fullRest);
}
}
updateExpectedNext(): void {
// need to consume the Terminal
if (isEmpty(this.ruleStack)) {
// must reset nextProductionXXX to avoid walking down another Top Level production while what we are
// really seeking is the last Terminal...
this.nextProductionName = "";
this.nextProductionOccurrence = 0;
this.isAtEndOfPath = true;
} else {
this.nextProductionName = this.ruleStack.pop()!;
this.nextProductionOccurrence = this.occurrenceStack.pop()!;
}
}
}
export class NextAfterTokenWalker extends AbstractNextPossibleTokensWalker {
private nextTerminalName = "";
private nextTerminalOccurrence = 0;
constructor(
topProd: Rule,
protected path: ITokenGrammarPath,
) {
super(topProd, path);
this.nextTerminalName = this.path.lastTok.name;
this.nextTerminalOccurrence = this.path.lastTokOccurrence;
}
walkTerminal(
terminal: Terminal,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (
this.isAtEndOfPath &&
terminal.terminalType.name === this.nextTerminalName &&
terminal.idx === this.nextTerminalOccurrence &&
!this.found
) {
const fullRest = currRest.concat(prevRest);
const restProd = new Alternative({ definition: fullRest });
this.possibleTokTypes = first(restProd);
this.found = true;
}
}
}
export type AlternativesFirstTokens = TokenType[][];
export interface IFirstAfterRepetition {
token: TokenType | undefined;
occurrence: number | undefined;
isEndOfRule: boolean | undefined;
}
/**
* This walker only "walks" a single "TOP" level in the Grammar Ast, this means
* it never "follows" production refs
*/
export class AbstractNextTerminalAfterProductionWalker extends RestWalker {
protected result: IFirstAfterRepetition = {
token: undefined,
occurrence: undefined,
isEndOfRule: undefined,
};
constructor(
protected topRule: Rule,
protected occurrence: number,
) {
super();
}
startWalking(): IFirstAfterRepetition {
this.walk(this.topRule);
return this.result;
}
}
export class NextTerminalAfterManyWalker extends AbstractNextTerminalAfterProductionWalker {
walkMany(
manyProd: Repetition,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (manyProd.idx === this.occurrence) {
const firstAfterMany = _first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterMany === undefined;
if (firstAfterMany instanceof Terminal) {
this.result.token = firstAfterMany.terminalType;
this.result.occurrence = firstAfterMany.idx;
}
} else {
super.walkMany(manyProd, currRest, prevRest);
}
}
}
export class NextTerminalAfterManySepWalker extends AbstractNextTerminalAfterProductionWalker {
walkManySep(
manySepProd: RepetitionWithSeparator,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (manySepProd.idx === this.occurrence) {
const firstAfterManySep = _first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterManySep === undefined;
if (firstAfterManySep instanceof Terminal) {
this.result.token = firstAfterManySep.terminalType;
this.result.occurrence = firstAfterManySep.idx;
}
} else {
super.walkManySep(manySepProd, currRest, prevRest);
}
}
}
export class NextTerminalAfterAtLeastOneWalker extends AbstractNextTerminalAfterProductionWalker {
walkAtLeastOne(
atLeastOneProd: RepetitionMandatory,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (atLeastOneProd.idx === this.occurrence) {
const firstAfterAtLeastOne = _first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterAtLeastOne === undefined;
if (firstAfterAtLeastOne instanceof Terminal) {
this.result.token = firstAfterAtLeastOne.terminalType;
this.result.occurrence = firstAfterAtLeastOne.idx;
}
} else {
super.walkAtLeastOne(atLeastOneProd, currRest, prevRest);
}
}
}
// TODO: reduce code duplication in the AfterWalkers
export class NextTerminalAfterAtLeastOneSepWalker extends AbstractNextTerminalAfterProductionWalker {
walkAtLeastOneSep(
atleastOneSepProd: RepetitionMandatoryWithSeparator,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (atleastOneSepProd.idx === this.occurrence) {
const firstAfterfirstAfterAtLeastOneSep = _first(
currRest.concat(prevRest),
);
this.result.isEndOfRule = firstAfterfirstAfterAtLeastOneSep === undefined;
if (firstAfterfirstAfterAtLeastOneSep instanceof Terminal) {
this.result.token = firstAfterfirstAfterAtLeastOneSep.terminalType;
this.result.occurrence = firstAfterfirstAfterAtLeastOneSep.idx;
}
} else {
super.walkAtLeastOneSep(atleastOneSepProd, currRest, prevRest);
}
}
}
export interface PartialPathAndSuffixes {
partialPath: TokenType[];
suffixDef: IProduction[];
}
export function possiblePathsFrom(
targetDef: IProduction[],
maxLength: number,
currPath: TokenType[] = [],
): PartialPathAndSuffixes[] {
// avoid side effects
currPath = clone(currPath);
let result: PartialPathAndSuffixes[] = [];
let i = 0;
// TODO: avoid inner funcs
function remainingPathWith(nextDef: IProduction[]) {
return nextDef.concat(drop(targetDef, i + 1));
}
// TODO: avoid inner funcs
function getAlternativesForProd(definition: IProduction[]) {
const alternatives = possiblePathsFrom(
remainingPathWith(definition),
maxLength,
currPath,
);
return result.concat(alternatives);
}
/**
* Mandatory productions will halt the loop as the paths computed from their recursive calls will already contain the
* following (rest) of the targetDef.
*
* For optional productions (Option/Repetition/...) the loop will continue to represent the paths that do not include the
* the optional production.
*/
while (currPath.length < maxLength && i < targetDef.length) {
const prod = targetDef[i];
/* istanbul ignore else */
if (prod instanceof Alternative) {
return getAlternativesForProd(prod.definition);
} else if (prod instanceof NonTerminal) {
return getAlternativesForProd(prod.definition);
} else if (prod instanceof Option) {
result = getAlternativesForProd(prod.definition);
} else if (prod instanceof RepetitionMandatory) {
const newDef = prod.definition.concat([
new Repetition({
definition: prod.definition,
}),
]);
return getAlternativesForProd(newDef);
} else if (prod instanceof RepetitionMandatoryWithSeparator) {
const newDef = [
new Alternative({ definition: prod.definition }),
new Repetition({
definition: [new Terminal({ terminalType: prod.separator })].concat(
<any>prod.definition,
),
}),
];
return getAlternativesForProd(newDef);
} else if (prod instanceof RepetitionWithSeparator) {
const newDef = prod.definition.concat([
new Repetition({
definition: [new Terminal({ terminalType: prod.separator })].concat(
<any>prod.definition,
),
}),
]);
result = getAlternativesForProd(newDef);
} else if (prod instanceof Repetition) {
const newDef = prod.definition.concat([
new Repetition({
definition: prod.definition,
}),
]);
result = getAlternativesForProd(newDef);
} else if (prod instanceof Alternation) {
forEach(prod.definition, (currAlt) => {
// TODO: this is a limited check for empty alternatives
// It would prevent a common case of infinite loops during parser initialization.
// However **in-directly** empty alternatives may still cause issues.
if (isEmpty(currAlt.definition) === false) {
result = getAlternativesForProd(currAlt.definition);
}
});
return result;
} else if (prod instanceof Terminal) {
currPath.push(prod.terminalType);
} else {
throw Error("non exhaustive match");
}
i++;
}
result.push({
partialPath: currPath,
suffixDef: drop(targetDef, i),
});
return result;
}
interface IPathToExamine {
idx: number;
def: IProduction[];
ruleStack: string[];
occurrenceStack: number[];
}
export function nextPossibleTokensAfter(
initialDef: IProduction[],
tokenVector: IToken[],
tokMatcher: TokenMatcher,
maxLookAhead: number,
): ISyntacticContentAssistPath[] {
const EXIT_NON_TERMINAL: any = "EXIT_NONE_TERMINAL";
// to avoid creating a new Array each time.
const EXIT_NON_TERMINAL_ARR = [EXIT_NON_TERMINAL];
const EXIT_ALTERNATIVE: any = "EXIT_ALTERNATIVE";
let foundCompletePath = false;
const tokenVectorLength = tokenVector.length;
const minimalAlternativesIndex = tokenVectorLength - maxLookAhead - 1;
const result: ISyntacticContentAssistPath[] = [];
const possiblePaths: IPathToExamine[] = [];
possiblePaths.push({
idx: -1,
def: initialDef,
ruleStack: [],
occurrenceStack: [],
});
while (!isEmpty(possiblePaths)) {
const currPath = possiblePaths.pop()!;
// skip alternatives if no more results can be found (assuming deterministic grammar with fixed lookahead)
if (currPath === EXIT_ALTERNATIVE) {
if (
foundCompletePath &&
last(possiblePaths)!.idx <= minimalAlternativesIndex
) {
// remove irrelevant alternative
possiblePaths.pop();
}
continue;
}
const currDef = currPath.def;
const currIdx = currPath.idx;
const currRuleStack = currPath.ruleStack;
const currOccurrenceStack = currPath.occurrenceStack;
// For Example: an empty path could exist in a valid grammar in the case of an EMPTY_ALT
if (isEmpty(currDef)) {
continue;
}
const prod = currDef[0];
/* istanbul ignore else */
if (prod === EXIT_NON_TERMINAL) {
const nextPath = {
idx: currIdx,
def: drop(currDef),
ruleStack: dropRight(currRuleStack),
occurrenceStack: dropRight(currOccurrenceStack),
};
possiblePaths.push(nextPath);
} else if (prod instanceof Terminal) {
/* istanbul ignore else */
if (currIdx < tokenVectorLength - 1) {
const nextIdx = currIdx + 1;
const actualToken = tokenVector[nextIdx];
if (tokMatcher!(actualToken, prod.terminalType)) {
const nextPath = {
idx: nextIdx,
def: drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPath);
}
// end of the line
} else if (currIdx === tokenVectorLength - 1) {
// IGNORE ABOVE ELSE
result.push({
nextTokenType: prod.terminalType,
nextTokenOccurrence: prod.idx,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
});
foundCompletePath = true;
} else {
throw Error("non exhaustive match");
}
} else if (prod instanceof NonTerminal) {
const newRuleStack = clone(currRuleStack);
newRuleStack.push(prod.nonTerminalName);
const newOccurrenceStack = clone(currOccurrenceStack);
newOccurrenceStack.push(prod.idx);
const nextPath = {
idx: currIdx,
def: prod.definition.concat(EXIT_NON_TERMINAL_ARR, drop(currDef)),
ruleStack: newRuleStack,
occurrenceStack: newOccurrenceStack,
};
possiblePaths.push(nextPath);
} else if (prod instanceof Option) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
const nextPathWithout = {
idx: currIdx,
def: drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
const nextPathWith = {
idx: currIdx,
def: prod.definition.concat(drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPathWith);
} else if (prod instanceof RepetitionMandatory) {
// TODO:(THE NEW operators here take a while...) (convert once?)
const secondIteration = new Repetition({
definition: prod.definition,
idx: prod.idx,
});
const nextDef = prod.definition.concat([secondIteration], drop(currDef));
const nextPath = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPath);
} else if (prod instanceof RepetitionMandatoryWithSeparator) {
// TODO:(THE NEW operators here take a while...) (convert once?)
const separatorGast = new Terminal({
terminalType: prod.separator,
});
const secondIteration = new Repetition({
definition: [<any>separatorGast].concat(prod.definition),
idx: prod.idx,
});
const nextDef = prod.definition.concat([secondIteration], drop(currDef));
const nextPath = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPath);
} else if (prod instanceof RepetitionWithSeparator) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
const nextPathWithout = {
idx: currIdx,
def: drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
const separatorGast = new Terminal({
terminalType: prod.separator,
});
const nthRepetition = new Repetition({
definition: [<any>separatorGast].concat(prod.definition),
idx: prod.idx,
});
const nextDef = prod.definition.concat([nthRepetition], drop(currDef));
const nextPathWith = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPathWith);
} else if (prod instanceof Repetition) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
const nextPathWithout = {
idx: currIdx,
def: drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
// TODO: an empty repetition will cause infinite loops here, will the parser detect this in selfAnalysis?
const nthRepetition = new Repetition({
definition: prod.definition,
idx: prod.idx,
});
const nextDef = prod.definition.concat([nthRepetition], drop(currDef));
const nextPathWith = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(nextPathWith);
} else if (prod instanceof Alternation) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
for (let i = prod.definition.length - 1; i >= 0; i--) {
const currAlt: any = prod.definition[i];
const currAltPath = {
idx: currIdx,
def: currAlt.definition.concat(drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
};
possiblePaths.push(currAltPath);
possiblePaths.push(EXIT_ALTERNATIVE);
}
} else if (prod instanceof Alternative) {
possiblePaths.push({
idx: currIdx,
def: prod.definition.concat(drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack,
});
} else if (prod instanceof Rule) {
// last because we should only encounter at most a single one of these per invocation.
possiblePaths.push(
expandTopLevelRule(prod, currIdx, currRuleStack, currOccurrenceStack),
);
} else {
throw Error("non exhaustive match");
}
}
return result;
}
function expandTopLevelRule(
topRule: Rule,
currIdx: number,
currRuleStack: string[],
currOccurrenceStack: number[],
): IPathToExamine {
const newRuleStack = clone(currRuleStack);
newRuleStack.push(topRule.name);
const newCurrOccurrenceStack = clone(currOccurrenceStack);
// top rule is always assumed to have been called with occurrence index 1
newCurrOccurrenceStack.push(1);
return {
idx: currIdx,
def: topRule.definition,
ruleStack: newRuleStack,
occurrenceStack: newCurrOccurrenceStack,
};
}

View File

@@ -0,0 +1,33 @@
// Lookahead keys are 32Bit integers in the form
// TTTTTTTT-ZZZZZZZZZZZZ-YYYY-XXXXXXXX
// XXXX -> Occurrence Index bitmap.
// YYYY -> DSL Method Type bitmap.
// ZZZZZZZZZZZZZZZ -> Rule short Index bitmap.
// TTTTTTTTT -> alternation alternative index bitmap
export const BITS_FOR_METHOD_TYPE = 4;
export const BITS_FOR_OCCURRENCE_IDX = 8;
export const BITS_FOR_RULE_IDX = 12;
// TODO: validation, this means that there may at most 2^8 --> 256 alternatives for an alternation.
export const BITS_FOR_ALT_IDX = 8;
// short string used as part of mapping keys.
// being short improves the performance when composing KEYS for maps out of these
// The 5 - 8 bits (16 possible values, are reserved for the DSL method indices)
export const OR_IDX = 1 << BITS_FOR_OCCURRENCE_IDX;
export const OPTION_IDX = 2 << BITS_FOR_OCCURRENCE_IDX;
export const MANY_IDX = 3 << BITS_FOR_OCCURRENCE_IDX;
export const AT_LEAST_ONE_IDX = 4 << BITS_FOR_OCCURRENCE_IDX;
export const MANY_SEP_IDX = 5 << BITS_FOR_OCCURRENCE_IDX;
export const AT_LEAST_ONE_SEP_IDX = 6 << BITS_FOR_OCCURRENCE_IDX;
// this actually returns a number, but it is always used as a string (object prop key)
export function getKeyForAutomaticLookahead(
ruleIdx: number,
dslMethodIdx: number,
occurrence: number,
): number {
return occurrence | dslMethodIdx | ruleIdx;
}
const BITS_START_FOR_ALT_IDX = 32 - BITS_FOR_ALT_IDX;

View File

@@ -0,0 +1,739 @@
import { every, flatten, forEach, has, isEmpty, map, reduce } from "lodash-es";
import { possiblePathsFrom } from "./interpreter.js";
import { RestWalker } from "./rest.js";
import { Predicate, TokenMatcher } from "../parser/parser.js";
import {
tokenStructuredMatcher,
tokenStructuredMatcherNoCategories,
} from "../../scan/tokens.js";
import {
Alternation,
Alternative as AlternativeGAST,
GAstVisitor,
Option,
Repetition,
RepetitionMandatory,
RepetitionMandatoryWithSeparator,
RepetitionWithSeparator,
} from "@chevrotain/gast";
import {
BaseParser,
IOrAlt,
IProduction,
IProductionWithOccurrence,
LookaheadProductionType,
LookaheadSequence,
Rule,
TokenType,
} from "@chevrotain/types";
export enum PROD_TYPE {
OPTION,
REPETITION,
REPETITION_MANDATORY,
REPETITION_MANDATORY_WITH_SEPARATOR,
REPETITION_WITH_SEPARATOR,
ALTERNATION,
}
export function getProdType(
prod: IProduction | LookaheadProductionType,
): PROD_TYPE {
/* istanbul ignore else */
if (prod instanceof Option || prod === "Option") {
return PROD_TYPE.OPTION;
} else if (prod instanceof Repetition || prod === "Repetition") {
return PROD_TYPE.REPETITION;
} else if (
prod instanceof RepetitionMandatory ||
prod === "RepetitionMandatory"
) {
return PROD_TYPE.REPETITION_MANDATORY;
} else if (
prod instanceof RepetitionMandatoryWithSeparator ||
prod === "RepetitionMandatoryWithSeparator"
) {
return PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR;
} else if (
prod instanceof RepetitionWithSeparator ||
prod === "RepetitionWithSeparator"
) {
return PROD_TYPE.REPETITION_WITH_SEPARATOR;
} else if (prod instanceof Alternation || prod === "Alternation") {
return PROD_TYPE.ALTERNATION;
} else {
throw Error("non exhaustive match");
}
}
export function getLookaheadPaths(options: {
occurrence: number;
rule: Rule;
prodType: LookaheadProductionType;
maxLookahead: number;
}): LookaheadSequence[] {
const { occurrence, rule, prodType, maxLookahead } = options;
const type = getProdType(prodType);
if (type === PROD_TYPE.ALTERNATION) {
return getLookaheadPathsForOr(occurrence, rule, maxLookahead);
} else {
return getLookaheadPathsForOptionalProd(
occurrence,
rule,
type,
maxLookahead,
);
}
}
export function buildLookaheadFuncForOr(
occurrence: number,
ruleGrammar: Rule,
maxLookahead: number,
hasPredicates: boolean,
dynamicTokensEnabled: boolean,
laFuncBuilder: Function,
): (orAlts?: IOrAlt<any>[]) => number | undefined {
const lookAheadPaths = getLookaheadPathsForOr(
occurrence,
ruleGrammar,
maxLookahead,
);
const tokenMatcher = areTokenCategoriesNotUsed(lookAheadPaths)
? tokenStructuredMatcherNoCategories
: tokenStructuredMatcher;
return laFuncBuilder(
lookAheadPaths,
hasPredicates,
tokenMatcher,
dynamicTokensEnabled,
);
}
/**
* When dealing with an Optional production (OPTION/MANY/2nd iteration of AT_LEAST_ONE/...) we need to compare
* the lookahead "inside" the production and the lookahead immediately "after" it in the same top level rule (context free).
*
* Example: given a production:
* ABC(DE)?DF
*
* The optional '(DE)?' should only be entered if we see 'DE'. a single Token 'D' is not sufficient to distinguish between the two
* alternatives.
*
* @returns A Lookahead function which will return true IFF the parser should parse the Optional production.
*/
export function buildLookaheadFuncForOptionalProd(
occurrence: number,
ruleGrammar: Rule,
k: number,
dynamicTokensEnabled: boolean,
prodType: PROD_TYPE,
lookaheadBuilder: (
lookAheadSequence: LookaheadSequence,
tokenMatcher: TokenMatcher,
dynamicTokensEnabled: boolean,
) => () => boolean,
): () => boolean {
const lookAheadPaths = getLookaheadPathsForOptionalProd(
occurrence,
ruleGrammar,
prodType,
k,
);
const tokenMatcher = areTokenCategoriesNotUsed(lookAheadPaths)
? tokenStructuredMatcherNoCategories
: tokenStructuredMatcher;
return lookaheadBuilder(
lookAheadPaths[0],
tokenMatcher,
dynamicTokensEnabled,
);
}
export type Alternative = TokenType[][];
export function buildAlternativesLookAheadFunc(
alts: LookaheadSequence[],
hasPredicates: boolean,
tokenMatcher: TokenMatcher,
dynamicTokensEnabled: boolean,
): (orAlts: IOrAlt<any>[]) => number | undefined {
const numOfAlts = alts.length;
const areAllOneTokenLookahead = every(alts, (currAlt) => {
return every(currAlt, (currPath) => {
return currPath.length === 1;
});
});
// This version takes into account the predicates as well.
if (hasPredicates) {
/**
* @returns {number} - The chosen alternative index
*/
return function (
this: BaseParser,
orAlts: IOrAlt<any>[],
): number | undefined {
// unfortunately the predicates must be extracted every single time
// as they cannot be cached due to references to parameters(vars) which are no longer valid.
// note that in the common case of no predicates, no cpu time will be wasted on this (see else block)
const predicates: (Predicate | undefined)[] = map(
orAlts,
(currAlt) => currAlt.GATE,
);
for (let t = 0; t < numOfAlts; t++) {
const currAlt = alts[t];
const currNumOfPaths = currAlt.length;
const currPredicate = predicates[t];
if (currPredicate !== undefined && currPredicate.call(this) === false) {
// if the predicate does not match there is no point in checking the paths
continue;
}
nextPath: for (let j = 0; j < currNumOfPaths; j++) {
const currPath = currAlt[j];
const currPathLength = currPath.length;
for (let i = 0; i < currPathLength; i++) {
const nextToken = this.LA(i + 1);
if (tokenMatcher(nextToken, currPath[i]) === false) {
// mismatch in current path
// try the next pth
continue nextPath;
}
}
// found a full path that matches.
// this will also work for an empty ALT as the loop will be skipped
return t;
}
// none of the paths for the current alternative matched
// try the next alternative
}
// none of the alternatives could be matched
return undefined;
};
} else if (areAllOneTokenLookahead && !dynamicTokensEnabled) {
// optimized (common) case of all the lookaheads paths requiring only
// a single token lookahead. These Optimizations cannot work if dynamically defined Tokens are used.
const singleTokenAlts = map(alts, (currAlt) => {
return flatten(currAlt);
});
const choiceToAlt = reduce(
singleTokenAlts,
(result, currAlt, idx) => {
forEach(currAlt, (currTokType) => {
if (!has(result, currTokType.tokenTypeIdx!)) {
result[currTokType.tokenTypeIdx!] = idx;
}
forEach(currTokType.categoryMatches!, (currExtendingType) => {
if (!has(result, currExtendingType)) {
result[currExtendingType] = idx;
}
});
});
return result;
},
{} as Record<number, number>,
);
/**
* @returns {number} - The chosen alternative index
*/
return function (this: BaseParser): number {
const nextToken = this.LA(1);
return choiceToAlt[nextToken.tokenTypeIdx];
};
} else {
// optimized lookahead without needing to check the predicates at all.
// this causes code duplication which is intentional to improve performance.
/**
* @returns {number} - The chosen alternative index
*/
return function (this: BaseParser): number | undefined {
for (let t = 0; t < numOfAlts; t++) {
const currAlt = alts[t];
const currNumOfPaths = currAlt.length;
nextPath: for (let j = 0; j < currNumOfPaths; j++) {
const currPath = currAlt[j];
const currPathLength = currPath.length;
for (let i = 0; i < currPathLength; i++) {
const nextToken = this.LA(i + 1);
if (tokenMatcher(nextToken, currPath[i]) === false) {
// mismatch in current path
// try the next pth
continue nextPath;
}
}
// found a full path that matches.
// this will also work for an empty ALT as the loop will be skipped
return t;
}
// none of the paths for the current alternative matched
// try the next alternative
}
// none of the alternatives could be matched
return undefined;
};
}
}
export function buildSingleAlternativeLookaheadFunction(
alt: LookaheadSequence,
tokenMatcher: TokenMatcher,
dynamicTokensEnabled: boolean,
): () => boolean {
const areAllOneTokenLookahead = every(alt, (currPath) => {
return currPath.length === 1;
});
const numOfPaths = alt.length;
// optimized (common) case of all the lookaheads paths requiring only
// a single token lookahead.
if (areAllOneTokenLookahead && !dynamicTokensEnabled) {
const singleTokensTypes = flatten(alt);
if (
singleTokensTypes.length === 1 &&
isEmpty((<any>singleTokensTypes[0]).categoryMatches)
) {
const expectedTokenType = singleTokensTypes[0];
const expectedTokenUniqueKey = (<any>expectedTokenType).tokenTypeIdx;
return function (this: BaseParser): boolean {
return this.LA(1).tokenTypeIdx === expectedTokenUniqueKey;
};
} else {
const choiceToAlt = reduce(
singleTokensTypes,
(result, currTokType, idx) => {
result[currTokType.tokenTypeIdx!] = true;
forEach(currTokType.categoryMatches!, (currExtendingType) => {
result[currExtendingType] = true;
});
return result;
},
[] as boolean[],
);
return function (this: BaseParser): boolean {
const nextToken = this.LA(1);
return choiceToAlt[nextToken.tokenTypeIdx] === true;
};
}
} else {
return function (this: BaseParser): boolean {
nextPath: for (let j = 0; j < numOfPaths; j++) {
const currPath = alt[j];
const currPathLength = currPath.length;
for (let i = 0; i < currPathLength; i++) {
const nextToken = this.LA(i + 1);
if (tokenMatcher(nextToken, currPath[i]) === false) {
// mismatch in current path
// try the next pth
continue nextPath;
}
}
// found a full path that matches.
return true;
}
// none of the paths matched
return false;
};
}
}
class RestDefinitionFinderWalker extends RestWalker {
private restDef: IProduction[];
constructor(
private topProd: Rule,
private targetOccurrence: number,
private targetProdType: PROD_TYPE,
) {
super();
}
startWalking(): IProduction[] {
this.walk(this.topProd);
return this.restDef;
}
private checkIsTarget(
node: IProductionWithOccurrence,
expectedProdType: PROD_TYPE,
currRest: IProduction[],
prevRest: IProduction[],
): boolean {
if (
node.idx === this.targetOccurrence &&
this.targetProdType === expectedProdType
) {
this.restDef = currRest.concat(prevRest);
return true;
}
// performance optimization, do not iterate over the entire Grammar ast after we have found the target
return false;
}
walkOption(
optionProd: Option,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (!this.checkIsTarget(optionProd, PROD_TYPE.OPTION, currRest, prevRest)) {
super.walkOption(optionProd, currRest, prevRest);
}
}
walkAtLeastOne(
atLeastOneProd: RepetitionMandatory,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (
!this.checkIsTarget(
atLeastOneProd,
PROD_TYPE.REPETITION_MANDATORY,
currRest,
prevRest,
)
) {
super.walkOption(atLeastOneProd, currRest, prevRest);
}
}
walkAtLeastOneSep(
atLeastOneSepProd: RepetitionMandatoryWithSeparator,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (
!this.checkIsTarget(
atLeastOneSepProd,
PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR,
currRest,
prevRest,
)
) {
super.walkOption(atLeastOneSepProd, currRest, prevRest);
}
}
walkMany(
manyProd: Repetition,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (
!this.checkIsTarget(manyProd, PROD_TYPE.REPETITION, currRest, prevRest)
) {
super.walkOption(manyProd, currRest, prevRest);
}
}
walkManySep(
manySepProd: RepetitionWithSeparator,
currRest: IProduction[],
prevRest: IProduction[],
): void {
if (
!this.checkIsTarget(
manySepProd,
PROD_TYPE.REPETITION_WITH_SEPARATOR,
currRest,
prevRest,
)
) {
super.walkOption(manySepProd, currRest, prevRest);
}
}
}
/**
* Returns the definition of a target production in a top level level rule.
*/
class InsideDefinitionFinderVisitor extends GAstVisitor {
public result: IProduction[] = [];
constructor(
private targetOccurrence: number,
private targetProdType: PROD_TYPE,
private targetRef?: any,
) {
super();
}
private checkIsTarget(
node: { definition: IProduction[] } & IProductionWithOccurrence,
expectedProdName: PROD_TYPE,
): void {
if (
node.idx === this.targetOccurrence &&
this.targetProdType === expectedProdName &&
(this.targetRef === undefined || node === this.targetRef)
) {
this.result = node.definition;
}
}
public visitOption(node: Option): void {
this.checkIsTarget(node, PROD_TYPE.OPTION);
}
public visitRepetition(node: Repetition): void {
this.checkIsTarget(node, PROD_TYPE.REPETITION);
}
public visitRepetitionMandatory(node: RepetitionMandatory): void {
this.checkIsTarget(node, PROD_TYPE.REPETITION_MANDATORY);
}
public visitRepetitionMandatoryWithSeparator(
node: RepetitionMandatoryWithSeparator,
): void {
this.checkIsTarget(node, PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR);
}
public visitRepetitionWithSeparator(node: RepetitionWithSeparator): void {
this.checkIsTarget(node, PROD_TYPE.REPETITION_WITH_SEPARATOR);
}
public visitAlternation(node: Alternation): void {
this.checkIsTarget(node, PROD_TYPE.ALTERNATION);
}
}
function initializeArrayOfArrays(size: number): any[][] {
const result = new Array(size);
for (let i = 0; i < size; i++) {
result[i] = [];
}
return result;
}
/**
* A sort of hash function between a Path in the grammar and a string.
* Note that this returns multiple "hashes" to support the scenario of token categories.
* - A single path with categories may match multiple **actual** paths.
*/
function pathToHashKeys(path: TokenType[]): string[] {
let keys = [""];
for (let i = 0; i < path.length; i++) {
const tokType = path[i];
const longerKeys = [];
for (let j = 0; j < keys.length; j++) {
const currShorterKey = keys[j];
longerKeys.push(currShorterKey + "_" + tokType.tokenTypeIdx);
for (let t = 0; t < tokType.categoryMatches!.length; t++) {
const categoriesKeySuffix = "_" + tokType.categoryMatches![t];
longerKeys.push(currShorterKey + categoriesKeySuffix);
}
}
keys = longerKeys;
}
return keys;
}
/**
* Imperative style due to being called from a hot spot
*/
function isUniquePrefixHash(
altKnownPathsKeys: Record<string, boolean>[],
searchPathKeys: string[],
idx: number,
): boolean {
for (
let currAltIdx = 0;
currAltIdx < altKnownPathsKeys.length;
currAltIdx++
) {
// We only want to test vs the other alternatives
if (currAltIdx === idx) {
continue;
}
const otherAltKnownPathsKeys = altKnownPathsKeys[currAltIdx];
for (let searchIdx = 0; searchIdx < searchPathKeys.length; searchIdx++) {
const searchKey = searchPathKeys[searchIdx];
if (otherAltKnownPathsKeys[searchKey] === true) {
return false;
}
}
}
// None of the SearchPathKeys were found in any of the other alternatives
return true;
}
export function lookAheadSequenceFromAlternatives(
altsDefs: IProduction[],
k: number,
): LookaheadSequence[] {
const partialAlts = map(altsDefs, (currAlt) =>
possiblePathsFrom([currAlt], 1),
);
const finalResult = initializeArrayOfArrays(partialAlts.length);
const altsHashes = map(partialAlts, (currAltPaths) => {
const dict: { [key: string]: boolean } = {};
forEach(currAltPaths, (item) => {
const keys = pathToHashKeys(item.partialPath);
forEach(keys, (currKey) => {
dict[currKey] = true;
});
});
return dict;
});
let newData = partialAlts;
// maxLookahead loop
for (let pathLength = 1; pathLength <= k; pathLength++) {
const currDataset = newData;
newData = initializeArrayOfArrays(currDataset.length);
// alternatives loop
for (let altIdx = 0; altIdx < currDataset.length; altIdx++) {
const currAltPathsAndSuffixes = currDataset[altIdx];
// paths in current alternative loop
for (
let currPathIdx = 0;
currPathIdx < currAltPathsAndSuffixes.length;
currPathIdx++
) {
const currPathPrefix = currAltPathsAndSuffixes[currPathIdx].partialPath;
const suffixDef = currAltPathsAndSuffixes[currPathIdx].suffixDef;
const prefixKeys = pathToHashKeys(currPathPrefix);
const isUnique = isUniquePrefixHash(altsHashes, prefixKeys, altIdx);
// End of the line for this path.
if (isUnique || isEmpty(suffixDef) || currPathPrefix.length === k) {
const currAltResult = finalResult[altIdx];
// TODO: Can we implement a containsPath using Maps/Dictionaries?
if (containsPath(currAltResult, currPathPrefix) === false) {
currAltResult.push(currPathPrefix);
// Update all new keys for the current path.
for (let j = 0; j < prefixKeys.length; j++) {
const currKey = prefixKeys[j];
altsHashes[altIdx][currKey] = true;
}
}
}
// Expand longer paths
else {
const newPartialPathsAndSuffixes = possiblePathsFrom(
suffixDef,
pathLength + 1,
currPathPrefix,
);
newData[altIdx] = newData[altIdx].concat(newPartialPathsAndSuffixes);
// Update keys for new known paths
forEach(newPartialPathsAndSuffixes, (item) => {
const prefixKeys = pathToHashKeys(item.partialPath);
forEach(prefixKeys, (key) => {
altsHashes[altIdx][key] = true;
});
});
}
}
}
}
return finalResult;
}
export function getLookaheadPathsForOr(
occurrence: number,
ruleGrammar: Rule,
k: number,
orProd?: Alternation,
): LookaheadSequence[] {
const visitor = new InsideDefinitionFinderVisitor(
occurrence,
PROD_TYPE.ALTERNATION,
orProd,
);
ruleGrammar.accept(visitor);
return lookAheadSequenceFromAlternatives(visitor.result, k);
}
export function getLookaheadPathsForOptionalProd(
occurrence: number,
ruleGrammar: Rule,
prodType: PROD_TYPE,
k: number,
): LookaheadSequence[] {
const insideDefVisitor = new InsideDefinitionFinderVisitor(
occurrence,
prodType,
);
ruleGrammar.accept(insideDefVisitor);
const insideDef = insideDefVisitor.result;
const afterDefWalker = new RestDefinitionFinderWalker(
ruleGrammar,
occurrence,
prodType,
);
const afterDef = afterDefWalker.startWalking();
const insideFlat = new AlternativeGAST({ definition: insideDef });
const afterFlat = new AlternativeGAST({ definition: afterDef });
return lookAheadSequenceFromAlternatives([insideFlat, afterFlat], k);
}
export function containsPath(
alternative: Alternative,
searchPath: TokenType[],
): boolean {
compareOtherPath: for (let i = 0; i < alternative.length; i++) {
const otherPath = alternative[i];
if (otherPath.length !== searchPath.length) {
continue;
}
for (let j = 0; j < otherPath.length; j++) {
const searchTok = searchPath[j];
const otherTok = otherPath[j];
const matchingTokens =
searchTok === otherTok ||
otherTok.categoryMatchesMap![searchTok.tokenTypeIdx!] !== undefined;
if (matchingTokens === false) {
continue compareOtherPath;
}
}
return true;
}
return false;
}
export function isStrictPrefixOfPath(
prefix: TokenType[],
other: TokenType[],
): boolean {
return (
prefix.length < other.length &&
every(prefix, (tokType, idx) => {
const otherTokType = other[idx];
return (
tokType === otherTokType ||
otherTokType.categoryMatchesMap![tokType.tokenTypeIdx!]
);
})
);
}
export function areTokenCategoriesNotUsed(
lookAheadPaths: LookaheadSequence[],
): boolean {
return every(lookAheadPaths, (singleAltPaths) =>
every(singleAltPaths, (singlePath) =>
every(singlePath, (token) => isEmpty(token.categoryMatches!)),
),
);
}

View File

@@ -0,0 +1,57 @@
import {
IParserUnresolvedRefDefinitionError,
ParserDefinitionErrorType,
} from "../parser/parser.js";
import { forEach, values } from "lodash-es";
import { GAstVisitor, NonTerminal, Rule } from "@chevrotain/gast";
import {
IGrammarResolverErrorMessageProvider,
IParserDefinitionError,
} from "./types.js";
export function resolveGrammar(
topLevels: Record<string, Rule>,
errMsgProvider: IGrammarResolverErrorMessageProvider,
): IParserDefinitionError[] {
const refResolver = new GastRefResolverVisitor(topLevels, errMsgProvider);
refResolver.resolveRefs();
return refResolver.errors;
}
export class GastRefResolverVisitor extends GAstVisitor {
public errors: IParserUnresolvedRefDefinitionError[] = [];
private currTopLevel: Rule;
constructor(
private nameToTopRule: Record<string, Rule>,
private errMsgProvider: IGrammarResolverErrorMessageProvider,
) {
super();
}
public resolveRefs(): void {
forEach(values(this.nameToTopRule), (prod) => {
this.currTopLevel = prod;
prod.accept(this);
});
}
public visitNonTerminal(node: NonTerminal): void {
const ref = this.nameToTopRule[node.nonTerminalName];
if (!ref) {
const msg = this.errMsgProvider.buildRuleNotFoundError(
this.currTopLevel,
node,
);
this.errors.push({
message: msg,
type: ParserDefinitionErrorType.UNRESOLVED_SUBRULE_REF,
ruleName: this.currTopLevel.name,
unresolvedRefName: node.nonTerminalName,
});
} else {
node.referencedRule = ref;
}
}
}

View File

@@ -0,0 +1,314 @@
import { clone, forEach, has, isEmpty, map, values } from "lodash-es";
import { toFastProperties } from "@chevrotain/utils";
import { computeAllProdsFollows } from "../grammar/follow.js";
import { createTokenInstance, EOF } from "../../scan/tokens_public.js";
import {
defaultGrammarValidatorErrorProvider,
defaultParserErrorProvider,
} from "../errors_public.js";
import {
resolveGrammar,
validateGrammar,
} from "../grammar/gast/gast_resolver_public.js";
import {
CstNode,
IParserConfig,
IRecognitionException,
IRuleConfig,
IToken,
TokenType,
TokenVocabulary,
} from "@chevrotain/types";
import { Recoverable } from "./traits/recoverable.js";
import { LooksAhead } from "./traits/looksahead.js";
import { TreeBuilder } from "./traits/tree_builder.js";
import { LexerAdapter } from "./traits/lexer_adapter.js";
import { RecognizerApi } from "./traits/recognizer_api.js";
import { RecognizerEngine } from "./traits/recognizer_engine.js";
import { ErrorHandler } from "./traits/error_handler.js";
import { MixedInParser } from "./traits/parser_traits.js";
import { ContentAssist } from "./traits/context_assist.js";
import { GastRecorder } from "./traits/gast_recorder.js";
import { PerformanceTracer } from "./traits/perf_tracer.js";
import { applyMixins } from "./utils/apply_mixins.js";
import { IParserDefinitionError } from "../grammar/types.js";
import { Rule } from "@chevrotain/gast";
import { IParserConfigInternal, ParserMethodInternal } from "./types.js";
import { validateLookahead } from "../grammar/checks.js";
export const END_OF_FILE = createTokenInstance(
EOF,
"",
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
);
Object.freeze(END_OF_FILE);
export type TokenMatcher = (token: IToken, tokType: TokenType) => boolean;
export const DEFAULT_PARSER_CONFIG: Required<
Omit<IParserConfigInternal, "lookaheadStrategy">
> = Object.freeze({
recoveryEnabled: false,
maxLookahead: 3,
dynamicTokensEnabled: false,
outputCst: true,
errorMessageProvider: defaultParserErrorProvider,
nodeLocationTracking: "none",
traceInitPerf: false,
skipValidations: false,
});
export const DEFAULT_RULE_CONFIG: Required<IRuleConfig<any>> = Object.freeze({
recoveryValueFunc: () => undefined,
resyncEnabled: true,
});
export enum ParserDefinitionErrorType {
INVALID_RULE_NAME = 0,
DUPLICATE_RULE_NAME = 1,
INVALID_RULE_OVERRIDE = 2,
DUPLICATE_PRODUCTIONS = 3,
UNRESOLVED_SUBRULE_REF = 4,
LEFT_RECURSION = 5,
NONE_LAST_EMPTY_ALT = 6,
AMBIGUOUS_ALTS = 7,
CONFLICT_TOKENS_RULES_NAMESPACE = 8,
INVALID_TOKEN_NAME = 9,
NO_NON_EMPTY_LOOKAHEAD = 10,
AMBIGUOUS_PREFIX_ALTS = 11,
TOO_MANY_ALTS = 12,
CUSTOM_LOOKAHEAD_VALIDATION = 13,
}
export interface IParserDuplicatesDefinitionError extends IParserDefinitionError {
dslName: string;
occurrence: number;
parameter?: string;
}
export interface IParserEmptyAlternativeDefinitionError extends IParserDefinitionError {
occurrence: number;
alternative: number;
}
export interface IParserAmbiguousAlternativesDefinitionError extends IParserDefinitionError {
occurrence: number | string;
alternatives: number[];
}
export interface IParserUnresolvedRefDefinitionError extends IParserDefinitionError {
unresolvedRefName: string;
}
export interface IParserState {
errors: IRecognitionException[];
lexerState: any;
RULE_STACK: number[];
CST_STACK: CstNode[];
}
export type Predicate = () => boolean;
export function EMPTY_ALT(): () => undefined;
export function EMPTY_ALT<T>(value: T): () => T;
export function EMPTY_ALT(value: any = undefined) {
return function () {
return value;
};
}
export class Parser {
// Set this flag to true if you don't want the Parser to throw error when problems in it's definition are detected.
// (normally during the parser's constructor).
// This is a design time flag, it will not affect the runtime error handling of the parser, just design time errors,
// for example: duplicate rule names, referencing an unresolved subrule, etc...
// This flag should not be enabled during normal usage, it is used in special situations, for example when
// needing to display the parser definition errors in some GUI(online playground).
static DEFER_DEFINITION_ERRORS_HANDLING: boolean = false;
/**
* @deprecated use the **instance** method with the same name instead
*/
static performSelfAnalysis(parserInstance: Parser): void {
throw Error(
"The **static** `performSelfAnalysis` method has been deprecated." +
"\t\nUse the **instance** method with the same name instead.",
);
}
public performSelfAnalysis(this: MixedInParser): void {
this.TRACE_INIT("performSelfAnalysis", () => {
let defErrorsMsgs;
this.selfAnalysisDone = true;
const className = this.className;
this.TRACE_INIT("toFastProps", () => {
// Without this voodoo magic the parser would be x3-x4 slower
// It seems it is better to invoke `toFastProperties` **before**
// Any manipulations of the `this` object done during the recording phase.
toFastProperties(this);
});
this.TRACE_INIT("Grammar Recording", () => {
try {
this.enableRecording();
// Building the GAST
forEach(this.definedRulesNames, (currRuleName) => {
const wrappedRule = (this as any)[
currRuleName
] as ParserMethodInternal<unknown[], unknown>;
const originalGrammarAction = wrappedRule["originalGrammarAction"];
let recordedRuleGast!: Rule;
this.TRACE_INIT(`${currRuleName} Rule`, () => {
recordedRuleGast = this.topLevelRuleRecord(
currRuleName,
originalGrammarAction,
);
});
this.gastProductionsCache[currRuleName] = recordedRuleGast;
});
} finally {
this.disableRecording();
}
});
let resolverErrors: IParserDefinitionError[] = [];
this.TRACE_INIT("Grammar Resolving", () => {
resolverErrors = resolveGrammar({
rules: values(this.gastProductionsCache),
});
this.definitionErrors = this.definitionErrors.concat(resolverErrors);
});
this.TRACE_INIT("Grammar Validations", () => {
// only perform additional grammar validations IFF no resolving errors have occurred.
// as unresolved grammar may lead to unhandled runtime exceptions in the follow up validations.
if (isEmpty(resolverErrors) && this.skipValidations === false) {
const validationErrors = validateGrammar({
rules: values(this.gastProductionsCache),
tokenTypes: values(this.tokensMap),
errMsgProvider: defaultGrammarValidatorErrorProvider,
grammarName: className,
});
const lookaheadValidationErrors = validateLookahead({
lookaheadStrategy: this.lookaheadStrategy,
rules: values(this.gastProductionsCache),
tokenTypes: values(this.tokensMap),
grammarName: className,
});
this.definitionErrors = this.definitionErrors.concat(
validationErrors,
lookaheadValidationErrors,
);
}
});
// this analysis may fail if the grammar is not perfectly valid
if (isEmpty(this.definitionErrors)) {
// The results of these computations are not needed unless error recovery is enabled.
if (this.recoveryEnabled) {
this.TRACE_INIT("computeAllProdsFollows", () => {
const allFollows = computeAllProdsFollows(
values(this.gastProductionsCache),
);
this.resyncFollows = allFollows;
});
}
this.TRACE_INIT("ComputeLookaheadFunctions", () => {
this.lookaheadStrategy.initialize?.({
rules: values(this.gastProductionsCache),
});
this.preComputeLookaheadFunctions(values(this.gastProductionsCache));
});
}
if (
!Parser.DEFER_DEFINITION_ERRORS_HANDLING &&
!isEmpty(this.definitionErrors)
) {
defErrorsMsgs = map(
this.definitionErrors,
(defError) => defError.message,
);
throw new Error(
`Parser Definition Errors detected:\n ${defErrorsMsgs.join(
"\n-------------------------------\n",
)}`,
);
}
});
}
definitionErrors: IParserDefinitionError[] = [];
selfAnalysisDone = false;
protected skipValidations: boolean;
constructor(tokenVocabulary: TokenVocabulary, config: IParserConfig) {
const that: MixedInParser = this as any;
that.initErrorHandler(config);
that.initLexerAdapter();
that.initLooksAhead(config);
that.initRecognizerEngine(tokenVocabulary, config);
that.initRecoverable(config);
that.initTreeBuilder(config);
that.initContentAssist();
that.initGastRecorder(config);
that.initPerformanceTracer(config);
if (has(config, "ignoredIssues")) {
throw new Error(
"The <ignoredIssues> IParserConfig property has been deprecated.\n\t" +
"Please use the <IGNORE_AMBIGUITIES> flag on the relevant DSL method instead.\n\t" +
"See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#IGNORING_AMBIGUITIES\n\t" +
"For further details.",
);
}
this.skipValidations = has(config, "skipValidations")
? (config.skipValidations as boolean) // casting assumes the end user passing the correct type
: DEFAULT_PARSER_CONFIG.skipValidations;
}
}
applyMixins(Parser, [
Recoverable,
LooksAhead,
TreeBuilder,
LexerAdapter,
RecognizerEngine,
RecognizerApi,
ErrorHandler,
ContentAssist,
GastRecorder,
PerformanceTracer,
]);
export class CstParser extends Parser {
constructor(
tokenVocabulary: TokenVocabulary,
config: IParserConfigInternal = DEFAULT_PARSER_CONFIG,
) {
const configClone = clone(config);
configClone.outputCst = true;
super(tokenVocabulary, configClone);
}
}
export class EmbeddedActionsParser extends Parser {
constructor(
tokenVocabulary: TokenVocabulary,
config: IParserConfigInternal = DEFAULT_PARSER_CONFIG,
) {
const configClone = clone(config);
configClone.outputCst = false;
super(tokenVocabulary, configClone);
}
}

View File

@@ -0,0 +1,269 @@
import { forEach, has } from "lodash-es";
import { DEFAULT_PARSER_CONFIG } from "../parser.js";
import {
ILookaheadStrategy,
IParserConfig,
OptionalProductionType,
} from "@chevrotain/types";
import {
AT_LEAST_ONE_IDX,
AT_LEAST_ONE_SEP_IDX,
getKeyForAutomaticLookahead,
MANY_IDX,
MANY_SEP_IDX,
OPTION_IDX,
OR_IDX,
} from "../../grammar/keys.js";
import { MixedInParser } from "./parser_traits.js";
import {
Alternation,
GAstVisitor,
getProductionDslName,
Option,
Repetition,
RepetitionMandatory,
RepetitionMandatoryWithSeparator,
RepetitionWithSeparator,
Rule,
} from "@chevrotain/gast";
import { LLkLookaheadStrategy } from "../../grammar/llk_lookahead.js";
/**
* Trait responsible for the lookahead related utilities and optimizations.
*/
export class LooksAhead {
maxLookahead: number;
lookAheadFuncsCache: any;
dynamicTokensEnabled: boolean;
lookaheadStrategy: ILookaheadStrategy;
initLooksAhead(config: IParserConfig) {
this.dynamicTokensEnabled = has(config, "dynamicTokensEnabled")
? (config.dynamicTokensEnabled as boolean) // assumes end user provides the correct config value/type
: DEFAULT_PARSER_CONFIG.dynamicTokensEnabled;
this.maxLookahead = has(config, "maxLookahead")
? (config.maxLookahead as number) // assumes end user provides the correct config value/type
: DEFAULT_PARSER_CONFIG.maxLookahead;
this.lookaheadStrategy = has(config, "lookaheadStrategy")
? (config.lookaheadStrategy as ILookaheadStrategy) // assumes end user provides the correct config value/type
: new LLkLookaheadStrategy({ maxLookahead: this.maxLookahead });
this.lookAheadFuncsCache = new Map();
}
preComputeLookaheadFunctions(this: MixedInParser, rules: Rule[]): void {
forEach(rules, (currRule) => {
this.TRACE_INIT(`${currRule.name} Rule Lookahead`, () => {
const {
alternation,
repetition,
option,
repetitionMandatory,
repetitionMandatoryWithSeparator,
repetitionWithSeparator,
} = collectMethods(currRule);
forEach(alternation, (currProd) => {
const prodIdx = currProd.idx === 0 ? "" : currProd.idx;
this.TRACE_INIT(`${getProductionDslName(currProd)}${prodIdx}`, () => {
const laFunc = this.lookaheadStrategy.buildLookaheadForAlternation({
prodOccurrence: currProd.idx,
rule: currRule,
maxLookahead: currProd.maxLookahead || this.maxLookahead,
hasPredicates: currProd.hasPredicates,
dynamicTokensEnabled: this.dynamicTokensEnabled,
});
const key = getKeyForAutomaticLookahead(
this.fullRuleNameToShort[currRule.name],
OR_IDX,
currProd.idx,
);
this.setLaFuncCache(key, laFunc);
});
});
forEach(repetition, (currProd) => {
this.computeLookaheadFunc(
currRule,
currProd.idx,
MANY_IDX,
"Repetition",
currProd.maxLookahead,
getProductionDslName(currProd),
);
});
forEach(option, (currProd) => {
this.computeLookaheadFunc(
currRule,
currProd.idx,
OPTION_IDX,
"Option",
currProd.maxLookahead,
getProductionDslName(currProd),
);
});
forEach(repetitionMandatory, (currProd) => {
this.computeLookaheadFunc(
currRule,
currProd.idx,
AT_LEAST_ONE_IDX,
"RepetitionMandatory",
currProd.maxLookahead,
getProductionDslName(currProd),
);
});
forEach(repetitionMandatoryWithSeparator, (currProd) => {
this.computeLookaheadFunc(
currRule,
currProd.idx,
AT_LEAST_ONE_SEP_IDX,
"RepetitionMandatoryWithSeparator",
currProd.maxLookahead,
getProductionDslName(currProd),
);
});
forEach(repetitionWithSeparator, (currProd) => {
this.computeLookaheadFunc(
currRule,
currProd.idx,
MANY_SEP_IDX,
"RepetitionWithSeparator",
currProd.maxLookahead,
getProductionDslName(currProd),
);
});
});
});
}
computeLookaheadFunc(
this: MixedInParser,
rule: Rule,
prodOccurrence: number,
prodKey: number,
prodType: OptionalProductionType,
prodMaxLookahead: number | undefined,
dslMethodName: string,
): void {
this.TRACE_INIT(
`${dslMethodName}${prodOccurrence === 0 ? "" : prodOccurrence}`,
() => {
const laFunc = this.lookaheadStrategy.buildLookaheadForOptional({
prodOccurrence,
rule,
maxLookahead: prodMaxLookahead || this.maxLookahead,
dynamicTokensEnabled: this.dynamicTokensEnabled,
prodType,
});
const key = getKeyForAutomaticLookahead(
this.fullRuleNameToShort[rule.name],
prodKey,
prodOccurrence,
);
this.setLaFuncCache(key, laFunc);
},
);
}
// this actually returns a number, but it is always used as a string (object prop key)
getKeyForAutomaticLookahead(
this: MixedInParser,
dslMethodIdx: number,
occurrence: number,
): number {
const currRuleShortName: any = this.getLastExplicitRuleShortName();
return getKeyForAutomaticLookahead(
currRuleShortName,
dslMethodIdx,
occurrence,
);
}
getLaFuncFromCache(this: MixedInParser, key: number): Function {
return this.lookAheadFuncsCache.get(key);
}
/* istanbul ignore next */
setLaFuncCache(this: MixedInParser, key: number, value: Function): void {
this.lookAheadFuncsCache.set(key, value);
}
}
class DslMethodsCollectorVisitor extends GAstVisitor {
public dslMethods: {
option: Option[];
alternation: Alternation[];
repetition: Repetition[];
repetitionWithSeparator: RepetitionWithSeparator[];
repetitionMandatory: RepetitionMandatory[];
repetitionMandatoryWithSeparator: RepetitionMandatoryWithSeparator[];
} = {
option: [],
alternation: [],
repetition: [],
repetitionWithSeparator: [],
repetitionMandatory: [],
repetitionMandatoryWithSeparator: [],
};
reset() {
this.dslMethods = {
option: [],
alternation: [],
repetition: [],
repetitionWithSeparator: [],
repetitionMandatory: [],
repetitionMandatoryWithSeparator: [],
};
}
public visitOption(option: Option): void {
this.dslMethods.option.push(option);
}
public visitRepetitionWithSeparator(manySep: RepetitionWithSeparator): void {
this.dslMethods.repetitionWithSeparator.push(manySep);
}
public visitRepetitionMandatory(atLeastOne: RepetitionMandatory): void {
this.dslMethods.repetitionMandatory.push(atLeastOne);
}
public visitRepetitionMandatoryWithSeparator(
atLeastOneSep: RepetitionMandatoryWithSeparator,
): void {
this.dslMethods.repetitionMandatoryWithSeparator.push(atLeastOneSep);
}
public visitRepetition(many: Repetition): void {
this.dslMethods.repetition.push(many);
}
public visitAlternation(or: Alternation): void {
this.dslMethods.alternation.push(or);
}
}
const collectorVisitor = new DslMethodsCollectorVisitor();
export function collectMethods(rule: Rule): {
option: Option[];
alternation: Alternation[];
repetition: Repetition[];
repetitionWithSeparator: RepetitionWithSeparator[];
repetitionMandatory: RepetitionMandatory[];
repetitionMandatoryWithSeparator: RepetitionMandatoryWithSeparator[];
} {
collectorVisitor.reset();
rule.accept(collectorVisitor);
const dslMethods = collectorVisitor.dslMethods;
// avoid uncleaned references
collectorVisitor.reset();
return <any>dslMethods;
}

View File

@@ -0,0 +1,54 @@
import { IParserConfig } from "@chevrotain/types";
import { has } from "lodash-es";
import { timer } from "@chevrotain/utils";
import { MixedInParser } from "./parser_traits.js";
import { DEFAULT_PARSER_CONFIG } from "../parser.js";
/**
* Trait responsible for runtime parsing errors.
*/
export class PerformanceTracer {
traceInitPerf: boolean | number;
traceInitMaxIdent: number;
traceInitIndent: number;
initPerformanceTracer(config: IParserConfig) {
if (has(config, "traceInitPerf")) {
const userTraceInitPerf = config.traceInitPerf;
const traceIsNumber = typeof userTraceInitPerf === "number";
this.traceInitMaxIdent = traceIsNumber
? <number>userTraceInitPerf
: Infinity;
this.traceInitPerf = traceIsNumber
? userTraceInitPerf > 0
: (userTraceInitPerf as boolean); // assumes end user provides the correct config value/type
} else {
this.traceInitMaxIdent = 0;
this.traceInitPerf = DEFAULT_PARSER_CONFIG.traceInitPerf;
}
this.traceInitIndent = -1;
}
TRACE_INIT<T>(this: MixedInParser, phaseDesc: string, phaseImpl: () => T): T {
// No need to optimize this using NOOP pattern because
// It is not called in a hot spot...
if (this.traceInitPerf === true) {
this.traceInitIndent++;
const indent = new Array(this.traceInitIndent + 1).join("\t");
if (this.traceInitIndent < this.traceInitMaxIdent) {
console.log(`${indent}--> <${phaseDesc}>`);
}
const { time, value } = timer(phaseImpl);
/* istanbul ignore next - Difficult to reproduce specific performance behavior (>10ms) in tests */
const traceMethod = time > 10 ? console.warn : console.log;
if (this.traceInitIndent < this.traceInitMaxIdent) {
traceMethod(`${indent}<-- <${phaseDesc}> time: ${time}ms`);
}
this.traceInitIndent--;
return value;
} else {
return phaseImpl();
}
}
}

View File

@@ -0,0 +1,719 @@
import {
AtLeastOneSepMethodOpts,
ConsumeMethodOpts,
DSLMethodOpts,
DSLMethodOptsWithErr,
GrammarAction,
IOrAlt,
IRuleConfig,
ISerializedGast,
IToken,
ManySepMethodOpts,
OrMethodOpts,
SubruleMethodOpts,
TokenType,
} from "@chevrotain/types";
import { includes, values } from "lodash-es";
import { isRecognitionException } from "../../exceptions_public.js";
import { DEFAULT_RULE_CONFIG, ParserDefinitionErrorType } from "../parser.js";
import { defaultGrammarValidatorErrorProvider } from "../../errors_public.js";
import { validateRuleIsOverridden } from "../../grammar/checks.js";
import { MixedInParser } from "./parser_traits.js";
import { Rule, serializeGrammar } from "@chevrotain/gast";
import { IParserDefinitionError } from "../../grammar/types.js";
import { ParserMethodInternal } from "../types.js";
/**
* This trait is responsible for implementing the public API
* for defining Chevrotain parsers, i.e:
* - CONSUME
* - RULE
* - OPTION
* - ...
*/
export class RecognizerApi {
ACTION<T>(this: MixedInParser, impl: () => T): T {
return impl.call(this);
}
consume(
this: MixedInParser,
idx: number,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, idx, options);
}
subrule<ARGS extends unknown[], R>(
this: MixedInParser,
idx: number,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, idx, options);
}
option<OUT>(
this: MixedInParser,
idx: number,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, idx);
}
or(
this: MixedInParser,
idx: number,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<any>,
): any {
return this.orInternal(altsOrOpts, idx);
}
many(
this: MixedInParser,
idx: number,
actionORMethodDef: GrammarAction<any> | DSLMethodOpts<any>,
): void {
return this.manyInternal(idx, actionORMethodDef);
}
atLeastOne(
this: MixedInParser,
idx: number,
actionORMethodDef: GrammarAction<any> | DSLMethodOptsWithErr<any>,
): void {
return this.atLeastOneInternal(idx, actionORMethodDef);
}
CONSUME(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 0, options);
}
CONSUME1(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 1, options);
}
CONSUME2(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 2, options);
}
CONSUME3(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 3, options);
}
CONSUME4(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 4, options);
}
CONSUME5(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 5, options);
}
CONSUME6(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 6, options);
}
CONSUME7(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 7, options);
}
CONSUME8(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 8, options);
}
CONSUME9(
this: MixedInParser,
tokType: TokenType,
options?: ConsumeMethodOpts,
): IToken {
return this.consumeInternal(tokType, 9, options);
}
SUBRULE<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 0, options);
}
SUBRULE1<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 1, options);
}
SUBRULE2<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 2, options);
}
SUBRULE3<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 3, options);
}
SUBRULE4<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 4, options);
}
SUBRULE5<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 5, options);
}
SUBRULE6<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 6, options);
}
SUBRULE7<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 7, options);
}
SUBRULE8<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 8, options);
}
SUBRULE9<ARGS extends unknown[], R>(
this: MixedInParser,
ruleToCall: ParserMethodInternal<ARGS, R>,
options?: SubruleMethodOpts<ARGS>,
): R {
return this.subruleInternal(ruleToCall, 9, options);
}
OPTION<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 0);
}
OPTION1<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 1);
}
OPTION2<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 2);
}
OPTION3<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 3);
}
OPTION4<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 4);
}
OPTION5<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 5);
}
OPTION6<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 6);
}
OPTION7<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 7);
}
OPTION8<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 8);
}
OPTION9<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): OUT | undefined {
return this.optionInternal(actionORMethodDef, 9);
}
OR<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 0);
}
OR1<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 1);
}
OR2<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 2);
}
OR3<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 3);
}
OR4<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 4);
}
OR5<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 5);
}
OR6<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 6);
}
OR7<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 7);
}
OR8<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 8);
}
OR9<T>(
this: MixedInParser,
altsOrOpts: IOrAlt<any>[] | OrMethodOpts<unknown>,
): T {
return this.orInternal(altsOrOpts, 9);
}
MANY<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(0, actionORMethodDef);
}
MANY1<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(1, actionORMethodDef);
}
MANY2<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(2, actionORMethodDef);
}
MANY3<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(3, actionORMethodDef);
}
MANY4<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(4, actionORMethodDef);
}
MANY5<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(5, actionORMethodDef);
}
MANY6<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(6, actionORMethodDef);
}
MANY7<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(7, actionORMethodDef);
}
MANY8<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(8, actionORMethodDef);
}
MANY9<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOpts<OUT>,
): void {
this.manyInternal(9, actionORMethodDef);
}
MANY_SEP<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(0, options);
}
MANY_SEP1<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(1, options);
}
MANY_SEP2<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(2, options);
}
MANY_SEP3<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(3, options);
}
MANY_SEP4<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(4, options);
}
MANY_SEP5<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(5, options);
}
MANY_SEP6<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(6, options);
}
MANY_SEP7<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(7, options);
}
MANY_SEP8<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(8, options);
}
MANY_SEP9<OUT>(this: MixedInParser, options: ManySepMethodOpts<OUT>): void {
this.manySepFirstInternal(9, options);
}
AT_LEAST_ONE<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(0, actionORMethodDef);
}
AT_LEAST_ONE1<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
return this.atLeastOneInternal(1, actionORMethodDef);
}
AT_LEAST_ONE2<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(2, actionORMethodDef);
}
AT_LEAST_ONE3<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(3, actionORMethodDef);
}
AT_LEAST_ONE4<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(4, actionORMethodDef);
}
AT_LEAST_ONE5<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(5, actionORMethodDef);
}
AT_LEAST_ONE6<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(6, actionORMethodDef);
}
AT_LEAST_ONE7<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(7, actionORMethodDef);
}
AT_LEAST_ONE8<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(8, actionORMethodDef);
}
AT_LEAST_ONE9<OUT>(
this: MixedInParser,
actionORMethodDef: GrammarAction<OUT> | DSLMethodOptsWithErr<OUT>,
): void {
this.atLeastOneInternal(9, actionORMethodDef);
}
AT_LEAST_ONE_SEP<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(0, options);
}
AT_LEAST_ONE_SEP1<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(1, options);
}
AT_LEAST_ONE_SEP2<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(2, options);
}
AT_LEAST_ONE_SEP3<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(3, options);
}
AT_LEAST_ONE_SEP4<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(4, options);
}
AT_LEAST_ONE_SEP5<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(5, options);
}
AT_LEAST_ONE_SEP6<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(6, options);
}
AT_LEAST_ONE_SEP7<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(7, options);
}
AT_LEAST_ONE_SEP8<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(8, options);
}
AT_LEAST_ONE_SEP9<OUT>(
this: MixedInParser,
options: AtLeastOneSepMethodOpts<OUT>,
): void {
this.atLeastOneSepFirstInternal(9, options);
}
RULE<T>(
this: MixedInParser,
name: string,
implementation: (...implArgs: any[]) => T,
config: IRuleConfig<T> = DEFAULT_RULE_CONFIG,
): (idxInCallingRule?: number, ...args: any[]) => T | any {
if (includes(this.definedRulesNames, name)) {
const errMsg =
defaultGrammarValidatorErrorProvider.buildDuplicateRuleNameError({
topLevelRule: name,
grammarName: this.className,
});
const error = {
message: errMsg,
type: ParserDefinitionErrorType.DUPLICATE_RULE_NAME,
ruleName: name,
};
this.definitionErrors.push(error);
}
this.definedRulesNames.push(name);
const ruleImplementation = this.defineRule(name, implementation, config);
(this as any)[name] = ruleImplementation;
return ruleImplementation;
}
OVERRIDE_RULE<T>(
this: MixedInParser,
name: string,
impl: (...implArgs: any[]) => T,
config: IRuleConfig<T> = DEFAULT_RULE_CONFIG,
): (idxInCallingRule?: number, ...args: any[]) => T {
const ruleErrors: IParserDefinitionError[] = validateRuleIsOverridden(
name,
this.definedRulesNames,
this.className,
);
this.definitionErrors = this.definitionErrors.concat(ruleErrors);
const ruleImplementation = this.defineRule(name, impl, config);
(this as any)[name] = ruleImplementation;
return ruleImplementation;
}
BACKTRACK<T>(
this: MixedInParser,
grammarRule: (...args: any[]) => T,
args?: any[],
): () => boolean {
return function () {
// save org state
this.isBackTrackingStack.push(1);
const orgState = this.saveRecogState();
try {
grammarRule.apply(this, args);
// if no exception was thrown we have succeed parsing the rule.
return true;
} catch (e) {
if (isRecognitionException(e)) {
return false;
} else {
throw e;
}
} finally {
this.reloadRecogState(orgState);
this.isBackTrackingStack.pop();
}
};
}
// GAST export APIs
public getGAstProductions(this: MixedInParser): Record<string, Rule> {
return this.gastProductionsCache;
}
public getSerializedGastProductions(this: MixedInParser): ISerializedGast[] {
return serializeGrammar(values(this.gastProductionsCache));
}
}

1165
frontend/node_modules/chevrotain/src/scan/lexer.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

323
frontend/node_modules/chevrotain/src/scan/reg_exp.ts generated vendored Normal file
View File

@@ -0,0 +1,323 @@
import {
Alternative,
Atom,
BaseRegExpVisitor,
Character,
Disjunction,
Group,
Set,
} from "@chevrotain/regexp-to-ast";
import { every, find, forEach, includes, isArray, values } from "lodash-es";
import { PRINT_ERROR, PRINT_WARNING } from "@chevrotain/utils";
import { ASTNode, getRegExpAst } from "./reg_exp_parser.js";
import { charCodeToOptimizedIndex, minOptimizationVal } from "./lexer.js";
const complementErrorMessage =
"Complement Sets are not supported for first char optimization";
export const failedOptimizationPrefixMsg =
'Unable to use "first char" lexer optimizations:\n';
export function getOptimizedStartCodesIndices(
regExp: RegExp,
ensureOptimizations = false,
): number[] {
try {
const ast = getRegExpAst(regExp);
const firstChars = firstCharOptimizedIndices(
ast.value,
{},
ast.flags.ignoreCase,
);
return firstChars;
} catch (e) {
/* istanbul ignore next */
// Testing this relies on the regexp-to-ast library having a bug... */
// TODO: only the else branch needs to be ignored, try to fix with newer prettier / tsc
if (e.message === complementErrorMessage) {
if (ensureOptimizations) {
PRINT_WARNING(
`${failedOptimizationPrefixMsg}` +
`\tUnable to optimize: < ${regExp.toString()} >\n` +
"\tComplement Sets cannot be automatically optimized.\n" +
"\tThis will disable the lexer's first char optimizations.\n" +
"\tSee: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#COMPLEMENT for details.",
);
}
} else {
let msgSuffix = "";
if (ensureOptimizations) {
msgSuffix =
"\n\tThis will disable the lexer's first char optimizations.\n" +
"\tSee: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#REGEXP_PARSING for details.";
}
PRINT_ERROR(
`${failedOptimizationPrefixMsg}\n` +
`\tFailed parsing: < ${regExp.toString()} >\n` +
`\tUsing the @chevrotain/regexp-to-ast library\n` +
"\tPlease open an issue at: https://github.com/chevrotain/chevrotain/issues" +
msgSuffix,
);
}
}
return [];
}
export function firstCharOptimizedIndices(
ast: ASTNode,
result: { [charCode: number]: number },
ignoreCase: boolean,
): number[] {
switch (ast.type) {
case "Disjunction":
for (let i = 0; i < ast.value.length; i++) {
firstCharOptimizedIndices(ast.value[i], result, ignoreCase);
}
break;
case "Alternative":
const terms = ast.value;
for (let i = 0; i < terms.length; i++) {
const term = terms[i];
// skip terms that cannot effect the first char results
switch (term.type) {
case "EndAnchor":
// A group back reference cannot affect potential starting char.
// because if a back reference is the first production than automatically
// the group being referenced has had to come BEFORE so its codes have already been added
case "GroupBackReference":
// assertions do not affect potential starting codes
case "Lookahead":
case "NegativeLookahead":
case "Lookbehind":
case "NegativeLookbehind":
case "StartAnchor":
case "WordBoundary":
case "NonWordBoundary":
continue;
}
const atom = term;
switch (atom.type) {
case "Character":
addOptimizedIdxToResult(atom.value, result, ignoreCase);
break;
case "Set":
if (atom.complement === true) {
throw Error(complementErrorMessage);
}
forEach(atom.value, (code) => {
if (typeof code === "number") {
addOptimizedIdxToResult(code, result, ignoreCase);
} else {
// range
const range = code as any;
// cannot optimize when ignoreCase is
if (ignoreCase === true) {
for (
let rangeCode = range.from;
rangeCode <= range.to;
rangeCode++
) {
addOptimizedIdxToResult(rangeCode, result, ignoreCase);
}
}
// Optimization (2 orders of magnitude less work for very large ranges)
else {
// handle unoptimized values
for (
let rangeCode = range.from;
rangeCode <= range.to && rangeCode < minOptimizationVal;
rangeCode++
) {
addOptimizedIdxToResult(rangeCode, result, ignoreCase);
}
// Less common charCode where we optimize for faster init time, by using larger "buckets"
if (range.to >= minOptimizationVal) {
const minUnOptVal =
range.from >= minOptimizationVal
? range.from
: minOptimizationVal;
const maxUnOptVal = range.to;
const minOptIdx = charCodeToOptimizedIndex(minUnOptVal);
const maxOptIdx = charCodeToOptimizedIndex(maxUnOptVal);
for (
let currOptIdx = minOptIdx;
currOptIdx <= maxOptIdx;
currOptIdx++
) {
result[currOptIdx] = currOptIdx;
}
}
}
}
});
break;
case "Group":
firstCharOptimizedIndices(atom.value, result, ignoreCase);
break;
/* istanbul ignore next */
default:
throw Error("Non Exhaustive Match");
}
// reached a mandatory production, no more **start** codes can be found on this alternative
const isOptionalQuantifier =
atom.quantifier !== undefined && atom.quantifier.atLeast === 0;
if (
// A group may be optional due to empty contents /(?:)/
// or if everything inside it is optional /((a)?)/
(atom.type === "Group" && isWholeOptional(atom) === false) ||
// If this term is not a group it may only be optional if it has an optional quantifier
(atom.type !== "Group" && isOptionalQuantifier === false)
) {
break;
}
}
break;
/* istanbul ignore next */
default:
throw Error("non exhaustive match!");
}
// console.log(Object.keys(result).length)
return values(result);
}
function addOptimizedIdxToResult(
code: number,
result: { [charCode: number]: number },
ignoreCase: boolean,
) {
const optimizedCharIdx = charCodeToOptimizedIndex(code);
result[optimizedCharIdx] = optimizedCharIdx;
if (ignoreCase === true) {
handleIgnoreCase(code, result);
}
}
function handleIgnoreCase(
code: number,
result: { [charCode: number]: number },
) {
const char = String.fromCharCode(code);
const upperChar = char.toUpperCase();
/* istanbul ignore else */
if (upperChar !== char) {
const optimizedCharIdx = charCodeToOptimizedIndex(upperChar.charCodeAt(0));
result[optimizedCharIdx] = optimizedCharIdx;
} else {
const lowerChar = char.toLowerCase();
if (lowerChar !== char) {
const optimizedCharIdx = charCodeToOptimizedIndex(
lowerChar.charCodeAt(0),
);
result[optimizedCharIdx] = optimizedCharIdx;
}
}
}
function findCode(setNode: Set, targetCharCodes: number[]) {
return find(setNode.value, (codeOrRange) => {
if (typeof codeOrRange === "number") {
return includes(targetCharCodes, codeOrRange);
} else {
// range
const range = <any>codeOrRange;
return (
find(
targetCharCodes,
(targetCode) => range.from <= targetCode && targetCode <= range.to,
) !== undefined
);
}
});
}
function isWholeOptional(ast: any): boolean {
const quantifier = (ast as Atom).quantifier;
if (quantifier && quantifier.atLeast === 0) {
return true;
}
if (!ast.value) {
return false;
}
return isArray(ast.value)
? every(ast.value, isWholeOptional)
: isWholeOptional(ast.value);
}
class CharCodeFinder extends BaseRegExpVisitor {
found: boolean = false;
constructor(private targetCharCodes: number[]) {
super();
}
visitChildren(node: ASTNode) {
// No need to keep looking...
if (this.found === true) {
return;
}
// switch lookaheads / lookbehinds as they do not actually consume any characters thus
// finding a charCode at lookahead context does not mean that regexp can actually contain it in a match.
switch (node.type) {
case "Lookahead":
this.visitLookahead(node);
return;
case "NegativeLookahead":
this.visitNegativeLookahead(node);
return;
case "Lookbehind":
this.visitLookbehind(node);
return;
case "NegativeLookbehind":
this.visitNegativeLookbehind(node);
return;
}
super.visitChildren(node);
}
visitCharacter(node: Character) {
if (includes(this.targetCharCodes, node.value)) {
this.found = true;
}
}
visitSet(node: Set) {
if (node.complement) {
if (findCode(node, this.targetCharCodes) === undefined) {
this.found = true;
}
} else {
if (findCode(node, this.targetCharCodes) !== undefined) {
this.found = true;
}
}
}
}
export function canMatchCharCode(
charCodes: number[],
pattern: RegExp | string,
) {
if (pattern instanceof RegExp) {
const ast = getRegExpAst(pattern);
const charCodeFinder = new CharCodeFinder(charCodes);
charCodeFinder.visit(ast);
return charCodeFinder.found;
} else {
return (
find(<any>pattern, (char) => {
return includes(charCodes, (<string>char).charCodeAt(0));
}) !== undefined
);
}
}

View File

@@ -0,0 +1 @@
export const EOF_TOKEN_TYPE = 1;