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