diff --git a/data/skeletons/lalr1.d b/data/skeletons/lalr1.d index 9e0095f3..9b7c786c 100644 --- a/data/skeletons/lalr1.d +++ b/data/skeletons/lalr1.d @@ -452,7 +452,7 @@ m4_popdef([b4_at_dollar])])dnl /* Take a decision. First try without lookahead. */ yyn = yypact_[yystate]; - if (yy_pact_value_is_default_ (yyn)) + if (yyPactValueIsDefault(yyn)) { label = YYDEFAULT; break; @@ -497,7 +497,7 @@ m4_popdef([b4_at_dollar])])dnl /* <= 0 means reduce or error. */ else if ((yyn = yytable_[yyn]) <= 0) { - if (yy_table_value_is_error_ (yyn)) + if (yyTableValueIsError(yyn)) label = YYERRLAB; else { @@ -555,7 +555,7 @@ m4_popdef([b4_at_dollar])])dnl ++yynerrs_; if (yychar == TokenKind.]b4_symbol(empty, id)[) yytoken = ]b4_symbol(empty, kind)[; - yyerror (]b4_locations_if([yylloc, ])[yysyntax_error (yystate, yytoken)); + yyerror (]b4_locations_if([yylloc, ])[yysyntax_error(new Context(yystack, yytoken]b4_locations_if([[, yylloc]])[))); } ]b4_locations_if([ yyerrloc = yylloc;])[ @@ -602,7 +602,7 @@ m4_popdef([b4_at_dollar])])dnl for (;;) { yyn = yypact_[yystate]; - if (!yy_pact_value_is_default_ (yyn)) + if (!yyPactValueIsDefault(yyn)) { yyn += ]b4_symbol(1, kind)[; if (0 <= yyn && yyn <= yylast_ && yycheck_[yyn] == ]b4_symbol(1, kind)[) @@ -659,7 +659,7 @@ m4_popdef([b4_at_dollar])])dnl } // Generate an error message. - private final string yysyntax_error (int yystate, SymbolKind tok) + private final string yysyntax_error(Context yyctx) {]b4_parse_error_case([verbose], [[ /* There are many possibilities here to consider: - Assume YYFAIL is not used. It's too flawed to consider. @@ -692,14 +692,71 @@ m4_popdef([b4_at_dollar])])dnl will still contain any token that will not be accepted due to an error action in a later state. */ - if (tok != ]b4_symbol(empty, kind)[) + if (yyctx.getToken() != ]b4_symbol(empty, kind)[) { // FIXME: This method of building the message is not compatible // with internationalization. string res = "syntax error, unexpected "; - res ~= format!"%s"(tok); - int yyn = yypact_[yystate]; - if (!yy_pact_value_is_default_ (yyn)) + res ~= format!"%s"(yyctx.getToken); + immutable int argmax = 5; + SymbolKind[] yyarg = new SymbolKind[argmax]; + int yycount = yyctx.getExpectedTokens(yyarg, argmax); + if (yycount < argmax) + { + for (int yyi = 0; yyi < yycount; yyi++) + { + res ~= yyi == 0 ? ", expecting " : " or "; + res ~= format!"%s"(SymbolKind(yyarg[yyi])); + } + } + return res; + }]])[ + return "syntax error"; + } + + /** + * Information needed to get the list of expected tokens and to forge + * a syntax error diagnostic. + */ + public static final class Context + { + + private YYStack yystack; + private SymbolKind yytoken;]b4_locations_if([[ + private ]b4_location_type[ yylocation;]])[ + + this(YYStack stack, SymbolKind kind]b4_locations_if([[, ]b4_location_type[ loc]])[) + { + yystack = stack; + yytoken = kind;]b4_locations_if([[ + yylocation = loc;]])[ + } + + final SymbolKind getToken() const + { + return yytoken; + }]b4_locations_if([[ + + final ]b4_location_type[ getLocation() + { + return yylocation; + }]])[ + /** + * Put in YYARG at most YYARGN of the expected tokens given the + * current YYCTX, and return the number of tokens stored in YYARG. If + * YYARG is null, return the number of expected tokens (guaranteed to + * be less than YYNTOKENS). + */ + int getExpectedTokens(SymbolKind[] yyarg, int yyargn) + { + return getExpectedTokens(yyarg, 0, yyargn); + } + + int getExpectedTokens(SymbolKind[] yyarg, int yyoffset, int yyargn) + { + int yycount = yyoffset; + int yyn = yypact_[this.yystack.stateAt(0)]; + if (!yyPactValueIsDefault(yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first @@ -709,33 +766,28 @@ m4_popdef([b4_at_dollar])])dnl /* Stay within bounds of both yycheck and yytname. */ int yychecklim = yylast_ - yyn + 1; int yyxend = yychecklim < yyntokens_ ? yychecklim : yyntokens_; - int count = 0; - for (int x = yyxbegin; x < yyxend; ++x) - if (yycheck_[x + yyn] == x && x != ]b4_symbol(1, kind)[ - && !yy_table_value_is_error_ (yytable_[x + yyn])) - ++count; - if (count < 5) - { - count = 0; - for (int x = yyxbegin; x < yyxend; ++x) - if (yycheck_[x + yyn] == x && x != ]b4_symbol(1, kind)[ - && !yy_table_value_is_error_ (yytable_[x + yyn])) - { - res ~= count++ == 0 ? ", expecting " : " or "; - res ~= format!"%s"(SymbolKind(x)); - } - } + for (int yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck_[yyx + yyn] == yyx && yyx != ]b4_symbol(1, kind)[ + && !yyTableValueIsError(yytable_[yyx + yyn])) + yycount++; + if (yycount < yyargn) + { + yycount = 0; + for (int x = yyxbegin; x < yyxend; ++x) + if (yycheck_[x + yyn] == x && x != ]b4_symbol(1, kind)[ + && !yyTableValueIsError(yytable_[x + yyn])) + yyarg[yycount++] = SymbolKind(x); + } } - return res; - }]])[ - return "syntax error"; + return yycount - yyoffset; + } } /** * Whether the given yypact_ value indicates a defaulted state. * @@param yyvalue the value to check */ - private static bool yy_pact_value_is_default_ (int yyvalue) + private static bool yyPactValueIsDefault(int yyvalue) { return yyvalue == yypact_ninf_; } @@ -744,7 +796,7 @@ m4_popdef([b4_at_dollar])])dnl * Whether the given yytable_ value indicates a syntax error. * @@param yyvalue the value to check */ - private static bool yy_table_value_is_error_ (int yyvalue) + private static bool yyTableValueIsError(int yyvalue) { return yyvalue == yytable_ninf_; } diff --git a/doc/bison.texi b/doc/bison.texi index 67494d35..afecf7d4 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -13948,10 +13948,29 @@ enumerators are forged from the symbol names. Use void toString(W)(W sink) to g the symbol names. @end defcv -@deftypemethod {YYParser.Context} {YYParser.Location} yylloc_from_stack() +@deftypemethod {YYParser.Context} {YYParser.SymbolKind} getToken() +The kind of the lookahead. Return @code{null} iff there is no lookahead. +@end deftypemethod + +@deftypemethod {YYParser.Context} {YYParser.Location} getLocation() The location of the lookahead. @end deftypemethod +@deftypemethod {YYParser.Context} {int} getExpectedTokens(@code{YYParser.SymbolKind[]} @var{argv}, @code{int} @var{argc}) +Fill @var{argv} with the expected tokens, which never includes +@code{SymbolKind.YYERROR}, or @code{SymbolKind.YYUNDEF}. + +Never put more than @var{argc} elements into @var{argv}, and on success +return the number of tokens stored in @var{argv}. If there are more +expected tokens than @var{argc}, fill @var{argv} up to @var{argc} and return +0. If there are no expected tokens, also return 0, but set @code{argv[0]} +to @code{null}. + +If @var{argv} is null, return the size needed to store all the possible +values, which is always less than @code{YYNTOKENS}. +@end deftypemethod + + @node D Scanner Interface @subsection D Scanner Interface @c - %code lexer