From f5714da755e2a36e29cbeabfb79bbb7b12963414 Mon Sep 17 00:00:00 2001 From: Shahar Soel Date: Sat, 5 Sep 2015 23:31:55 +0300 Subject: [PATCH] The error message description argument for the 'OR' parsing DSL is now optional. By default the error message will contain the names of the expected first Tokens in each alternative. fixes #24 --- src/parse/parser_public.ts | 52 ++++++++++++------- .../sql_statements/sql_recovery_parser.ts | 2 +- test/parse/recognizer_lookahead_spec.ts | 2 +- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/parse/parser_public.ts b/src/parse/parser_public.ts index 0873c14ec..f4828fa17 100644 --- a/src/parse/parser_public.ts +++ b/src/parse/parser_public.ts @@ -531,7 +531,7 @@ namespace chevrotain { * Convenience method equivalent to OR1 * @see OR1 */ - protected OR(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes:string, ignoreAmbiguities:boolean = false):T { + protected OR(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes?:string, ignoreAmbiguities:boolean = false):T { return this.OR1(alts, errMsgTypes, ignoreAmbiguities) } @@ -560,43 +560,49 @@ namespace chevrotain { * As in CONSUME the index in the method name indicates the occurrence * of the alternation production in it's top rule. * - * @param {{ALT:Function}[] | {WHEN:Function, THEN_DO:Function}[]} alts An array of alternatives - * @param {string} errMsgTypes A description for the alternatives used in error messages + * @param {{ALT:Function}[] | {WHEN:Function, THEN_DO:Function}[]} alts - An array of alternatives + * + * @param {string} [errMsgTypes] - A description for the alternatives used in error messages + * If none is provided, the error message will include the names of the expected + * Tokens which may start each alternative. + * + * @param {boolean} [ignoreAmbiguities] - if true this will ignore ambiguities caused when two alternatives can not + * be distinguished by a lookahead of one. enabling this means the first alternative + * that matches will be taken. This is sometimes the grammar's intent. + * * only enable this if you know what you are doing! + * * @returns {*} The result of invoking the chosen alternative - * @param {boolean} [ignoreAmbiguities] if true this will ignore ambiguities caused when two alternatives can not - * be distinguished by a lookahead of one. enabling this means the first alternative - * that matches will be taken. This is sometimes the grammar's intent. - * * only enable this if you know what you are doing! + */ - protected OR1(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes:string, ignoreAmbiguities:boolean = false):T { + protected OR1(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes?:string, ignoreAmbiguities:boolean = false):T { return this.orInternal(alts, errMsgTypes, 1, ignoreAmbiguities) } /** * @see OR1 */ - protected OR2(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes:string, ignoreAmbiguities:boolean = false):T { + protected OR2(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes?:string, ignoreAmbiguities:boolean = false):T { return this.orInternal(alts, errMsgTypes, 2, ignoreAmbiguities) } /** * @see OR1 */ - protected OR3(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes:string, ignoreAmbiguities:boolean = false):T { + protected OR3(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes?:string, ignoreAmbiguities:boolean = false):T { return this.orInternal(alts, errMsgTypes, 3, ignoreAmbiguities) } /** * @see OR1 */ - protected OR4(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes:string, ignoreAmbiguities:boolean = false):T { + protected OR4(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes?:string, ignoreAmbiguities:boolean = false):T { return this.orInternal(alts, errMsgTypes, 4, ignoreAmbiguities) } /** * @see OR1 */ - protected OR5(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes:string, ignoreAmbiguities:boolean = false):T { + protected OR5(alts:IOrAlt[] | IOrAltImplicit[], errMsgTypes?:string, ignoreAmbiguities:boolean = false):T { return this.orInternal(alts, errMsgTypes, 5, ignoreAmbiguities) } @@ -943,8 +949,8 @@ namespace chevrotain { let reSyncEnabled = isFirstInvokedRule || ( doReSync && !this.isBackTracking() - // if errorRecovery is disabled, the exception will be rethrown to the top rule - // (isFirstInvokedRule) and there will resync to EOF and terminate. + // if errorRecovery is disabled, the exception will be rethrown to the top rule + // (isFirstInvokedRule) and there will resync to EOF and terminate. && this.isErrorRecoveryEnabled) if (reSyncEnabled && exceptions.isRecognitionException(e)) { @@ -1450,7 +1456,7 @@ namespace chevrotain { return res } } - this.raiseNoAltException(errMsgTypes) + this.raiseNoAltException(occurrence, errMsgTypes) } // else implicit lookahead @@ -1460,7 +1466,7 @@ namespace chevrotain { return (alts[altToTake]).ALT.call(this) } - this.raiseNoAltException(errMsgTypes) + this.raiseNoAltException(occurrence, errMsgTypes) } /** @@ -1599,9 +1605,17 @@ namespace chevrotain { this.RULE_STACK = newState.RULE_STACK } - private raiseNoAltException(errMsgTypes:string):void { - throw this.SAVE_ERROR(new exceptions.NoViableAltException("expecting: " + errMsgTypes + - " but found '" + this.NEXT_TOKEN().image + "'", this.NEXT_TOKEN())) + private raiseNoAltException(occurrence:number, errMsgTypes:string):void { + let errSuffix = " but found '" + this.NEXT_TOKEN().image + "'" + if (errMsgTypes === undefined) { + let ruleName = _.last(this.RULE_STACK) + let ruleGrammar = this.getGAstProductions().get(ruleName) + let nextTokens = new interp.NextInsideOrWalker(ruleGrammar, occurrence).startWalking() + let nextTokensFlat = _.flatten(nextTokens) + let nextTokensNames = _.map(nextTokensFlat, (currTokenClass:Function) => tokenName(currTokenClass)) + errMsgTypes = `one of: <${nextTokensNames.join(" ,")}}>` + } + throw this.SAVE_ERROR(new exceptions.NoViableAltException(`expecting: ${errMsgTypes} ${errSuffix}`, this.NEXT_TOKEN())) } } diff --git a/test/full_flow/error_recovery/sql_statements/sql_recovery_parser.ts b/test/full_flow/error_recovery/sql_statements/sql_recovery_parser.ts index 46a0c5bff..2e31e60f0 100644 --- a/test/full_flow/error_recovery/sql_statements/sql_recovery_parser.ts +++ b/test/full_flow/error_recovery/sql_statements/sql_recovery_parser.ts @@ -147,7 +147,7 @@ namespace chevrotain.examples.recovery.sql { [ // @formatter:off {ALT: () => {value = this.CONSUME1(StringTok)}}, {ALT: () => {value = this.CONSUME1(IntTok)}} - ], "a String or an Integer") + ]) // @formatter:on return PT(value) } diff --git a/test/parse/recognizer_lookahead_spec.ts b/test/parse/recognizer_lookahead_spec.ts index 622b8e136..587287a87 100644 --- a/test/parse/recognizer_lookahead_spec.ts +++ b/test/parse/recognizer_lookahead_spec.ts @@ -935,7 +935,7 @@ namespace chevrotain.recognizer.lookahead.spec { this.CONSUME1(FiveTok) total += "A5" }}, - ], "digits") + ]) this.OR2([ {ALT: () => {