import { every, includes, some } from "lodash-es"; import { AbstractProduction, Alternation, Alternative, NonTerminal, Option, Repetition, RepetitionMandatory, RepetitionMandatoryWithSeparator, RepetitionWithSeparator, Rule, Terminal, } from "./model.js"; import type { IProduction, IProductionWithOccurrence } from "@chevrotain/types"; export function isSequenceProd( prod: IProduction, ): prod is { definition: IProduction[] } & IProduction { return ( prod instanceof Alternative || prod instanceof Option || prod instanceof Repetition || prod instanceof RepetitionMandatory || prod instanceof RepetitionMandatoryWithSeparator || prod instanceof RepetitionWithSeparator || prod instanceof Terminal || prod instanceof Rule ); } export function isOptionalProd( prod: IProduction, alreadyVisited: NonTerminal[] = [], ): boolean { const isDirectlyOptional = prod instanceof Option || prod instanceof Repetition || prod instanceof RepetitionWithSeparator; if (isDirectlyOptional) { return true; } // note that this can cause infinite loop if one optional empty TOP production has a cyclic dependency with another // empty optional top rule // may be indirectly optional ((A?B?C?) | (D?E?F?)) if (prod instanceof Alternation) { // for OR its enough for just one of the alternatives to be optional return some((prod).definition, (subProd: IProduction) => { return isOptionalProd(subProd, alreadyVisited); }); } else if (prod instanceof NonTerminal && includes(alreadyVisited, prod)) { // avoiding stack overflow due to infinite recursion return false; } else if (prod instanceof AbstractProduction) { if (prod instanceof NonTerminal) { alreadyVisited.push(prod); } return every( (prod).definition, (subProd: IProduction) => { return isOptionalProd(subProd, alreadyVisited); }, ); } else { return false; } } export function isBranchingProd( prod: IProduction, ): prod is { definition: IProduction[] } & IProduction { return prod instanceof Alternation; } export function getProductionDslName(prod: IProductionWithOccurrence): string { /* istanbul ignore else */ if (prod instanceof NonTerminal) { return "SUBRULE"; } else if (prod instanceof Option) { return "OPTION"; } else if (prod instanceof Alternation) { return "OR"; } else if (prod instanceof RepetitionMandatory) { return "AT_LEAST_ONE"; } else if (prod instanceof RepetitionMandatoryWithSeparator) { return "AT_LEAST_ONE_SEP"; } else if (prod instanceof RepetitionWithSeparator) { return "MANY_SEP"; } else if (prod instanceof Repetition) { return "MANY"; } else if (prod instanceof Terminal) { return "CONSUME"; /* c8 ignore next 3 */ } else { throw Error("non exhaustive match"); } }