UNPKG

chevrotain

Version:

Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers

321 lines (283 loc) 10.3 kB
import { hasTokenLabel, tokenLabel } from "../scan/tokens_public" import * as utils from "../utils/utils" import { first, map, reduce } from "../utils/utils" import { Alternation, NonTerminal, Rule, Terminal } from "./grammar/gast/gast_public" import { getProductionDslName } from "./grammar/gast/gast" import { IGrammarResolverErrorMessageProvider, IGrammarValidatorErrorMessageProvider, IParserErrorMessageProvider, IProductionWithOccurrence, TokenType } from "../../api" export const defaultParserErrorProvider: IParserErrorMessageProvider = { buildMismatchTokenMessage({ expected, actual, previous, ruleName }): string { let hasLabel = hasTokenLabel(expected) let expectedMsg = hasLabel ? `--> ${tokenLabel(expected)} <--` : `token of type --> ${expected.name} <--` let msg = `Expecting ${expectedMsg} but found --> '${actual.image}' <--` return msg }, buildNotAllInputParsedMessage({ firstRedundant, ruleName }): string { return "Redundant input, expecting EOF but found: " + firstRedundant.image }, buildNoViableAltMessage({ expectedPathsPerAlt, actual, previous, customUserDescription, ruleName }): string { let errPrefix = "Expecting: " // TODO: issue: No Viable Alternative Error may have incomplete details. #502 let actualText = first(actual).image let errSuffix = "\nbut found: '" + actualText + "'" if (customUserDescription) { return errPrefix + customUserDescription + errSuffix } else { let allLookAheadPaths = reduce( expectedPathsPerAlt, (result, currAltPaths) => result.concat(currAltPaths), [] ) let nextValidTokenSequences = map( allLookAheadPaths, (currPath) => `[${map(currPath, (currTokenType) => tokenLabel(currTokenType)).join( ", " )}]` ) let nextValidSequenceItems = map( nextValidTokenSequences, (itemMsg, idx) => ` ${idx + 1}. ${itemMsg}` ) let calculatedDescription = `one of these possible Token sequences:\n${nextValidSequenceItems.join( "\n" )}` return errPrefix + calculatedDescription + errSuffix } }, buildEarlyExitMessage({ expectedIterationPaths, actual, customUserDescription, ruleName }): string { let errPrefix = "Expecting: " // TODO: issue: No Viable Alternative Error may have incomplete details. #502 let actualText = first(actual).image let errSuffix = "\nbut found: '" + actualText + "'" if (customUserDescription) { return errPrefix + customUserDescription + errSuffix } else { let nextValidTokenSequences = map( expectedIterationPaths, (currPath) => `[${map(currPath, (currTokenType) => tokenLabel(currTokenType)).join( "," )}]` ) let calculatedDescription = `expecting at least one iteration which starts with one of these possible Token sequences::\n ` + `<${nextValidTokenSequences.join(" ,")}>` return errPrefix + calculatedDescription + errSuffix } } } Object.freeze(defaultParserErrorProvider) export const defaultGrammarResolverErrorProvider: IGrammarResolverErrorMessageProvider = { buildRuleNotFoundError( topLevelRule: Rule, undefinedRule: NonTerminal ): string { const msg = "Invalid grammar, reference to a rule which is not defined: ->" + undefinedRule.nonTerminalName + "<-\n" + "inside top level rule: ->" + topLevelRule.name + "<-" return msg } } export const defaultGrammarValidatorErrorProvider: IGrammarValidatorErrorMessageProvider = { buildDuplicateFoundError( topLevelRule: Rule, duplicateProds: IProductionWithOccurrence[] ): string { function getExtraProductionArgument( prod: IProductionWithOccurrence ): string { if (prod instanceof Terminal) { return prod.terminalType.name } else if (prod instanceof NonTerminal) { return prod.nonTerminalName } else { return "" } } const topLevelName = topLevelRule.name const duplicateProd = first(duplicateProds) const index = duplicateProd.idx const dslName = getProductionDslName(duplicateProd) let extraArgument = getExtraProductionArgument(duplicateProd) const hasExplicitIndex = index > 0 let msg = `->${dslName}${hasExplicitIndex ? index : ""}<- ${ extraArgument ? `with argument: ->${extraArgument}<-` : "" } appears more than once (${ duplicateProds.length } times) in the top level rule: ->${topLevelName}<-. For further details see: https://chevrotain.io/docs/FAQ.html#NUMERICAL_SUFFIXES ` // white space trimming time! better to trim afterwards as it allows to use WELL formatted multi line template strings... msg = msg.replace(/[ \t]+/g, " ") msg = msg.replace(/\s\s+/g, "\n") return msg }, buildNamespaceConflictError(rule: Rule): string { const errMsg = `Namespace conflict found in grammar.\n` + `The grammar has both a Terminal(Token) and a Non-Terminal(Rule) named: <${rule.name}>.\n` + `To resolve this make sure each Terminal and Non-Terminal names are unique\n` + `This is easy to accomplish by using the convention that Terminal names start with an uppercase letter\n` + `and Non-Terminal names start with a lower case letter.` return errMsg }, buildAlternationPrefixAmbiguityError(options: { topLevelRule: Rule prefixPath: TokenType[] ambiguityIndices: number[] alternation: Alternation }): string { const pathMsg = map(options.prefixPath, (currTok) => tokenLabel(currTok) ).join(", ") const occurrence = options.alternation.idx === 0 ? "" : options.alternation.idx const errMsg = `Ambiguous alternatives: <${options.ambiguityIndices.join( " ," )}> due to common lookahead prefix\n` + `in <OR${occurrence}> inside <${options.topLevelRule.name}> Rule,\n` + `<${pathMsg}> may appears as a prefix path in all these alternatives.\n` + `See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#COMMON_PREFIX\n` + `For Further details.` return errMsg }, buildAlternationAmbiguityError(options: { topLevelRule: Rule prefixPath: TokenType[] ambiguityIndices: number[] alternation: Alternation }): string { let pathMsg = map(options.prefixPath, (currtok) => tokenLabel(currtok) ).join(", ") let occurrence = options.alternation.idx === 0 ? "" : options.alternation.idx let currMessage = `Ambiguous Alternatives Detected: <${options.ambiguityIndices.join( " ," )}> in <OR${occurrence}>` + ` inside <${options.topLevelRule.name}> Rule,\n` + `<${pathMsg}> may appears as a prefix path in all these alternatives.\n` currMessage = currMessage + `See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES\n` + `For Further details.` return currMessage }, buildEmptyRepetitionError(options: { topLevelRule: Rule repetition: IProductionWithOccurrence }): string { let dslName = getProductionDslName(options.repetition) if (options.repetition.idx !== 0) { dslName += options.repetition.idx } const errMsg = `The repetition <${dslName}> within Rule <${options.topLevelRule.name}> can never consume any tokens.\n` + `This could lead to an infinite loop.` return errMsg }, // TODO: remove - `errors_public` from nyc.config.js exclude // once this method is fully removed from this file buildTokenNameError(options: { tokenType: TokenType expectedPattern: RegExp }): string { /* istanbul ignore next */ return "deprecated" }, buildEmptyAlternationError(options: { topLevelRule: Rule alternation: Alternation emptyChoiceIdx: number }): string { const errMsg = `Ambiguous empty alternative: <${options.emptyChoiceIdx + 1}>` + ` in <OR${options.alternation.idx}> inside <${options.topLevelRule.name}> Rule.\n` + `Only the last alternative may be an empty alternative.` return errMsg }, buildTooManyAlternativesError(options: { topLevelRule: Rule alternation: Alternation }): string { const errMsg = `An Alternation cannot have more than 256 alternatives:\n` + `<OR${options.alternation.idx}> inside <${ options.topLevelRule.name }> Rule.\n has ${options.alternation.definition.length + 1} alternatives.` return errMsg }, buildLeftRecursionError(options: { topLevelRule: Rule leftRecursionPath: Rule[] }): string { const ruleName = options.topLevelRule.name let pathNames = utils.map( options.leftRecursionPath, (currRule) => currRule.name ) let leftRecursivePath = `${ruleName} --> ${pathNames .concat([ruleName]) .join(" --> ")}` let errMsg = `Left Recursion found in grammar.\n` + `rule: <${ruleName}> can be invoked from itself (directly or indirectly)\n` + `without consuming any Tokens. The grammar path that causes this is: \n ${leftRecursivePath}\n` + ` To fix this refactor your grammar to remove the left recursion.\n` + `see: https://en.wikipedia.org/wiki/LL_parser#Left_Factoring.` return errMsg }, // TODO: remove - `errors_public` from nyc.config.js exclude // once this method is fully removed from this file buildInvalidRuleNameError(options: { topLevelRule: Rule expectedPattern: RegExp }): string { /* istanbul ignore next */ return "deprecated" }, buildDuplicateRuleNameError(options: { topLevelRule: Rule | string grammarName: string }): string { let ruleName if (options.topLevelRule instanceof Rule) { ruleName = options.topLevelRule.name } else { ruleName = options.topLevelRule } const errMsg = `Duplicate definition, rule: ->${ruleName}<- is already defined in the grammar: ->${options.grammarName}<-` return errMsg } }