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. --- data/skeletons/yacc.c | 99 +++++++++++++++++++++++++++--------------- examples/c/calc/calc.y | 32 +++++++++++--- 2 files changed, 90 insertions(+), 41 deletions(-) diff --git a/data/skeletons/yacc.c b/data/skeletons/yacc.c index 022b01de..5c7a938d 100644 --- a/data/skeletons/yacc.c +++ b/data/skeletons/yacc.c @@ -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; } diff --git a/examples/c/calc/calc.y b/examples/c/calc/calc.y index 0ba74da6..20f000a2 100644 --- a/examples/c/calc/calc.y +++ b/examples/c/calc/calc.y @@ -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 (); } -- 2.24.1