+
+
+
+## ------------------------------------------------------ ##
+## Default %printer and %destructor for mid-rule values. ##
+## ------------------------------------------------------ ##
+
+AT_SETUP([Default %printer and %destructor for mid-rule values])
+
+AT_DATA_GRAMMAR([[input.y]],
+[[%debug /* So that %printer is actually compiled. */
+
+%{
+# include <stdio.h>
+# include <stdlib.h>
+ static void yyerror (const char *msg);
+ static int yylex (void);
+# define USE(SYM)
+# define YYLTYPE int
+# define YYLLOC_DEFAULT(Current, Rhs, N) (void)(Rhs)
+# define YY_LOCATION_PRINT(File, Loc)
+%}
+
+%printer { fprintf (yyoutput, "%d", @$); } <>
+%destructor { fprintf (stderr, "DESTROY %d\n", @$); } <>
+%printer { fprintf (yyoutput, "<*> printer should not be called"); } <*>
+%destructor { fprintf (yyoutput, "<*> destructor should not be called"); } <*>
+
+%%
+
+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; }
+ ;
+
+%%
+
+static int
+yylex (void)
+{
+ static int called;
+ if (called++)
+ abort ();
+ return 0;
+}
+
+static void
+yyerror (const char *msg)
+{
+ fprintf (stderr, "%s\n", msg);
+}
+
+int
+main (void)
+{
+ yydebug = 1;
+ return yyparse ();
+}
+]])
+
+AT_BISON_CHECK([-o input.c input.y], 0,,
+[[input.y:33.3-23: warning: unset value: $$
+input.y:30.3-35.37: warning: unused value: $3
+]])
+
+AT_COMPILE([input])
+AT_PARSER_CHECK([./input], 1,,
+[[Starting parse
+Entering state 0
+Reducing stack by rule 1 (line 30):
+-> $$ = nterm $@1 (: )
+Stack now 0
+Entering state 2
+Reducing stack by rule 2 (line 31):
+-> $$ = nterm @2 (: 2)
+Stack now 0 2
+Entering state 4
+Reducing stack by rule 3 (line 32):
+-> $$ = nterm @3 (: 3)
+Stack now 0 2 4
+Entering state 5
+Reducing stack by rule 4 (line 33):
+-> $$ = nterm @4 (: 4)
+Stack now 0 2 4 5
+Entering state 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 (: )
+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_DATA_GRAMMAR([[input.y]],
+[[%code {
+ #include <stdio.h>
+ static int yylex (void);
+ static void yyerror (char const *msg);
+}
+
+%debug
+
+]$1[ {
+ printf ("%d\n", @$.first_line);
+} ]m4_if($1, [%initial-action], [], [[start]])[
+
+%%
+
+start: ;
+
+%%
+
+static int
+yylex (void)
+{
+ return 0;
+}
+
+static void
+yyerror (char const *msg)
+{
+ fprintf (stderr, "%s\n", msg);
+}
+
+int
+main (void)
+{
+ return yyparse ();
+}
+]])
+
+AT_BISON_CHECK([[-o input.c input.y]])
+AT_COMPILE([[input]])
+
+AT_CLEANUP])
+
+AT_CHECK_ACTION_LOCATIONS([[%initial-action]])
+AT_CHECK_ACTION_LOCATIONS([[%destructor]])
+AT_CHECK_ACTION_LOCATIONS([[%printer]])
+
+
+## ----------------------------------------------- ##
+## Fix user actions without a trailing semicolon. ##
+## ----------------------------------------------- ##
+
+AT_SETUP([[Fix user actions without a trailing semicolon]])
+
+# This feature is undocumented, but we accidentally broke it in 2.3a,
+# and there was a complaint at:
+# <http://lists.gnu.org/archive/html/bug-bison/2008-11/msg00001.html>.
+
+AT_DATA([input.y],
+[[%%
+start: test2 test1 test0 testc;
+
+test2
+: 'a' { semi; /* TEST:N:2 */ }
+| 'b' { if (0) {no_semi} /* TEST:N:2 */ }
+| 'c' { if (0) {semi;} /* TEST:N:2 */ }
+| 'd' { semi; no_semi /* TEST:Y:2 */ }
+| 'e' { semi(); no_semi() /* TEST:Y:2 */ }
+| 'f' { semi[]; no_semi[] /* TEST:Y:2 */ }
+| 'g' { semi++; no_semi++ /* TEST:Y:2 */ }
+| 'h' { {no_semi} no_semi /* TEST:Y:2 */ }
+| 'i' { {semi;} no_semi /* TEST:Y:2 */ }
+;
+test1
+ : 'a' { semi; // TEST:N:1 ;
+} | 'b' { if (0) {no_semi} // TEST:N:1 ;
+} | 'c' { if (0) {semi;} // TEST:N:1 ;
+} | 'd' { semi; no_semi // TEST:Y:1 ;
+} | 'e' { semi(); no_semi() // TEST:Y:1 ;
+} | 'f' { semi[]; no_semi[] // TEST:Y:1 ;
+} | 'g' { semi++; no_semi++ // TEST:Y:1 ;
+} | 'h' { {no_semi} no_semi // TEST:Y:1 ;
+} | 'i' { {semi;} no_semi // TEST:Y:1 ;
+} ;
+test0
+ : 'a' { semi; // TEST:N:1 {}
+} | 'b' { if (0) {no_semi} // TEST:N:1 {}
+} | 'c' { if (0) {semi;} // TEST:N:1 {}
+} | 'd' { semi; no_semi // TEST:Y:1 {}
+} | 'e' { semi(); no_semi() // TEST:Y:1 {}
+} | 'f' { semi[]; no_semi[] // TEST:Y:1 {}
+} | 'g' { semi++; no_semi++ // TEST:Y:1 {}
+} | 'h' { {no_semi} no_semi // TEST:Y:1 {}
+} | 'i' { {semi;} no_semi // TEST:Y:1 {}
+} ;
+
+testc
+: 'a' {
+#define TEST_MACRO_N \
+[]"broken\" $ @ $$ @$ [];\
+string;"}
+| 'b' {
+no_semi
+#define TEST_MACRO_N \
+[]"broken\" $ @ $$ @$ [];\
+string;"}
+]])
+
+AT_BISON_CHECK([[-o input.c input.y]], [0], [],
+[[input.y:8.48: warning: a ';' might be needed at the end of action code
+input.y:8.48: warning: future versions of Bison will not add the ';'
+input.y:9.48: warning: a ';' might be needed at the end of action code
+input.y:9.48: warning: future versions of Bison will not add the ';'
+input.y:10.48: warning: a ';' might be needed at the end of action code
+input.y:10.48: warning: future versions of Bison will not add the ';'
+input.y:11.48: warning: a ';' might be needed at the end of action code
+input.y:11.48: warning: future versions of Bison will not add the ';'
+input.y:12.48: warning: a ';' might be needed at the end of action code
+input.y:12.48: warning: future versions of Bison will not add the ';'
+input.y:13.48: warning: a ';' might be needed at the end of action code
+input.y:13.48: warning: future versions of Bison will not add the ';'
+input.y:20.1: warning: a ';' might be needed at the end of action code
+input.y:20.1: warning: future versions of Bison will not add the ';'
+input.y:21.1: warning: a ';' might be needed at the end of action code
+input.y:21.1: warning: future versions of Bison will not add the ';'
+input.y:22.1: warning: a ';' might be needed at the end of action code
+input.y:22.1: warning: future versions of Bison will not add the ';'
+input.y:23.1: warning: a ';' might be needed at the end of action code
+input.y:23.1: warning: future versions of Bison will not add the ';'
+input.y:24.1: warning: a ';' might be needed at the end of action code
+input.y:24.1: warning: future versions of Bison will not add the ';'
+input.y:25.1: warning: a ';' might be needed at the end of action code
+input.y:25.1: warning: future versions of Bison will not add the ';'
+input.y:31.1: warning: a ';' might be needed at the end of action code
+input.y:31.1: warning: future versions of Bison will not add the ';'
+input.y:32.1: warning: a ';' might be needed at the end of action code
+input.y:32.1: warning: future versions of Bison will not add the ';'
+input.y:33.1: warning: a ';' might be needed at the end of action code
+input.y:33.1: warning: future versions of Bison will not add the ';'
+input.y:34.1: warning: a ';' might be needed at the end of action code
+input.y:34.1: warning: future versions of Bison will not add the ';'
+input.y:35.1: warning: a ';' might be needed at the end of action code
+input.y:35.1: warning: future versions of Bison will not add the ';'
+input.y:36.1: warning: a ';' might be needed at the end of action code
+input.y:36.1: warning: future versions of Bison will not add the ';'
+]])
+
+AT_MATCHES_CHECK([input.c], [[/\* TEST:N:2 \*/ \}$]], [[3]])
+AT_MATCHES_CHECK([input.c], [[/\* TEST:Y:2 \*/ ;\}$]], [[6]])
+AT_MATCHES_CHECK([input.c], [[// TEST:N:1 [;{}]*\n\}$]], [[6]])
+AT_MATCHES_CHECK([input.c], [[// TEST:Y:1 [;{}]*\n;\}$]], [[12]])
+AT_MATCHES_CHECK([input.c], [[#define TEST_MACRO_N \\\n\[\]"broken\\" \$ \@ \$\$ \@\$ \[\];\\\nstring;"\}]], [[2]])
+
+AT_CLEANUP
+
+
+## -------------------------------------------------- ##
+## Destroying lookahead assigned by semantic action. ##
+## -------------------------------------------------- ##
+
+AT_SETUP([[Destroying lookahead assigned by semantic action]])
+
+AT_DATA_GRAMMAR([input.y],
+[[
+%code {
+ #include <assert.h>
+ #include <stdio.h>
+ static void yyerror (char const *);
+ static int yylex (void);
+ #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;
+} ;
+
+%%
+
+static void
+yyerror (char const *msg)
+{
+ fprintf (stderr, "%s\n", msg);
+}
+
+static int
+yylex (void)
+{
+ static char const *input = "a";
+ return *input++;
+}
+
+int
+main (void)
+{
+ return yyparse ();
+}
+]])
+
+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_DATA_GRAMMAR([input.y],
+[[
+%error-verbose
+%debug
+%pure-parser
+%code {
+# include <stdio.h>
+# include <stdlib.h>
+# include <assert.h>
+
+ static void yyerror (const char *msg);
+ static int yylex (YYSTYPE *yylval);
+}
+%%
+input:
+ exp exp {}
+;
+
+exp:
+ 'a' { printf ("a: %d\n", $1); }
+| 'b' { YYBACKUP('a', 123); }
+| 'c' 'd' { YYBACKUP('a', 456); }
+;
+
+%%
+static int
+yylex (YYSTYPE *yylval)
+{
+ static char const input[] = "bcd";
+ static size_t toknum;
+ assert (toknum < sizeof input);
+ *yylval = (toknum + 1) * 10;
+ return input[toknum++];
+}
+
+static void
+yyerror (const char *msg)
+{
+ fprintf (stderr, "%s\n", msg);
+}
+
+int
+main (void)
+{
+ yydebug = !!getenv("YYDEBUG");
+ return yyparse ();
+}
+]])
+
+AT_BISON_CHECK([[-o input.c input.y]])
+AT_COMPILE([[input]])
+AT_PARSER_CHECK([[./input]], [[0]],
+[[a: 123
+a: 456
+]])
+
+AT_CLEANUP