chevrotain
Version:
Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers
102 lines • 4.57 kB
JavaScript
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 = []) {
prod.definition.forEach((subProd, index) => {
const currRest = prod.definition.slice(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
orProd.definition.forEach((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