d: create the Parser.Context class

This will provide the user an interface for creating custom error messages.

* data/skeletons/lalr1.d: Add the Context class.
* doc/bison.texi: Document it.
This commit is contained in:
Adela Vais
2020-10-25 16:19:55 +02:00
committed by Akim Demaille
parent 98691fcd2d
commit 0cd16ae964
2 changed files with 102 additions and 31 deletions

View File

@@ -452,7 +452,7 @@ m4_popdef([b4_at_dollar])])dnl
/* Take a decision. First try without lookahead. */ /* Take a decision. First try without lookahead. */
yyn = yypact_[yystate]; yyn = yypact_[yystate];
if (yy_pact_value_is_default_ (yyn)) if (yyPactValueIsDefault(yyn))
{ {
label = YYDEFAULT; label = YYDEFAULT;
break; break;
@@ -497,7 +497,7 @@ m4_popdef([b4_at_dollar])])dnl
/* <= 0 means reduce or error. */ /* <= 0 means reduce or error. */
else if ((yyn = yytable_[yyn]) <= 0) else if ((yyn = yytable_[yyn]) <= 0)
{ {
if (yy_table_value_is_error_ (yyn)) if (yyTableValueIsError(yyn))
label = YYERRLAB; label = YYERRLAB;
else else
{ {
@@ -555,7 +555,7 @@ m4_popdef([b4_at_dollar])])dnl
++yynerrs_; ++yynerrs_;
if (yychar == TokenKind.]b4_symbol(empty, id)[) if (yychar == TokenKind.]b4_symbol(empty, id)[)
yytoken = ]b4_symbol(empty, kind)[; 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([ ]b4_locations_if([
yyerrloc = yylloc;])[ yyerrloc = yylloc;])[
@@ -602,7 +602,7 @@ m4_popdef([b4_at_dollar])])dnl
for (;;) for (;;)
{ {
yyn = yypact_[yystate]; yyn = yypact_[yystate];
if (!yy_pact_value_is_default_ (yyn)) if (!yyPactValueIsDefault(yyn))
{ {
yyn += ]b4_symbol(1, kind)[; yyn += ]b4_symbol(1, kind)[;
if (0 <= yyn && yyn <= yylast_ && yycheck_[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. // 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], [[ {]b4_parse_error_case([verbose], [[
/* There are many possibilities here to consider: /* There are many possibilities here to consider:
- Assume YYFAIL is not used. It's too flawed 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 will still contain any token that will not be accepted due
to an error action in a later state. 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 // FIXME: This method of building the message is not compatible
// with internationalization. // with internationalization.
string res = "syntax error, unexpected "; string res = "syntax error, unexpected ";
res ~= format!"%s"(tok); res ~= format!"%s"(yyctx.getToken);
int yyn = yypact_[yystate]; immutable int argmax = 5;
if (!yy_pact_value_is_default_ (yyn)) 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 /* Start YYX at -YYN if negative to avoid negative
indexes in YYCHECK. In other words, skip the first 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. */ /* Stay within bounds of both yycheck and yytname. */
int yychecklim = yylast_ - yyn + 1; int yychecklim = yylast_ - yyn + 1;
int yyxend = yychecklim < yyntokens_ ? yychecklim : yyntokens_; int yyxend = yychecklim < yyntokens_ ? yychecklim : yyntokens_;
int count = 0; 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) for (int x = yyxbegin; x < yyxend; ++x)
if (yycheck_[x + yyn] == x && x != ]b4_symbol(1, kind)[ if (yycheck_[x + yyn] == x && x != ]b4_symbol(1, kind)[
&& !yy_table_value_is_error_ (yytable_[x + yyn])) && !yyTableValueIsError(yytable_[x + yyn]))
++count; yyarg[yycount++] = SymbolKind(x);
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));
} }
} }
return yycount - yyoffset;
} }
return res;
}]])[
return "syntax error";
} }
/** /**
* Whether the given <code>yypact_</code> value indicates a defaulted state. * Whether the given <code>yypact_</code> value indicates a defaulted state.
* @@param yyvalue the value to check * @@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_; return yyvalue == yypact_ninf_;
} }
@@ -744,7 +796,7 @@ m4_popdef([b4_at_dollar])])dnl
* Whether the given <code>yytable_</code> value indicates a syntax error. * Whether the given <code>yytable_</code> value indicates a syntax error.
* @@param yyvalue the value to check * @@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_; return yyvalue == yytable_ninf_;
} }

View File

@@ -13948,10 +13948,29 @@ enumerators are forged from the symbol names. Use void toString(W)(W sink) to g
the symbol names. the symbol names.
@end defcv @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. The location of the lookahead.
@end deftypemethod @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 @node D Scanner Interface
@subsection D Scanner Interface @subsection D Scanner Interface
@c - %code lexer @c - %code lexer