# Executing Actions. -*- Autotest -*- # Copyright (C) 2001-2015, 2018-2022 Free Software Foundation, Inc. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . AT_BANNER([[User Actions.]]) ## ----------------- ## ## Midrule actions. ## ## ----------------- ## AT_SETUP([Midrule actions]) # Bison once forgot the midrule actions. It was because the action # was attached to the host rule (the one with the midrule action), # instead of being attached to the empty rule dedicated to this # action. AT_BISON_OPTION_PUSHDEFS AT_DATA_GRAMMAR([[input.y]], [[%define parse.error verbose %debug %code { ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %% exp: { putchar ('0'); } '1' { putchar ('1'); } '2' { putchar ('2'); } '3' { putchar ('3'); } '4' { putchar ('4'); } '5' { putchar ('5'); } '6' { putchar ('6'); } '7' { putchar ('7'); } '8' { putchar ('8'); } '9' { putchar ('9'); } { putchar ('\n'); } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE(["123456789"])[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_OPTION_POPDEFS AT_BISON_CHECK([-d -v -o input.c input.y]) AT_COMPILE([input]) AT_PARSER_CHECK([input], 0, [[0123456789 ]]) AT_CLEANUP ## ----------------------- ## ## Typed midrule actions. ## ## ----------------------- ## AT_SETUP([Typed midrule actions]) AT_BISON_OPTION_PUSHDEFS AT_DATA_GRAMMAR([[input.y]], [[%define parse.error verbose %debug %code { ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %union { int ival; } %type exp %% exp: { $$ = 0; } '1' { $$ = 1; } '2' { $$ = 2; } '3' { $$ = 3; } '4' { $$ = 4; } '5' { $$ = 5; } '6' { $$ = 6; } '7' { $$ = 7; } '8' { $$ = 8; } '9' { $$ = 9; } { $$ = 10; } { $$ = 11; } { $$ = $1 + $3 + $5 + $7 + $9 + $11 + $13 + $15 + $17 + $19 + $20 + $21; printf ("%d\n", $$); } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE(["123456789"])[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_OPTION_POPDEFS AT_BISON_CHECK([-d -v -o input.c input.y]) AT_COMPILE([input]) AT_PARSER_CHECK([input], 0, [[66 ]]) AT_CLEANUP ## ----------------------- ## ## Implicitly empty rule. ## ## ----------------------- ## AT_SETUP([Implicitly empty rule]) AT_BISON_OPTION_PUSHDEFS AT_DATA_GRAMMAR([[1.y]], [[%% exp: a b; a: /* empty. */ {}; // A midrule action does not count as an empty rule. b: {} {}; ]]) AT_BISON_CHECK([-fcaret -Wempty-rule 1.y], [0], [], [[1.y:11.17-18: warning: empty rule without %empty [-Wempty-rule] 11 | a: /* empty. */ {}; | ^~ | %empty 1.y: warning: fix-its can be applied. Rerun with option '--update'. [-Wother] ]]) AT_DATA_GRAMMAR([[2.y]], [[%% exp: a b c; a: /* empty. */ {}; b: %empty {}; c: /* empty. */ {}; ]]) AT_BISON_CHECK([-fcaret 2.y], [0], [], [[2.y:11.17-18: warning: empty rule without %empty [-Wempty-rule] 11 | a: /* empty. */ {}; | ^~ | %empty 2.y:13.17-18: warning: empty rule without %empty [-Wempty-rule] 13 | c: /* empty. */ {}; | ^~ | %empty 2.y: warning: fix-its can be applied. Rerun with option '--update'. [-Wother] ]]) AT_BISON_CHECK([-fcaret -Wno-empty-rule 2.y], [0]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ## ------------------------ ## ## Invalid uses of %empty. ## ## ------------------------ ## AT_SETUP([Invalid uses of %empty]) AT_BISON_OPTION_PUSHDEFS AT_DATA_GRAMMAR([[one.y]], [[%% exp: %empty {} %empty ; ]]) AT_BISON_CHECK([-fcaret one.y], [1], [], [[one.y:11.13-18: error: only one %empty allowed per rule 11 | %empty {} %empty | ^~~~~~ one.y:11.3-8: note: previous declaration 11 | %empty {} %empty | ^~~~~~ one.y: warning: fix-its can be applied. Rerun with option '--update'. [-Wother] ]]) AT_BISON_CHECK([-fcaret -u one.y], [1], [], [[one.y:11.13-18: error: only one %empty allowed per rule 11 | %empty {} %empty | ^~~~~~ one.y:11.3-8: note: previous declaration 11 | %empty {} %empty | ^~~~~~ bison: file 'one.y' was updated (backup: 'one.y~') ]]) AT_CHECK([sed -e '1,8d' one.y], [], [[%% exp: %empty {} @&t@ ; ]]) AT_DATA_GRAMMAR([[two.y]], [[%% exp: 'a' %empty {} | %empty 'a' {} | %empty {} {} ; ]]) AT_BISON_CHECK([-fcaret two.y], [1], [], [[two.y:11.7-12: error: %empty on non-empty rule 11 | 'a' %empty {} | ^~~~~~ two.y:12.3-8: error: %empty on non-empty rule 12 | | %empty 'a' {} | ^~~~~~ two.y:13.3-8: error: %empty on non-empty rule 13 | | %empty {} {} | ^~~~~~ two.y: warning: fix-its can be applied. Rerun with option '--update'. [-Wother] ]]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ## ---------------------- ## ## Valid uses of %empty. ## ## ---------------------- ## AT_SETUP([Valid uses of %empty]) AT_BISON_OPTION_PUSHDEFS AT_DATA_GRAMMAR([[input.y]], [[ %debug %code { ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %% exp: %empty {} %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE[ ]AT_MAIN_DEFINE[ ]]) AT_FULL_COMPILE([input]) AT_PARSER_CHECK([input]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ## -------------------- ## ## Add missing %empty. ## ## -------------------- ## AT_SETUP([Add missing %empty]) AT_DATA([input.y], [[%% exp: a b c d e a: {} b:{ }; c: d : e: %% ]]) AT_BISON_CHECK([--update -Wall input.y], [], [], [ignore]) AT_CHECK([cat input.y], [], [[%% exp: a b c d e a: %empty {} b: %empty { }; c: %empty @&t@ d : %empty @&t@ e: %empty @&t@ %% ]]) # No warnings. AT_BISON_CHECK([-Wall input.y]) AT_CLEANUP ## ------------------ ## ## Initial location. ## ## ------------------ ## # AT_TEST(SKELETON-NAME, DIRECTIVES, [MORE-DIRECTIVES], [LOCATION = 1.1]) # ----------------------------------------------------------------------- # Check that the initial location is correct. m4_pushdef([AT_TEST], [AT_SETUP([Initial location: $1 $2]) AT_BISON_OPTION_PUSHDEFS([%locations %skeleton "$1" $2]) AT_DATA_GRAMMAR([[input.y]], [[%locations %debug %skeleton "$1" ]$2[ ]$3[ %code { # include # include /* getenv */ ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %% exp: { ]AT_CXX_IF([[std::cerr << @$ << '\n']], [[LOCATION_PRINT(stderr, @$); fputc ('\n', stderr)]])[; } %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_PROTOTYPE[ {]AT_PURE_IF([ YY_USE (lvalp); YY_USE (llocp);], [AT_CXX_IF([ YY_USE (lvalp); YY_USE (llocp);])])[ return 'x'; } int main (void) {]AT_CXX_IF([[ yy::parser p; p.set_debug_level (!!getenv ("YYDEBUG")); return p.parse ();]], [[ yydebug = !!getenv ("YYDEBUG"); return !!yyparse (]AT_PARAM_IF([0])[);]])[ } ]]) AT_FULL_COMPILE([input]) AT_PARSER_CHECK([input], 1, [], [m4_default([$4], [1.1]) m4_default([$4], [1.1])[: syntax error ]]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ]) ## FIXME: test Java, and iterate over skeletons. AT_TEST([yacc.c]) AT_TEST([yacc.c], [%define api.pure full]) AT_TEST([yacc.c], [%define api.pure %parse-param { int x }]) AT_TEST([yacc.c], [%define api.push-pull both]) AT_TEST([yacc.c], [%define api.push-pull both %define api.pure full]) AT_TEST([glr.c]) AT_TEST([glr.c], [%define api.pure]) AT_TEST([lalr1.cc]) AT_TEST([glr.cc]) AT_TEST([glr2.cc]) ## A very different test, based on PostgreSQL's implementation of the ## locations. See ## https://lists.gnu.org/r/bug-bison/2012-11/msg00023.html ## ## Weirdly enough, to trigger the warning with GCC 4.7, we must not ## use fprintf, so run the test twice: once to check the warning ## (absence thereof), and another time to check the value. AT_TEST([yacc.c], [%define api.pure full], [[%{ # define YYLTYPE int # define LOCATION_PRINT(Stream, Loc) \ (void) (Loc) # define YYLLOC_DEFAULT(Current, Rhs, N) \ (Current) = ((Rhs)[N ? 1 : 0]) %} ]], [@&t@]) AT_TEST([yacc.c], [%define api.pure full], [[%{ # define YYLTYPE int # define LOCATION_PRINT(Stream, Loc) \ fprintf ((Stream), "%d", (Loc)) # define YYLLOC_DEFAULT(Current, Rhs, N) \ (Current) = ((Rhs)[N ? 1 : 0]) %} ]], [0]) m4_popdef([AT_TEST]) ## ---------------- ## ## Location Print. ## ## ---------------- ## # AT_TEST(SKELETON-NAME, DIRECTIVES, [MORE-DIRECTIVES]) # ----------------------------------------------------- # Check that the initial location is correct. m4_pushdef([AT_TEST], [AT_SETUP([Location print: $1 $2]) AT_BISON_OPTION_PUSHDEFS([%locations %skeleton "$1" $2]) AT_DATA_GRAMMAR([[input.y]], [[%locations %debug %skeleton "$1" ]$2[ ]$3[ %code { #include /* putchar. */ ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %% exp: %empty; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE[ int main (void) { ]AT_YYLTYPE[ loc; ]AT_GLR_CC_IF([loc.initialize();])[ #define TEST(L1, C1, L2, C2) \ loc.]AT_FIRST_LINE[ = L1; \ loc.]AT_FIRST_COLUMN[ = C1; \ loc.]AT_LAST_LINE[ = L2; \ loc.]AT_LAST_COLUMN[ = C2; \ ]AT_CXX_IF([std::cout << loc], [LOCATION_PRINT(stdout, loc)])[;\ putchar ('\n'); TEST(1, 1, 1, 1); TEST(2, 1, 2, 10); TEST(3, 1, 4, 1); TEST(5, 1, 6, 10); TEST(7, 2, 0, 2); TEST(8, 0, 8, 0); return 0; } ]]) AT_FULL_COMPILE([input]) AT_PARSER_CHECK([input], 0, [[1.1 2.1-9 3.1-4.0 5.1-6.9 7.2 8.0 ]]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ]) ## FIXME: test Java and D. m4_map_args([AT_TEST], [yacc.c], [glr.c], [lalr1.cc], [glr.cc], [glr2.cc]) m4_popdef([AT_TEST]) ## ---------------- ## ## Exotic Dollars. ## ## ---------------- ## AT_SETUP([Exotic Dollars]) AT_BISON_OPTION_PUSHDEFS AT_DATA_GRAMMAR([[input.y]], [[%define parse.error verbose %debug %code { ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ # define USE(Var) } %union { int val; }; %type a_1 a_2 a_5 sum_of_the_five_previous_values %% exp: a_1 a_2 { $$ = 3; } { $$ = $3 + 1; } a_5 sum_of_the_five_previous_values { USE (($1, $2, $3, $4, $5)); printf ("%d\n", $6); } ; a_1: { $$ = 1; }; a_2: { $$ = 2; }; a_5: { $$ = 5; }; sum_of_the_five_previous_values: { $$ = $0 + $-1 + $-2 + $-3 + $-4; } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_CHECK([-d -v -o input.c input.y], 0) AT_COMPILE([input]) AT_PARSER_CHECK([input], 0, [[15 ]]) # Make sure that fields after $n or $-n are parsed correctly. At one # point while implementing dashes in symbol names, we were dropping # fields after $-n. AT_DATA_GRAMMAR([[input.y]], [[ %{ ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ typedef struct { int val; } stype; # define YYSTYPE stype %} %% start: one two { $$.val = $1.val + $2.val; } sum ; one: { $$.val = 1; } ; two: { $$.val = 2; } ; sum: { printf ("%d\n", $0.val + $-1.val + $-2.val); } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE[ ]AT_MAIN_DEFINE[ ]]) AT_FULL_COMPILE([input]) AT_PARSER_CHECK([[input]], [[0]], [[6 ]]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ## -------------------------- ## ## Printers and Destructors. ## ## -------------------------- ## # _AT_CHECK_PRINTER_AND_DESTRUCTOR($1, $2, $3, $4, # BISON-DIRECTIVE, UNION-FLAG) # ------------------------------------------------------------- m4_define([_AT_CHECK_PRINTER_AND_DESTRUCTOR], [# Make sure complex $n work. m4_if([$1$2$3$4], $[1]$[2]$[3]$[4], [], [m4_fatal([$0: Invalid arguments: $@])])dnl # Be sure to pass all the %directives to this macro to have correct # helping macros. So don't put any directly in the Bison file. AT_BISON_OPTION_PUSHDEFS([$5]) AT_DATA_GRAMMAR([[input.y]], [[%code requires { #include #include #include #include #define YYINITDEPTH 10 #define YYMAXDEPTH 10 #define RANGE(Location) ]AT_CXX_IF([(Location).begin.line, (Location).end.line], [(Location).first_line, (Location).last_line])[ #define USE(SYM) /* Display the symbol type Symbol. */ #define V(Symbol, Value, Location, Sep) \ fprintf (stderr, #Symbol " (%d@%d-%d)%s", Value, RANGE(Location), Sep) } $5 ]m4_ifval([$6], [%union { int ival; }]) m4_ifval([$6], [[%code provides {]], [[%code {]]) AT_LALR1_CC_IF([typedef yy::location YYLTYPE;])[ ]AT_YYLEX_DECLARE[ ]AT_LALR1_CC_IF([], [AT_YYERROR_DECLARE]) [} ]m4_ifval([$6], [%type '(' 'x' 'y' ')' ';' thing line input '!' raise check-spontaneous-errors END])[ /* FIXME: This %printer isn't actually tested. */ %printer { ]AT_CXX_IF([yyo << $$;], [fprintf (yyo, "%d", $$)])[; } '(' 'x' 'y' ')' ';' thing line input '!' raise check-spontaneous-errors END %destructor { fprintf (stderr, "Freeing nterm input (%d@%d-%d)\n", $$, RANGE (@$)); } input %destructor { fprintf (stderr, "Freeing nterm line (%d@%d-%d)\n", $$, RANGE (@$)); } line %destructor { fprintf (stderr, "Freeing nterm thing (%d@%d-%d)\n", $$, RANGE (@$)); } thing %destructor { fprintf (stderr, "Freeing nterm raise (%d@%d-%d)\n", $$, RANGE (@$)); } raise %destructor { fprintf (stderr, "Freeing nterm check-spontaneous-errors (%d@%d-%d)\n", $$, RANGE (@$)); } check-spontaneous-errors %destructor { fprintf (stderr, "Freeing token 'x' (%d@%d-%d)\n", $$, RANGE (@$)); } 'x' %destructor { fprintf (stderr, "Freeing token 'y' (%d@%d-%d)\n", $$, RANGE (@$)); } 'y' %token END 0 %destructor { fprintf (stderr, "Freeing token END (%d@%d-%d)\n", $$, RANGE (@$)); } END %% /* This grammar is made to exercise error recovery. "Lines" starting with '(' support error recovery, with ')' as synchronizing token. Lines starting with 'x' can never be recovered from if in error. */ input: %empty { $$ = 0; V(input, $$, @$, ": /* Nothing */\n"); } | line input /* Right recursive to load the stack so that popping at END can be exercised. */ { $$ = 2; V(input, $$, @$, ": "); V(line, $1, @1, " "); V(input, $2, @2, "\n"); } | '!' check-spontaneous-errors { $$ = $2; } ; check-spontaneous-errors: raise { abort(); USE(($$, $1)); } | '(' raise ')' { abort(); USE(($$, $2)); } | error { $$ = 5; V(check-spontaneous-errors, $$, @$, ": "); fprintf (stderr, "error (@%d-%d)\n", RANGE(@1)); } ; raise: %empty { $$ = 4; V(raise, $$, @$, ": %empty\n"); YYERROR; } | '!' '!' { $$ = 5; V(raise, $$, @$, ": "); V(!, $1, @2, " "); V(!, $2, @2, "\n"); YYERROR; } ; line: thing thing thing ';' { $$ = $1; V(line, $$, @$, ": "); V(thing, $1, @1, " "); V(thing, $2, @2, " "); V(thing, $3, @3, " "); V(;, $4, @4, "\n"); } | '(' thing thing ')' { $$ = $1; V(line, $$, @$, ": "); V('(', $1, @1, " "); V(thing, $2, @2, " "); V(thing, $3, @3, " "); V(')', $4, @4, "\n"); } | '(' thing ')' { $$ = $1; V(line, $$, @$, ": "); V('(', $1, @1, " "); V(thing, $2, @2, " "); V(')', $3, @3, "\n"); } | '(' error ')' { $$ = -1; V(line, $$, @$, ": "); V('(', $1, @1, " "); fprintf (stderr, "error (@%d-%d) ", RANGE(@2)); V(')', $3, @3, "\n"); } ; thing: 'x' { $$ = $1; V(thing, $$, @$, ": "); V('x', $1, @1, "\n"); } ; %% /* Alias to ARGV[1]. */ const char *source = YY_NULLPTR; ]AT_YYERROR_DEFINE[ static ]AT_YYLEX_PROTOTYPE[ { static int counter = 0; int c = ]AT_VAL[]m4_ifval([$6], [.ival])[ = counter++; assert (c <= YY_CAST (int, strlen (source))); /* As in BASIC, line numbers go from 10 to 10. */ ]AT_LOC_FIRST_LINE[ = ]AT_LOC_FIRST_COLUMN[ = (10 * c); ]AT_LOC_LAST_LINE[ = ]AT_LOC_LAST_COLUMN[ = ]AT_LOC_FIRST_LINE[ + 9; if (source[c]) fprintf (stderr, "sending: '%c'", source[c]); else fprintf (stderr, "sending: END"); fprintf (stderr, " (%d@%d-%d)\n", c, RANGE (]AT_LOC[)); return source[c]; } ]AT_LALR1_CC_IF([static bool yydebug;])[ ]AT_CXX_IF([int yyparse () { yy::parser parser; parser.set_debug_level (yydebug); return parser.parse (); } ])[ int main (int argc, const char *argv[]) { int status; yydebug = !!getenv ("YYDEBUG"); assert (argc == 2); (void) argc; source = argv[1]; status = yyparse (); switch (status) { case 0: fprintf (stderr, "Successful parse.\n"); break; case 1: fprintf (stderr, "Parsing FAILED.\n"); break; default: fprintf (stderr, "Parsing FAILED (status %d).\n", status); break; } return status; } ]]) AT_FULL_COMPILE([input]) # Check the location of "empty" # ----------------------------- # I.e., epsilon-reductions, as in "(x)" which ends by reducing # an empty "line" nterm. # FIXME: This location is not satisfying. Depend on the lookahead? AT_PARSER_CHECK([input '(x)'], 0, [], [[sending: '(' (0@0-9) sending: 'x' (1@10-19) thing (1@10-19): 'x' (1@10-19) sending: ')' (2@20-29) line (0@0-29): '(' (0@0-9) thing (1@10-19) ')' (2@20-29) sending: END (3@30-39) input (0@29-29): /* Nothing */ input (2@0-29): line (0@0-29) input (0@29-29) Freeing token END (3@30-39) Freeing nterm input (2@0-29) Successful parse. ]]) # Check the location of empty reductions raising an error # ------------------------------------------------------- # Here, the error is after token "!@0-9", so the error is raised from # @9-9, and the error recovery detects that it starts from @9-9 and # ends where starts the next token: END@10-19. # # So error recovery reports error@9-19. AT_PARSER_CHECK([input '!'], 0, [], [[sending: '!' (0@0-9) sending: END (1@10-19) raise (4@9-9): %empty check-spontaneous-errors (5@9-19): error (@9-19) Freeing token END (1@10-19) Freeing nterm input (5@0-19) Successful parse. ]]) # Check the location of not empty reductions raising an error # ----------------------------------------------------------- # This time the error is raised from a rule with 2 rhs symbols: @10-29. # It is recovered @10-29. AT_PARSER_CHECK([[input '!!!']], 0, [], [[sending: '!' (0@0-9) sending: '!' (1@10-19) sending: '!' (2@20-29) raise (5@10-29): ! (1@20-29) ! (2@20-29) check-spontaneous-errors (5@10-29): error (@10-29) sending: END (3@30-39) Freeing token END (3@30-39) Freeing nterm input (5@0-29) Successful parse. ]]) # Check locations in error recovery # --------------------------------- # '(y)' is an error, but can be recovered from. But what's the location # of the error itself ('y'), and of the resulting reduction ('(error)'). AT_PARSER_CHECK([input '(y)'], 0, [], [[sending: '(' (0@0-9) sending: 'y' (1@10-19) 10.10-19.18: syntax error, unexpected 'y', expecting 'x' Freeing token 'y' (1@10-19) sending: ')' (2@20-29) line (-1@0-29): '(' (0@0-9) error (@10-19) ')' (2@20-29) sending: END (3@30-39) input (0@29-29): /* Nothing */ input (2@0-29): line (-1@0-29) input (0@29-29) Freeing token END (3@30-39) Freeing nterm input (2@0-29) Successful parse. ]]) # Syntax errors caught by the parser # ---------------------------------- # Exercise the discarding of stack top and input until 'error' # can be reduced. # # '(', 'x', 'x', 'x', 'x', 'x', ')', # # Load the stack and provoke an error that cannot be caught by the # grammar, to check that the stack is cleared. And make sure the # lookahead is freed. # # '(', 'x', ')', # '(', 'x', ')', # 'y' AT_PARSER_CHECK([input '(xxxxx)(x)(x)y'], 1, [], [[sending: '(' (0@0-9) sending: 'x' (1@10-19) thing (1@10-19): 'x' (1@10-19) sending: 'x' (2@20-29) thing (2@20-29): 'x' (2@20-29) sending: 'x' (3@30-39) 30.30-39.38: syntax error, unexpected 'x', expecting ')' Freeing nterm thing (2@20-29) Freeing nterm thing (1@10-19) Freeing token 'x' (3@30-39) sending: 'x' (4@40-49) Freeing token 'x' (4@40-49) sending: 'x' (5@50-59) Freeing token 'x' (5@50-59) sending: ')' (6@60-69) line (-1@0-69): '(' (0@0-9) error (@10-59) ')' (6@60-69) sending: '(' (7@70-79) sending: 'x' (8@80-89) thing (8@80-89): 'x' (8@80-89) sending: ')' (9@90-99) line (7@70-99): '(' (7@70-79) thing (8@80-89) ')' (9@90-99) sending: '(' (10@100-109) sending: 'x' (11@110-119) thing (11@110-119): 'x' (11@110-119) sending: ')' (12@120-129) line (10@100-129): '(' (10@100-109) thing (11@110-119) ')' (12@120-129) sending: 'y' (13@130-139) input (0@129-129): /* Nothing */ input (2@100-129): line (10@100-129) input (0@129-129) input (2@70-129): line (7@70-99) input (2@100-129) input (2@0-129): line (-1@0-69) input (2@70-129) 130.130-139.138: syntax error, unexpected 'y', expecting END Freeing nterm input (2@0-129) Freeing token 'y' (13@130-139) Parsing FAILED. ]]) # Syntax error caught by the parser where lookahead = END # -------------------------------------------------------- # Load the stack and provoke an error that cannot be caught by the # grammar, to check that the stack is cleared. And make sure the # lookahead is freed. # # '(', 'x', ')', # '(', 'x', ')', # 'x' AT_PARSER_CHECK([input '(x)(x)x'], 1, [], [[sending: '(' (0@0-9) sending: 'x' (1@10-19) thing (1@10-19): 'x' (1@10-19) sending: ')' (2@20-29) line (0@0-29): '(' (0@0-9) thing (1@10-19) ')' (2@20-29) sending: '(' (3@30-39) sending: 'x' (4@40-49) thing (4@40-49): 'x' (4@40-49) sending: ')' (5@50-59) line (3@30-59): '(' (3@30-39) thing (4@40-49) ')' (5@50-59) sending: 'x' (6@60-69) thing (6@60-69): 'x' (6@60-69) sending: END (7@70-79) 70.70-79.78: syntax error, unexpected END, expecting 'x' Freeing nterm thing (6@60-69) Freeing nterm line (3@30-59) Freeing nterm line (0@0-29) Freeing token END (7@70-79) Parsing FAILED. ]]) # Check destruction upon stack overflow # ------------------------------------- # Upon stack overflow, all symbols on the stack should be destroyed. # Only check for yacc.c. AT_YACC_C_IF([ AT_PARSER_CHECK([input '(x)(x)(x)(x)(x)(x)(x)'], 2, [], [[sending: '(' (0@0-9) sending: 'x' (1@10-19) thing (1@10-19): 'x' (1@10-19) sending: ')' (2@20-29) line (0@0-29): '(' (0@0-9) thing (1@10-19) ')' (2@20-29) sending: '(' (3@30-39) sending: 'x' (4@40-49) thing (4@40-49): 'x' (4@40-49) sending: ')' (5@50-59) line (3@30-59): '(' (3@30-39) thing (4@40-49) ')' (5@50-59) sending: '(' (6@60-69) sending: 'x' (7@70-79) thing (7@70-79): 'x' (7@70-79) sending: ')' (8@80-89) line (6@60-89): '(' (6@60-69) thing (7@70-79) ')' (8@80-89) sending: '(' (9@90-99) sending: 'x' (10@100-109) thing (10@100-109): 'x' (10@100-109) sending: ')' (11@110-119) line (9@90-119): '(' (9@90-99) thing (10@100-109) ')' (11@110-119) sending: '(' (12@120-129) sending: 'x' (13@130-139) thing (13@130-139): 'x' (13@130-139) sending: ')' (14@140-149) line (12@120-149): '(' (12@120-129) thing (13@130-139) ')' (14@140-149) sending: '(' (15@150-159) sending: 'x' (16@160-169) thing (16@160-169): 'x' (16@160-169) sending: ')' (17@170-179) line (15@150-179): '(' (15@150-159) thing (16@160-169) ')' (17@170-179) sending: '(' (18@180-189) sending: 'x' (19@190-199) thing (19@190-199): 'x' (19@190-199) sending: ')' (20@200-209) 200.200-209.208: memory exhausted Freeing nterm thing (19@190-199) Freeing nterm line (15@150-179) Freeing nterm line (12@120-149) Freeing nterm line (9@90-119) Freeing nterm line (6@60-89) Freeing nterm line (3@30-59) Freeing nterm line (0@0-29) Parsing FAILED (status 2). ]]) ]) AT_BISON_OPTION_POPDEFS ])# _AT_CHECK_PRINTER_AND_DESTRUCTOR # AT_CHECK_PRINTER_AND_DESTRUCTOR([BISON-OPTIONS], [UNION-FLAG], [SKIP_FLAG]) # --------------------------------------------------------------------------- m4_define([AT_CHECK_PRINTER_AND_DESTRUCTOR], [AT_SETUP([Printers and Destructors$2]m4_ifval([$1], [[: $1]])) $3 _AT_CHECK_PRINTER_AND_DESTRUCTOR($[1], $[2], $[3], $[4], [%define parse.error verbose %debug %verbose %locations $1], [$2]) AT_CLEANUP ]) AT_CHECK_PRINTER_AND_DESTRUCTOR([]) AT_CHECK_PRINTER_AND_DESTRUCTOR([], [ with union]) AT_CHECK_PRINTER_AND_DESTRUCTOR([%glr-parser]) AT_CHECK_PRINTER_AND_DESTRUCTOR([%glr-parser], [ with union]) AT_CHECK_PRINTER_AND_DESTRUCTOR([%header %skeleton "lalr1.cc"]) AT_CHECK_PRINTER_AND_DESTRUCTOR([%header %skeleton "lalr1.cc"], [ with union]) AT_CHECK_PRINTER_AND_DESTRUCTOR([%header %skeleton "glr.cc"]) AT_CHECK_PRINTER_AND_DESTRUCTOR([%header %skeleton "glr.cc"], [ with union]) AT_CHECK_PRINTER_AND_DESTRUCTOR([%header %skeleton "glr2.cc"]) AT_CHECK_PRINTER_AND_DESTRUCTOR([%header %skeleton "glr2.cc"], [ with union]) ## ----------------------------------------- ## ## Default tagless %printer and %destructor. ## ## ----------------------------------------- ## # Check that the right %printer and %destructor are called, that they're not # called for $end, and that $$ and @$ work correctly. AT_SETUP([Default tagless %printer and %destructor]) AT_BISON_OPTION_PUSHDEFS([%locations %debug]) AT_DATA_GRAMMAR([[input.y]], [[%define parse.error verbose %debug %locations %code { ]AT_YYLEX_DECLARE[ ]AT_YYERROR_DECLARE[ # define USE(SYM) } %printer { #error "<*> printer should not be used." } <*> %printer { fprintf (yyo, "<> printer for '%c' @ %d", $$, @$.first_column); } <> %destructor { printf ("<> destructor for '%c' @ %d.\n", $$, @$.first_column); } <> %printer { fprintf (yyo, "'b'/'c' printer for '%c' @ %d", $$, @$.first_column); } 'b' 'c' %destructor { printf ("'b'/'c' destructor for '%c' @ %d.\n", $$, @$.first_column); } 'b' 'c' %destructor { #error "<*> destructor should not be used." } <*> %% start: 'a' 'b' 'c' 'd' 'e' { $$ = 'S'; USE(($1, $2, $3, $4, $5)); } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE(["abcd"], [[yylval = res]])[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_CHECK([-o input.c input.y], [], [], [[input.y:30.3-5: warning: useless %destructor for type <*> [-Wother] input.y:30.3-5: warning: useless %printer for type <*> [-Wother] ]]) AT_COMPILE([input]) AT_PARSER_CHECK([input --debug], 1, [[<> destructor for 'd' @ 4. 'b'/'c' destructor for 'c' @ 3. 'b'/'c' destructor for 'b' @ 2. <> destructor for 'a' @ 1. ]], [[Starting parse Entering state 0 Stack now 0 Reading a token Next token is token 'a' (1.1: <> printer for 'a' @ 1) Shifting token 'a' (1.1: <> printer for 'a' @ 1) Entering state 1 Stack now 0 1 Reading a token Next token is token 'b' (1.2: 'b'/'c' printer for 'b' @ 2) Shifting token 'b' (1.2: 'b'/'c' printer for 'b' @ 2) Entering state 3 Stack now 0 1 3 Reading a token Next token is token 'c' (1.3: 'b'/'c' printer for 'c' @ 3) Shifting token 'c' (1.3: 'b'/'c' printer for 'c' @ 3) Entering state 5 Stack now 0 1 3 5 Reading a token Next token is token 'd' (1.4: <> printer for 'd' @ 4) Shifting token 'd' (1.4: <> printer for 'd' @ 4) Entering state 6 Stack now 0 1 3 5 6 Reading a token Now at end of input. 1.5: syntax error, unexpected end of file, expecting 'e' Error: popping token 'd' (1.4: <> printer for 'd' @ 4) Stack now 0 1 3 5 Error: popping token 'c' (1.3: 'b'/'c' printer for 'c' @ 3) Stack now 0 1 3 Error: popping token 'b' (1.2: 'b'/'c' printer for 'b' @ 2) Stack now 0 1 Error: popping token 'a' (1.1: <> printer for 'a' @ 1) Stack now 0 Cleanup: discarding lookahead token "end of file" (1.5: ) Stack now 0 ]]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ## ------------------------------------------------------ ## ## Default tagged and per-type %printer and %destructor. ## ## ------------------------------------------------------ ## AT_SETUP([Default tagged and per-type %printer and %destructor]) AT_BISON_OPTION_PUSHDEFS([%debug]) AT_DATA_GRAMMAR([[input.y]], [[%define parse.error verbose %debug %{ ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ # define USE(SYM) %} %printer { #error "<> printer should not be used." } <> %union { int field0; int field1; int field2; } %type start 'a' 'g' %type 'e' %type 'f' %printer { fprintf (yyo, "<*>//e printer"); } <*> 'e' %destructor { printf ("<*>//e destructor.\n"); } <*> 'e' %type 'b' %printer { fprintf (yyo, " printer"); } %destructor { printf (" destructor.\n"); } %type 'c' %printer { fprintf (yyo, "'c' printer"); } 'c' %destructor { printf ("'c' destructor.\n"); } 'c' %type 'd' %printer { fprintf (yyo, "'d' printer"); } 'd' %destructor { printf ("'d' destructor.\n"); } 'd' %destructor { #error "<> destructor should not be used." } <> %% start: 'a' 'b' 'c' 'd' 'e' 'f' 'g' { USE(($1, $2, $3, $4, $5, $6, $7)); $$ = 'S'; } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE(["abcdef"])[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_CHECK([-o input.c input.y], [], [], [[input.y:22.3-4: warning: useless %destructor for type <> [-Wother] input.y:22.3-4: warning: useless %printer for type <> [-Wother] ]]) AT_COMPILE([input]) AT_PARSER_CHECK([input --debug], 1, [[<*>//e destructor. <*>//e destructor. 'd' destructor. 'c' destructor. destructor. <*>//e destructor. ]], [[Starting parse Entering state 0 Stack now 0 Reading a token Next token is token 'a' (<*>//e printer) Shifting token 'a' (<*>//e printer) Entering state 1 Stack now 0 1 Reading a token Next token is token 'b' ( printer) Shifting token 'b' ( printer) Entering state 3 Stack now 0 1 3 Reading a token Next token is token 'c' ('c' printer) Shifting token 'c' ('c' printer) Entering state 5 Stack now 0 1 3 5 Reading a token Next token is token 'd' ('d' printer) Shifting token 'd' ('d' printer) Entering state 6 Stack now 0 1 3 5 6 Reading a token Next token is token 'e' (<*>//e printer) Shifting token 'e' (<*>//e printer) Entering state 7 Stack now 0 1 3 5 6 7 Reading a token Next token is token 'f' (<*>//e printer) Shifting token 'f' (<*>//e printer) Entering state 8 Stack now 0 1 3 5 6 7 8 Reading a token Now at end of input. syntax error, unexpected end of file, expecting 'g' Error: popping token 'f' (<*>//e printer) Stack now 0 1 3 5 6 7 Error: popping token 'e' (<*>//e printer) Stack now 0 1 3 5 6 Error: popping token 'd' ('d' printer) Stack now 0 1 3 5 Error: popping token 'c' ('c' printer) Stack now 0 1 3 Error: popping token 'b' ( printer) Stack now 0 1 Error: popping token 'a' (<*>//e printer) Stack now 0 Cleanup: discarding lookahead token "end of file" () Stack now 0 ]]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ## ------------------------------------------------------------- ## ## Default %printer and %destructor for user-defined end token. ## ## ------------------------------------------------------------- ## AT_SETUP([Default %printer and %destructor for user-defined end token]) # Enable declaration of default %printer/%destructor. Make the parser # use these for all user-declared grammar symbols for which the user # does not declare a specific %printer/%destructor. Thus, the parser # uses it for token 0 if the user declares it but not if Bison # generates it as $end. Discussed starting at # , # , and # . # AT_TEST(TYPED) # -------------- m4_pushdef([AT_TEST], [m4_if($1, 0, [m4_pushdef([kind], []) m4_pushdef([not_kind], [*])], [m4_pushdef([kind], [*]) m4_pushdef([not_kind], [])]) AT_BISON_OPTION_PUSHDEFS([%locations %debug]) AT_DATA_GRAMMAR([[input]]$1[[.y]], [[%define parse.error verbose %debug %locations %code { ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ # define USE(SYM) } %destructor { #error "<]]not_kind[[> destructor should not be used." } <]]not_kind[[> %token END 0 %printer { fprintf (yyo, "<]]kind[[> for '%c' @ %d", $$, @$.first_column); } <]]kind[[> %destructor { printf ("<]]kind[[> for '%c' @ %d.\n", $$, @$.first_column); } <]]kind[[> %printer { #error "<]]not_kind[[> printer should not be used." } <]]not_kind[[> ]]m4_if($1, 0, [[[ ]]], [[[%union { char tag; } %type start END]]])[[ %% start: { $$ = 'S'; } ; %% #include /* abort */ static int yylex (void) { static int called; if (called++) abort (); yylval]]m4_if($1, 0,, [[[.tag]]])[[ = 'E'; yylloc.first_line = yylloc.last_line = 1; yylloc.first_column = yylloc.last_column = 1; return 0; } ]AT_YYERROR_DEFINE[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_OPTION_POPDEFS AT_BISON_CHECK([-o input$1.c input$1.y], [], [], [m4_if([$1], [0], [[input0.y:30.3-5: warning: useless %destructor for type <*> [-Wother] input0.y:30.3-5: warning: useless %printer for type <*> [-Wother] ]], [[input1.y:30.3-4: warning: useless %destructor for type <> [-Wother] input1.y:30.3-4: warning: useless %printer for type <> [-Wother] ]])]) AT_COMPILE([input$1]) AT_PARSER_CHECK([input$1 --debug], 0, [[<]]kind[[> for 'E' @ 1. <]]kind[[> for 'S' @ 1. ]], [[Starting parse Entering state 0 Stack now 0 Reducing stack by rule 1 (line 49): -> $$ = nterm start (1.1: <]]kind[[> for 'S' @ 1) Entering state 1 Stack now 0 1 Reading a token Now at end of input. Shifting token END (1.1: <]]kind[[> for 'E' @ 1) Entering state 2 Stack now 0 1 2 Stack now 0 1 2 Cleanup: popping token END (1.1: <]]kind[[> for 'E' @ 1) Cleanup: popping nterm start (1.1: <]]kind[[> for 'S' @ 1) ]]) m4_popdef([kind]) m4_popdef([not_kind]) ]) AT_TEST(0) AT_TEST(1) m4_popdef([AT_TEST]) AT_CLEANUP ## ------------------------------------------------------------------ ## ## Default %printer and %destructor are not for error or $undefined. ## ## ------------------------------------------------------------------ ## AT_SETUP([Default %printer and %destructor are not for error or $undefined]) # If Bison were to apply the default %printer and %destructor to the error # token or to $undefined: # - For the error token: # - It would generate warnings for unused $n. # - It would invoke the %printer and %destructor on the error token's # semantic value, which would be initialized from the lookahead, which # would be destroyed separately. # - For $undefined, who knows what the semantic value would be. AT_BISON_OPTION_PUSHDEFS([%debug]) AT_DATA_GRAMMAR([[input.y]], [[%debug %{ # include # include ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ # define USE(SYM) %} %printer { fprintf (yyo, "'%c'", $$); } <> <*> %destructor { fprintf (stderr, "DESTROY '%c'\n", $$); } <> <*> %% start: { $$ = 'S'; } /* In order to reveal the problems that this bug caused during parsing, add * $2 to USE. */ | 'a' error 'b' 'c' { USE(($1, $3, $4)); $$ = 'S'; } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE(["abd"], [yylval = res])[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_OPTION_POPDEFS AT_BISON_CHECK([-o input.c input.y], [], [], [[input.y:23.6-8: warning: useless %destructor for type <*> [-Wother] input.y:23.6-8: warning: useless %printer for type <*> [-Wother] ]]) AT_COMPILE([input]) AT_PARSER_CHECK([input --debug], [1], [], [[Starting parse Entering state 0 Stack now 0 Reading a token Next token is token 'a' ('a') Shifting token 'a' ('a') Entering state 1 Stack now 0 1 Reading a token Next token is token 'b' ('b') syntax error Shifting token error () Entering state 3 Stack now 0 1 3 Next token is token 'b' ('b') Shifting token 'b' ('b') Entering state 5 Stack now 0 1 3 5 Reading a token Next token is token "invalid token" () Error: popping token 'b' ('b') DESTROY 'b' Stack now 0 1 3 Error: popping token error () Stack now 0 1 Shifting token error () Entering state 3 Stack now 0 1 3 Next token is token "invalid token" () Error: discarding token "invalid token" () Error: popping token error () Stack now 0 1 Shifting token error () Entering state 3 Stack now 0 1 3 Reading a token Now at end of input. Cleanup: discarding lookahead token "end of file" () Stack now 0 1 3 Cleanup: popping token error () Cleanup: popping token 'a' ('a') DESTROY 'a' ]]) AT_CLEANUP ## ------------------------------------------------------ ## ## Default %printer and %destructor are not for $accept. ## ## ------------------------------------------------------ ## AT_SETUP([Default %printer and %destructor are not for $accept]) # If YYSTYPE is a union and Bison were to apply the default %printer and # %destructor to $accept: # - The %printer and %destructor code generated for $accept would always be # dead code because $accept is currently never shifted onto the stack. # - $$ for $accept would always be of type YYSTYPE because it's not possible # to declare '%type $accept'. (Also true for $undefined.) # - Thus, the compiler might complain that the user code assumes the wrong # type for $$ since the code might assume the type associated with a # specific union field, which is especially reasonable in C++ since that # type may be a base type. This test case checks for this problem. (Also # true for $undefined and the error token, so there are three warnings for # %printer and three for %destructor.) AT_BISON_OPTION_PUSHDEFS([%debug]) AT_DATA_GRAMMAR([[input.y]], [[%debug /* So that %printer is actually compiled. */ %{ # include # include ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ # define USE(SYM) %} %printer { char chr = $$; fprintf (yyo, "'%c'", chr); } <> <*> %destructor { char chr = $$; fprintf (stderr, "DESTROY '%c'\n", chr); } <> <*> %union { char chr; } %type start %% start: { USE($$); } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_OPTION_POPDEFS AT_BISON_CHECK([-o input.c input.y], [], [], [[input.y:24.3-4: warning: useless %destructor for type <> [-Wother] input.y:24.3-4: warning: useless %printer for type <> [-Wother] ]]) AT_COMPILE([input]) AT_CLEANUP ## ----------------------------------------------------- ## ## Default %printer and %destructor for midrule values. ## ## ----------------------------------------------------- ## AT_SETUP([Default %printer and %destructor for midrule values]) AT_BISON_OPTION_PUSHDEFS([%debug]) AT_DATA_GRAMMAR([[input.y]], [[%debug /* So that %printer is actually compiled. */ %{ ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ # define USE(SYM) # define YYLTYPE int # define YYLLOC_DEFAULT(Current, Rhs, N) (void)(Rhs) # define LOCATION_PRINT(File, Loc) %} %printer { fprintf (yyo, "%d", @$); } <> %destructor { fprintf (stderr, "DESTROY %d\n", @$); } <> %printer { #error "<*> printer should not be used" } <*> %destructor { #error "<*> destructor should not be used" } <*> %% start: { @$ = 1; } // Not set or used. { USE ($$); @$ = 2; } // Both set and used. { USE ($$); @$ = 3; } // Only set. { @$ = 4; } // Only used. 'c' { USE (($$, $2, $4, $5)); @$ = 0; } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_OPTION_POPDEFS AT_BISON_CHECK([-o input.c input.y], 0,, [[input.y:24.57-59: warning: useless %destructor for type <*> [-Wother] input.y:24.57-59: warning: useless %printer for type <*> [-Wother] input.y:33.3-23: warning: unset value: $$ [-Wother] input.y:32.3-23: warning: unused value: $3 [-Wother] ]]) AT_BISON_CHECK([-fcaret -o input.c input.y], 0,, [[input.y:24.57-59: warning: useless %destructor for type <*> [-Wother] 24 | %printer { #error "<*> printer should not be used" } <*> | ^~~ input.y:24.57-59: warning: useless %printer for type <*> [-Wother] 24 | %printer { #error "<*> printer should not be used" } <*> | ^~~ input.y:33.3-23: warning: unset value: $$ [-Wother] 33 | { @$ = 4; } // Only used. | ^~~~~~~~~~~~~~~~~~~~~ input.y:32.3-23: warning: unused value: $3 [-Wother] 32 | { USE ($$); @$ = 3; } // Only set. | ^~~~~~~~~~~~~~~~~~~~~ ]]) AT_COMPILE([input]) AT_PARSER_CHECK([input --debug], 1,, [[Starting parse Entering state 0 Stack now 0 Reducing stack by rule 1 (line 30): -> $$ = nterm $@1 (: ) Entering state 2 Stack now 0 2 Reducing stack by rule 2 (line 31): -> $$ = nterm @2 (: 2) Entering state 4 Stack now 0 2 4 Reducing stack by rule 3 (line 32): -> $$ = nterm @3 (: 3) Entering state 5 Stack now 0 2 4 5 Reducing stack by rule 4 (line 33): -> $$ = nterm @4 (: 4) Entering state 6 Stack now 0 2 4 5 6 Reading a token Now at end of input. syntax error Error: popping nterm @4 (: 4) DESTROY 4 Stack now 0 2 4 5 Error: popping nterm @3 (: 3) DESTROY 3 Stack now 0 2 4 Error: popping nterm @2 (: 2) DESTROY 2 Stack now 0 2 Error: popping nterm $@1 (: ) Stack now 0 Cleanup: discarding lookahead token "end of file" (: ) Stack now 0 ]]) AT_CLEANUP ## ----------------------- ## ## @$ implies %locations. ## ## ----------------------- ## # Bison once forgot to check for @$ in actions other than semantic actions. # AT_CHECK_ACTION_LOCATIONS(ACTION-DIRECTIVE) # ------------------------------------------- m4_define([AT_CHECK_ACTION_LOCATIONS], [AT_SETUP([[@$ in ]$1[ implies %locations]]) AT_BISON_OPTION_PUSHDEFS([%debug]) AT_DATA_GRAMMAR([[input.y]], [[%code { #include ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %debug ]$1[ { fprintf (stderr, "%d\n", @$.first_line); } ]m4_if($1, [%initial-action], [], [[start]])[ %% start: ; %% static int yylex (void) { return 0; } ]AT_YYERROR_DEFINE[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_CHECK([[-o input.c input.y]]) AT_COMPILE([[input]]) AT_BISON_OPTION_POPDEFS AT_CLEANUP]) AT_CHECK_ACTION_LOCATIONS([[%initial-action]]) AT_CHECK_ACTION_LOCATIONS([[%destructor]]) AT_CHECK_ACTION_LOCATIONS([[%printer]]) ## ------------------------- ## ## Qualified $$ in actions. ## ## ------------------------- ## # Check that we can use qualified $$ (v.g., $$) not only in rule # actions, but also where $$ is valid: %destructor/%printer and # %initial-action. # # FIXME: Not actually checking %destructor, but it's the same code as # %printer... # # To do that, use a semantic value that has two fields (sem_type), # declare symbols to have only one of these types (INT, float), and # use $$ to get the other one. Including for symbols that are # not typed (UNTYPED). m4_pushdef([AT_TEST], [AT_SETUP([[Qualified $$ in actions: $1]]) AT_BISON_OPTION_PUSHDEFS([%skeleton "$1" %debug]) AT_DATA_GRAMMAR([[input.y]], [[%skeleton "$1" %debug %code requires { typedef struct sem_type { int ival; float fval; } sem_type; ]AT_CXX_IF([[ # include // EOF. # include namespace { void report (std::ostream& yyo, int ival, float fval) { yyo << "ival: " << ival << ", fval: " << fval; } } ]], [[ # include static void report (FILE* yyo, int ival, float fval) { fprintf (yyo, "ival: %d, fval: %1.1f", ival, fval); } ]])[ } %define api.value.type {sem_type} %code { ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %token UNTYPED %token INT %type float %printer { report (yyo, $$, $$); } ; %printer { report (yyo, $$, $$ ); } ; %printer { report (yyo, $$, $$); } <>; %initial-action { $$ = 42; $$ = 4.2f; } %% float: UNTYPED INT { $$ = $1 + $2; $$ = $1 + $][2; }; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE([[{]AT_TOKEN([UNTYPED])[, ]AT_TOKEN([INT])[, EOF}]], [AT_VAL.ival = toknum * 10; AT_VAL.fval = YY_CAST (float, toknum) / 10.0f;])[ ]AT_MAIN_DEFINE[ ]]) AT_FULL_COMPILE([[input]]) AT_PARSER_CHECK([input --debug], 0, [], [stderr]) # Don't be too picky on the traces, GLR is not exactly the same. Keep # only the lines from the printer. AT_CHECK([[sed -ne '/ival:/p' stderr]], 0, [[Next token is token UNTYPED (ival: 10, fval: 0.1) Shifting token UNTYPED (ival: 10, fval: 0.1) Next token is token INT (ival: 20, fval: 0.2) Shifting token INT (ival: 20, fval: 0.2) $][1 = token UNTYPED (ival: 10, fval: 0.1) $][2 = token INT (ival: 20, fval: 0.2) -> $$ = nterm float (ival: 30, fval: 0.3) Cleanup: popping nterm float (ival: 30, fval: 0.3) ]]) AT_BISON_OPTION_POPDEFS AT_CLEANUP ]) m4_map_args([AT_TEST], [yacc.c], [glr.c], [lalr1.cc], [glr.cc], [glr2.cc]) m4_popdef([AT_TEST]) ## -------------------------------------------------- ## ## Destroying lookahead assigned by semantic action. ## ## -------------------------------------------------- ## AT_SETUP([[Destroying lookahead assigned by semantic action]]) AT_BISON_OPTION_PUSHDEFS AT_DATA_GRAMMAR([input.y], [[ %code { #include #include ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ #define USE(Var) } %destructor { fprintf (stderr, "'a' destructor\n"); } 'a' %destructor { fprintf (stderr, "'b' destructor\n"); } 'b' %% // In a previous version of Bison, yychar assigned by the semantic // action below was not translated into yytoken before the lookahead was // discarded and thus before its destructor (selected according to // yytoken) was called in order to return from yyparse. This would // happen even if YYACCEPT was performed in a later semantic action as // long as only consistent states with default reductions were visited // in between. However, we leave YYACCEPT in the same semantic action // for this test in order to show that skeletons cannot simply translate // immediately after every semantic action because a semantic action // that has set yychar might not always return normally. Instead, // skeletons must translate before every use of yytoken. start: 'a' accept { USE($1); } ; accept: %empty { assert (yychar == YYEMPTY); yychar = 'b'; YYACCEPT; } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE(["a"])[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_OPTION_POPDEFS AT_BISON_CHECK([[-o input.c input.y]]) AT_COMPILE([[input]]) AT_PARSER_CHECK([[input]], [[0]], [], [['b' destructor 'a' destructor ]]) AT_CLEANUP ## ---------- ## ## YYBACKUP. ## ## ---------- ## AT_SETUP([[YYBACKUP]]) AT_BISON_OPTION_PUSHDEFS([%pure-parser %debug]) AT_DATA_GRAMMAR([input.y], [[ %define parse.error verbose %debug %define api.pure %code { # include # include # include ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %% input: exp exp {} ; exp: 'a' { printf ("a: %d\n", $1); } | 'b' { YYBACKUP('a', 123); } | 'c' 'd' { YYBACKUP('a', 456); } ; %% ]AT_YYERROR_DEFINE[ ]AT_YYLEX_DEFINE(["bcd"], [*lvalp = (toknum + 1) * 10])[ ]AT_MAIN_DEFINE[ ]]) AT_BISON_OPTION_POPDEFS AT_BISON_CHECK([[-o input.c input.y]]) AT_COMPILE([[input]]) AT_PARSER_CHECK([[input]], [[0]], [[a: 123 a: 456 ]]) AT_CLEANUP