yacc.c: isolate yyexpected_tokens

Provide users with a means to query for the currently allowed tokens.
Could be used for autocompletion for instance.

* data/skeletons/yacc.c (yyexpected_tokens): New, extracted from
yysyntax_error_arguments.
* examples/c/calc/calc.y (PRINT_EXPECTED_TOKENS): New.
Use it.
This commit is contained in:
Akim Demaille
2020-01-06 20:04:58 +01:00
parent ee97f37cb4
commit 8637438cee
2 changed files with 92 additions and 43 deletions

View File

@@ -895,7 +895,7 @@ do { \
{ \
YYDPRINTF ((stderr, \
"LAC: initial context established for %s\n", \
yysymbol_name(yytoken))); \
yysymbol_name (yytoken))); \
yy_lac_established = 1; \
{ \
int yy_lac_status = \
@@ -948,7 +948,7 @@ yy_lac (yy_state_t *yyesa, yy_state_t **yyes,
yy_state_t *yyes_prev = yyssp;
yy_state_t *yyesp = yyes_prev;
/* Reduce until we encounter a shift and thereby accept the token. */
YYDPRINTF ((stderr, "LAC: checking lookahead %s:", yysymbol_name(yytoken)));
YYDPRINTF ((stderr, "LAC: checking lookahead %s:", yysymbol_name (yytoken)));
if (yytoken == YYUNDEFTOK)
{
YYDPRINTF ((stderr, " Always Err\n"));
@@ -1056,6 +1056,62 @@ yy_lac (yy_state_t *yyesa, yy_state_t **yyes,
YYPTRDIFF_T *yyes_capacity_p;]])[
} yyparse_context_t;
/* 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. */
static int
yyexpected_tokens (const yyparse_context_t *yyctx,
int yyarg[], int yyargn)
{
/* Actual size of YYARG. */
int yycount = 0;
]b4_lac_if([[
int yyx;
for (yyx = 0; yyx < YYNTOKENS; ++yyx)
if (yyx != YYTERROR && yyx != YYUNDEFTOK)
{
{
int yy_lac_status = yy_lac (yyctx->yyesa, yyctx->yyes_p, yyctx->yyes_capacity_p,
yyctx->yyssp, yyx);
if (yy_lac_status == 2)
return -2;
if (yy_lac_status == 1)
continue;
}
if (!yyarg)
++yycount;
else if (yycount == yyargn)
return 0;
else
yyarg[yycount++] = yyx;
}]],
[[ int yyn = yypact[+*yyctx->yyssp];
if (!yypact_value_is_default (yyn))
{
/* Start YYX at -YYN if negative to avoid negative indexes in
YYCHECK. In other words, skip the first -YYN actions for
this state because they are default actions. */
int yyxbegin = yyn < 0 ? -yyn : 0;
/* Stay within bounds of both yycheck and yytname. */
int yychecklim = YYLAST - yyn + 1;
int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
int yyx;
for (yyx = yyxbegin; yyx < yyxend; ++yyx)
if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
&& !yytable_value_is_error (yytable[yyx + yyn]))
{
if (!yyarg)
++yycount;
else if (yycount == yyargn)
return 0;
else
yyarg[yycount++] = yyx;
}
}]])[
return yycount;
}
static int
yysyntax_error_arguments (const yyparse_context_t *yyctx,
int yyarg[], int yyargn)
@@ -1092,45 +1148,16 @@ yysyntax_error_arguments (const yyparse_context_t *yyctx,
*/
if (yyctx->yytoken != YYEMPTY)
{
int yyn = yypact[+*yyctx->yyssp];]b4_lac_if([[
int yyn;]b4_lac_if([[
YYDPRINTF ((stderr, "Constructing syntax error message\n"));]])[
yyarg[yycount++] = yyctx->yytoken;
if (!yypact_value_is_default (yyn))
{]b4_lac_if([[
int yyx;
for (yyx = 0; yyx < YYNTOKENS; ++yyx)
if (yyx != YYTERROR && yyx != YYUNDEFTOK)
{
{
int yy_lac_status = yy_lac (yyctx->yyesa, yyctx->yyes_p, yyctx->yyes_capacity_p,
yyctx->yyssp, yyx);
if (yy_lac_status == 2)
return -2;
if (yy_lac_status == 1)
continue;
}]], [[
/* Start YYX at -YYN if negative to avoid negative indexes in
YYCHECK. In other words, skip the first -YYN actions for
this state because they are default actions. */
int yyxbegin = yyn < 0 ? -yyn : 0;
/* Stay within bounds of both yycheck and yytname. */
int yychecklim = YYLAST - yyn + 1;
int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
int yyx;
for (yyx = yyxbegin; yyx < yyxend; ++yyx)
if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
&& !yytable_value_is_error (yytable[yyx + yyn]))
{]])[
if (yycount == yyargn)
{
yycount = 1;
break;
}
yyarg[yycount++] = yyx;
}
}]b4_lac_if([[
else
yyn = yyexpected_tokens (yyctx, yyarg ? yyarg + 1 : yyarg, yyargn - 1);
if (yyn == -2)
return -2;]b4_lac_if([[
else if (yyn == 0)
YYDPRINTF ((stderr, "No expected tokens.\n"));]])[
else
yycount += yyn;
}
return yycount;
}

View File

@@ -1,15 +1,35 @@
%code top {
#include <ctype.h> /* isdigit. */
#include <stdbool.h>
#include <stdio.h> /* For printf, etc. */
#include <string.h> /* strcmp. */
int yylex (void);
void yyerror (char const *);
bool show_expected = false;
#define PRINT_EXPECTED_TOKENS() \
do { \
if (show_expected) \
{ \
yyparse_context_t ctx \
= {yyssp, yytoken, yyesa, &yyes, &yyes_capacity}; \
int tokens[YYNTOKENS]; \
int cnt = yyexpected_tokens (&ctx, tokens, YYNTOKENS); \
fprintf (stderr, "expected tokens in state %d rule %d (%d):", \
*yyssp, yyn - 1, cnt); \
for (int i = 0; i < cnt; ++i) \
fprintf (stderr, " %s", yysymbol_name(tokens[i])); \
fprintf (stderr, "\n"); \
} \
} while (0)
}
%define api.header.include {"calc.h"}
%define api.value.type union /* Generate YYSTYPE from these types: */
%define parse.error custom
%define parse.lac full
%token <double> NUM "number"
%type <double> expr term fact
@@ -23,8 +43,8 @@
%% /* The grammar follows. */
input:
%empty
| input line
%empty { PRINT_EXPECTED_TOKENS (); }
| input line { PRINT_EXPECTED_TOKENS (); }
;
line:
@@ -46,8 +66,8 @@ term:
;
fact:
"number"
| '(' expr ')' { $$ = $2; }
"number" { PRINT_EXPECTED_TOKENS (); }
| '(' expr { PRINT_EXPECTED_TOKENS (); } ')' { $$ = $expr; }
;
%%
@@ -110,7 +130,9 @@ main (int argc, char const* argv[])
{
/* Enable parse traces on option -p. */
for (int i = 1; i < argc; ++i)
if (!strcmp (argv[i], "-p"))
if (!strcmp (argv[i], "-e"))
show_expected = 1;
else if (!strcmp (argv[i], "-p"))
yydebug = 1;
return yyparse ();
}