mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 12:23:04 +00:00
See http://www.gnu.org/prep/maintain/maintain.html#Copyright-Notices * HACKING, Makefile.am, NEWS, PACKAGING, README, README-alpha: * TODO, bootstrap, bootstrap.conf: * build-aux/update-b4-copyright, cfg.mk, configure.ac: * data/README, data/bison.m4, data/c++-skel.m4, data/c++.m4: * data/c-skel.m4, data/c.m4, data/glr.c, data/glr.cc: * data/java-skel.m4, data/java.m4, data/lalr1.cc: * data/lalr1.java, data/location.cc: * data/xslt/bison.xsl: * data/xslt/xml2dot.xsl, data/xslt/xml2text.xsl: * data/xslt/xml2xhtml.xsl, data/yacc.c, djgpp/Makefile.maint: * djgpp/README.in, djgpp/config.bat, djgpp/config.sed: * djgpp/config.site, djgpp/config_h.sed, djgpp/djunpack.bat: * djgpp/subpipe.c, djgpp/subpipe.h: * djgpp/testsuite.sed, doc/bison.texinfo: * doc/refcard.tex, etc/README, etc/bench.pl.in: * examples/calc++/Makefile.am, examples/extexi: * lib/abitset.c, lib/abitset.h: * lib/bbitset.h, lib/bitset.c, lib/bitset.h: * lib/bitset_stats.c, lib/bitset_stats.h, lib/bitsetv-print.c: * lib/bitsetv-print.h, lib/bitsetv.c, lib/bitsetv.h: * lib/ebitset.c, lib/ebitset.h, lib/get-errno.c: * lib/get-errno.h, lib/lbitset.c, lib/lbitset.h: * lib/libiberty.h, lib/main.c, lib/timevar.c: * lib/timevar.def, lib/timevar.h, lib/vbitset.c: * lib/vbitset.h, lib/yyerror.c, m4/bison-i18n.m4: * m4/c-working.m4, m4/cxx.m4, m4/subpipe.m4, m4/timevar.m4: * src/AnnotationList.c, src/AnnotationList.h: * src/InadequacyList.c, src/InadequacyList.h, src/LR0.c: * src/LR0.h, src/Sbitset.c, src/Sbitset.h, src/assoc.c: * src/assoc.h, src/closure.c, src/closure.h, src/complain.c: * src/complain.h, src/conflicts.c, src/conflicts.h: * src/derives.c, src/derives.h, src/files.c, src/files.h: * src/flex-scanner.h, src/getargs.c, src/getargs.h: * src/gram.c, src/gram.h, src/graphviz.c, src/ielr.c: * src/ielr.h, src/lalr.c, src/lalr.h: * src/location.c, src/location.h, src/main.c: * src/muscle-tab.c, src/muscle-tab.h, src/named-ref.c: * src/named-ref.h, src/nullable.c, src/nullable.h: * src/output.c, src/output.h, src/parse-gram.y: * src/print-xml.c, src/print-xml.h, src/print.c, src/print.h: * src/print_graph.c, src/print_graph.h, src/reader.c: * src/reader.h, src/reduce.c, src/reduce.h, src/relation.c: * src/relation.h, src/scan-code.h, src/scan-code.l: * src/scan-gram.h, src/scan-gram.l, src/scan-skel.h: * src/scan-skel.l, src/state.c, src/state.h, src/symlist.c: * src/symlist.h, src/symtab.c, src/symtab.h, src/system.h: * src/tables.c, src/tables.h, src/uniqstr.c, src/uniqstr.h: * tests/actions.at, tests/atlocal.in, tests/c++.at: * tests/calc.at, tests/conflicts.at, tests/cxx-type.at: * tests/existing.at, tests/glr-regression.at: * tests/headers.at, tests/input.at, tests/java.at: * tests/local.at, tests/named-refs.at: * tests/output.at, tests/push.at, tests/reduce.at: * tests/regression.at, tests/sets.at, tests/skeletons.at: * tests/synclines.at, tests/testsuite.at, tests/torture.at: * data/Makefile.am, data/location.cc, doc/Makefile.am, src/Makefile.am: * tests/Makefile.am, lib/Makefile.am, examples/Makefile.am: * etc/Makefile.am: Don't use date ranges in copyright notices. Copyright (C) 1987, 1988, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
1483 lines
36 KiB
Plaintext
1483 lines
36 KiB
Plaintext
# Executing Actions. -*- Autotest -*-
|
|
|
|
# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
|
# 2010 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 <http://www.gnu.org/licenses/>.
|
|
|
|
AT_BANNER([[User Actions.]])
|
|
|
|
## ------------------ ##
|
|
## Mid-rule actions. ##
|
|
## ------------------ ##
|
|
|
|
AT_SETUP([Mid-rule actions])
|
|
|
|
# Bison once forgot the mid-rule actions. It was because the action
|
|
# was attached to the host rule (the one with the mid-rule action),
|
|
# instead of being attached to the empty rule dedicated to this
|
|
# action.
|
|
|
|
AT_DATA_GRAMMAR([[input.y]],
|
|
[[%error-verbose
|
|
%debug
|
|
%{
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
static void yyerror (const char *msg);
|
|
static int yylex (void);
|
|
%}
|
|
%%
|
|
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'); }
|
|
;
|
|
%%
|
|
static int
|
|
yylex (void)
|
|
{
|
|
static char const input[] = "123456789";
|
|
static size_t toknum;
|
|
if (! (toknum < sizeof input))
|
|
abort ();
|
|
return input[toknum++];
|
|
}
|
|
|
|
static void
|
|
yyerror (const char *msg)
|
|
{
|
|
fprintf (stderr, "%s\n", msg);
|
|
}
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
return yyparse ();
|
|
}
|
|
]])
|
|
|
|
AT_BISON_CHECK([-d -v -o input.c input.y])
|
|
AT_COMPILE([input])
|
|
AT_PARSER_CHECK([./input], 0,
|
|
[[0123456789
|
|
]])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
|
|
|
|
## ---------------- ##
|
|
## Exotic Dollars. ##
|
|
## ---------------- ##
|
|
|
|
AT_SETUP([Exotic Dollars])
|
|
|
|
AT_DATA_GRAMMAR([[input.y]],
|
|
[[%error-verbose
|
|
%debug
|
|
%{
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
static void yyerror (const char *msg);
|
|
static int yylex (void);
|
|
# define USE(Var)
|
|
%}
|
|
|
|
%union
|
|
{
|
|
int val;
|
|
};
|
|
|
|
%type <val> a_1 a_2 a_5
|
|
sum_of_the_five_previous_values
|
|
|
|
%%
|
|
exp: a_1 a_2 { $<val>$ = 3; } { $<val>$ = $<val>3 + 1; } a_5
|
|
sum_of_the_five_previous_values
|
|
{
|
|
USE (($1, $2, $<foo>3, $<foo>4, $5));
|
|
printf ("%d\n", $6);
|
|
}
|
|
;
|
|
a_1: { $$ = 1; };
|
|
a_2: { $$ = 2; };
|
|
a_5: { $$ = 5; };
|
|
|
|
sum_of_the_five_previous_values:
|
|
{
|
|
$$ = $<val>0 + $<val>-1 + $<val>-2 + $<val>-3 + $<val>-4;
|
|
}
|
|
;
|
|
|
|
%%
|
|
static int
|
|
yylex (void)
|
|
{
|
|
static int called;
|
|
if (called++)
|
|
abort ();
|
|
return EOF;
|
|
}
|
|
|
|
static void
|
|
yyerror (const char *msg)
|
|
{
|
|
fprintf (stderr, "%s\n", msg);
|
|
}
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
return yyparse ();
|
|
}
|
|
]])
|
|
|
|
AT_BISON_CHECK([-d -v -o input.c input.y], 0)
|
|
AT_COMPILE([input])
|
|
AT_PARSER_CHECK([./input], 0,
|
|
[[15
|
|
]])
|
|
|
|
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], $[1]$[2]$[3], [],
|
|
[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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#define YYINITDEPTH 10
|
|
#define YYMAXDEPTH 10
|
|
]AT_LALR1_CC_IF(
|
|
[#define RANGE(Location) (Location).begin.line, (Location).end.line],
|
|
[#define RANGE(Location) (Location).first_line, (Location).last_line])
|
|
[}
|
|
|
|
$5]
|
|
m4_ifval([$6], [%union
|
|
{
|
|
int ival;
|
|
}])
|
|
AT_LALR1_CC_IF([%define global_tokens_and_yystype])
|
|
m4_ifval([$6], [[%code provides {]], [[%code {]])
|
|
AT_LALR1_CC_IF([typedef yy::location YYLTYPE;])
|
|
[static int yylex (]AT_LEX_FORMALS[);
|
|
]AT_LALR1_CC_IF([], [static void yyerror (const char *msg);])
|
|
[}
|
|
|
|
]m4_ifval([$6], [%type <ival> '(' 'x' 'y' ')' ';' thing line input END])[
|
|
|
|
/* FIXME: This %printer isn't actually tested. */
|
|
%printer
|
|
{
|
|
]AT_LALR1_CC_IF([debug_stream () << $$;],
|
|
[fprintf (yyoutput, "%d", $$)])[;
|
|
}
|
|
input line thing 'x' 'y'
|
|
|
|
%destructor
|
|
{ printf ("Freeing nterm input (%d@%d-%d)\n", $$, RANGE (@$)); }
|
|
input
|
|
|
|
%destructor
|
|
{ printf ("Freeing nterm line (%d@%d-%d)\n", $$, RANGE (@$)); }
|
|
line
|
|
|
|
%destructor
|
|
{ printf ("Freeing nterm thing (%d@%d-%d)\n", $$, RANGE (@$)); }
|
|
thing
|
|
|
|
%destructor
|
|
{ printf ("Freeing token 'x' (%d@%d-%d)\n", $$, RANGE (@$)); }
|
|
'x'
|
|
|
|
%destructor
|
|
{ printf ("Freeing token 'y' (%d@%d-%d)\n", $$, RANGE (@$)); }
|
|
'y'
|
|
|
|
%token END 0
|
|
%destructor
|
|
{ printf ("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:
|
|
/* Nothing. */
|
|
{
|
|
$$ = 0;
|
|
printf ("input (%d@%d-%d): /* Nothing */\n", $$, RANGE (@$));
|
|
}
|
|
| line input /* Right recursive to load the stack so that popping at
|
|
END can be exercised. */
|
|
{
|
|
$$ = 2;
|
|
printf ("input (%d@%d-%d): line (%d@%d-%d) input (%d@%d-%d)\n",
|
|
$$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2));
|
|
}
|
|
;
|
|
|
|
line:
|
|
thing thing thing ';'
|
|
{
|
|
$$ = $1;
|
|
printf ("line (%d@%d-%d): thing (%d@%d-%d) thing (%d@%d-%d) thing (%d@%d-%d) ';' (%d@%d-%d)\n",
|
|
$$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2),
|
|
$3, RANGE (@3), $4, RANGE (@4));
|
|
}
|
|
| '(' thing thing ')'
|
|
{
|
|
$$ = $1;
|
|
printf ("line (%d@%d-%d): '(' (%d@%d-%d) thing (%d@%d-%d) thing (%d@%d-%d) ')' (%d@%d-%d)\n",
|
|
$$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2),
|
|
$3, RANGE (@3), $4, RANGE (@4));
|
|
}
|
|
| '(' thing ')'
|
|
{
|
|
$$ = $1;
|
|
printf ("line (%d@%d-%d): '(' (%d@%d-%d) thing (%d@%d-%d) ')' (%d@%d-%d)\n",
|
|
$$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2), $3, RANGE (@3));
|
|
}
|
|
| '(' error ')'
|
|
{
|
|
$$ = -1;
|
|
printf ("line (%d@%d-%d): '(' (%d@%d-%d) error (@%d-%d) ')' (%d@%d-%d)\n",
|
|
$$, RANGE (@$), $1, RANGE (@1), RANGE (@2), $3, RANGE (@3));
|
|
}
|
|
;
|
|
|
|
thing:
|
|
'x'
|
|
{
|
|
$$ = $1;
|
|
printf ("thing (%d@%d-%d): 'x' (%d@%d-%d)\n",
|
|
$$, RANGE (@$), $1, RANGE (@1));
|
|
}
|
|
;
|
|
%%
|
|
/* Alias to ARGV[1]. */
|
|
const char *source = 0;
|
|
|
|
static int
|
|
yylex (]AT_LEX_FORMALS[)
|
|
{
|
|
static unsigned int counter = 0;
|
|
|
|
int c = ]AT_VAL[]m4_ifval([$6], [.ival])[ = counter++;
|
|
/* As in BASIC, line numbers go from 10 to 10. */
|
|
]AT_LALR1_CC_IF(
|
|
[ AT_LOC.begin.line = AT_LOC.begin.column = 10 * c;
|
|
AT_LOC.end.line = AT_LOC.end.column = AT_LOC.begin.line + 9;
|
|
],
|
|
[ AT_LOC.first_line = AT_LOC.first_column = 10 * c;
|
|
AT_LOC.last_line = AT_LOC.last_column = AT_LOC.first_line + 9;
|
|
])[
|
|
|
|
if (! (0 <= c && c <= strlen (source)))
|
|
abort ();
|
|
if (source[c])
|
|
printf ("sending: '%c'", source[c]);
|
|
else
|
|
printf ("sending: END");
|
|
printf (" (%d@%d-%d)\n", c, RANGE (]AT_LOC[));
|
|
return source[c];
|
|
}
|
|
|
|
]AT_LALR1_CC_IF(
|
|
[/* A C++ error reporting function. */
|
|
void
|
|
yy::parser::error (const location& l, const std::string& m)
|
|
{
|
|
printf ("%d-%d: %s\n", RANGE (l), m.c_str());
|
|
}
|
|
|
|
static bool yydebug;
|
|
int
|
|
yyparse ()
|
|
{
|
|
yy::parser parser;
|
|
parser.set_debug_level (yydebug);
|
|
return parser.parse ();
|
|
}
|
|
],
|
|
[static void
|
|
yyerror (const char *msg)
|
|
{
|
|
printf ("%d-%d: %s\n", RANGE (yylloc), msg);
|
|
}])[
|
|
|
|
int
|
|
main (int argc, const char *argv[])
|
|
{
|
|
int status;
|
|
yydebug = !!getenv ("YYDEBUG");
|
|
assert (argc == 2);
|
|
source = argv[1];
|
|
status = yyparse ();
|
|
switch (status)
|
|
{
|
|
case 0: printf ("Successful parse.\n"); break;
|
|
case 1: printf ("Parsing FAILED.\n"); break;
|
|
default: printf ("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 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-19: 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-39: 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-139: 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-79: 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_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-209: 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_CHECK_PRINTER_AND_DESTRUCTOR([BISON-OPTIONS], [UNION-FLAG], [SKIP_FLAG])
|
|
# ---------------------------------------------------------------------------
|
|
m4_define([AT_CHECK_PRINTER_AND_DESTRUCTOR],
|
|
[AT_SETUP([Printers and Destructors $2: $1])
|
|
|
|
$3
|
|
_AT_CHECK_PRINTER_AND_DESTRUCTOR($[1], $[2], $[3], $[4],
|
|
[%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([%defines %skeleton "lalr1.cc"])
|
|
AT_CHECK_PRINTER_AND_DESTRUCTOR([%defines %skeleton "lalr1.cc"], [with union])
|
|
|
|
AT_CHECK_PRINTER_AND_DESTRUCTOR([%glr-parser])
|
|
AT_CHECK_PRINTER_AND_DESTRUCTOR([%glr-parser], [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_DATA_GRAMMAR([[input.y]],
|
|
[[%error-verbose
|
|
%debug
|
|
%locations
|
|
%initial-action {
|
|
@$.first_line = @$.last_line = 1;
|
|
@$.first_column = @$.last_column = 1;
|
|
}
|
|
|
|
%{
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
static void yyerror (const char *msg);
|
|
static int yylex (void);
|
|
# define USE(SYM)
|
|
%}
|
|
|
|
%printer {
|
|
fprintf (yyoutput, "<*> printer should not be called.\n");
|
|
} <*>
|
|
|
|
%printer {
|
|
fprintf (yyoutput, "<> printer for '%c' @ %d", $$, @$.first_column);
|
|
} <>
|
|
%destructor {
|
|
fprintf (stdout, "<> destructor for '%c' @ %d.\n", $$, @$.first_column);
|
|
} <>
|
|
|
|
%printer {
|
|
fprintf (yyoutput, "'b'/'c' printer for '%c' @ %d", $$, @$.first_column);
|
|
} 'b' 'c'
|
|
%destructor {
|
|
fprintf (stdout, "'b'/'c' destructor for '%c' @ %d.\n", $$, @$.first_column);
|
|
} 'b' 'c'
|
|
|
|
%destructor {
|
|
fprintf (yyoutput, "<*> destructor should not be called.\n");
|
|
} <*>
|
|
|
|
%%
|
|
|
|
start: 'a' 'b' 'c' 'd' 'e' { $$ = 'S'; USE(($1, $2, $3, $4, $5)); } ;
|
|
|
|
%%
|
|
|
|
static int
|
|
yylex (void)
|
|
{
|
|
static char const input[] = "abcd";
|
|
static size_t toknum;
|
|
if (! (toknum < sizeof input))
|
|
abort ();
|
|
yylval = input[toknum++];
|
|
yylloc.first_line = yylloc.last_line = 1;
|
|
yylloc.first_column = yylloc.last_column = toknum;
|
|
return yylval;
|
|
}
|
|
|
|
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])
|
|
AT_COMPILE([input])
|
|
AT_PARSER_CHECK([./input], 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
|
|
Reading a token: Next token is token 'a' (1.1-1.1: <> printer for 'a' @ 1)
|
|
Shifting token 'a' (1.1-1.1: <> printer for 'a' @ 1)
|
|
Entering state 1
|
|
Reading a token: Next token is token 'b' (1.2-1.2: 'b'/'c' printer for 'b' @ 2)
|
|
Shifting token 'b' (1.2-1.2: 'b'/'c' printer for 'b' @ 2)
|
|
Entering state 3
|
|
Reading a token: Next token is token 'c' (1.3-1.3: 'b'/'c' printer for 'c' @ 3)
|
|
Shifting token 'c' (1.3-1.3: 'b'/'c' printer for 'c' @ 3)
|
|
Entering state 5
|
|
Reading a token: Next token is token 'd' (1.4-1.4: <> printer for 'd' @ 4)
|
|
Shifting token 'd' (1.4-1.4: <> printer for 'd' @ 4)
|
|
Entering state 6
|
|
Reading a token: Now at end of input.
|
|
syntax error, unexpected $end, expecting 'e'
|
|
Error: popping token 'd' (1.4-1.4: <> printer for 'd' @ 4)
|
|
Stack now 0 1 3 5
|
|
Error: popping token 'c' (1.3-1.3: 'b'/'c' printer for 'c' @ 3)
|
|
Stack now 0 1 3
|
|
Error: popping token 'b' (1.2-1.2: 'b'/'c' printer for 'b' @ 2)
|
|
Stack now 0 1
|
|
Error: popping token 'a' (1.1-1.1: <> printer for 'a' @ 1)
|
|
Stack now 0
|
|
Cleanup: discarding lookahead token $end (1.5-1.5: )
|
|
Stack now 0
|
|
]])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
## ------------------------------------------------------ ##
|
|
## Default tagged and per-type %printer and %destructor. ##
|
|
## ------------------------------------------------------ ##
|
|
|
|
AT_SETUP([Default tagged and per-type %printer and %destructor])
|
|
|
|
AT_DATA_GRAMMAR([[input.y]],
|
|
[[%error-verbose
|
|
%debug
|
|
|
|
%{
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
static void yyerror (const char *msg);
|
|
static int yylex (void);
|
|
# define USE(SYM)
|
|
%}
|
|
|
|
%printer {
|
|
fprintf (yyoutput, "<> printer should not be called.\n");
|
|
} <>
|
|
|
|
%union { int field0; int field1; int field2; }
|
|
%type <field0> start 'a' 'g'
|
|
%type <field1> 'e'
|
|
%type <field2> 'f'
|
|
%printer {
|
|
fprintf (yyoutput, "<*>/<field2>/e printer");
|
|
} <*> 'e' <field2>
|
|
%destructor {
|
|
fprintf (stdout, "<*>/<field2>/e destructor.\n");
|
|
} <*> 'e' <field2>
|
|
|
|
%type <field1> 'b'
|
|
%printer { fprintf (yyoutput, "<field1> printer"); } <field1>
|
|
%destructor { fprintf (stdout, "<field1> destructor.\n"); } <field1>
|
|
|
|
%type <field0> 'c'
|
|
%printer { fprintf (yyoutput, "'c' printer"); } 'c'
|
|
%destructor { fprintf (stdout, "'c' destructor.\n"); } 'c'
|
|
|
|
%type <field1> 'd'
|
|
%printer { fprintf (yyoutput, "'d' printer"); } 'd'
|
|
%destructor { fprintf (stdout, "'d' destructor.\n"); } 'd'
|
|
|
|
%destructor {
|
|
fprintf (yyoutput, "<> destructor should not be called.\n");
|
|
} <>
|
|
|
|
%%
|
|
|
|
start:
|
|
'a' 'b' 'c' 'd' 'e' 'f' 'g'
|
|
{
|
|
USE(($1, $2, $3, $4, $5, $6, $7));
|
|
$$ = 'S';
|
|
}
|
|
;
|
|
|
|
%%
|
|
|
|
static int
|
|
yylex (void)
|
|
{
|
|
static char const input[] = "abcdef";
|
|
static size_t toknum;
|
|
if (! (toknum < sizeof input))
|
|
abort ();
|
|
return input[toknum++];
|
|
}
|
|
|
|
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])
|
|
AT_COMPILE([input])
|
|
AT_PARSER_CHECK([./input], 1,
|
|
[[<*>/<field2>/e destructor.
|
|
<*>/<field2>/e destructor.
|
|
'd' destructor.
|
|
'c' destructor.
|
|
<field1> destructor.
|
|
<*>/<field2>/e destructor.
|
|
]],
|
|
[[Starting parse
|
|
Entering state 0
|
|
Reading a token: Next token is token 'a' (<*>/<field2>/e printer)
|
|
Shifting token 'a' (<*>/<field2>/e printer)
|
|
Entering state 1
|
|
Reading a token: Next token is token 'b' (<field1> printer)
|
|
Shifting token 'b' (<field1> printer)
|
|
Entering state 3
|
|
Reading a token: Next token is token 'c' ('c' printer)
|
|
Shifting token 'c' ('c' printer)
|
|
Entering state 5
|
|
Reading a token: Next token is token 'd' ('d' printer)
|
|
Shifting token 'd' ('d' printer)
|
|
Entering state 6
|
|
Reading a token: Next token is token 'e' (<*>/<field2>/e printer)
|
|
Shifting token 'e' (<*>/<field2>/e printer)
|
|
Entering state 7
|
|
Reading a token: Next token is token 'f' (<*>/<field2>/e printer)
|
|
Shifting token 'f' (<*>/<field2>/e printer)
|
|
Entering state 8
|
|
Reading a token: Now at end of input.
|
|
syntax error, unexpected $end, expecting 'g'
|
|
Error: popping token 'f' (<*>/<field2>/e printer)
|
|
Stack now 0 1 3 5 6 7
|
|
Error: popping token 'e' (<*>/<field2>/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' (<field1> printer)
|
|
Stack now 0 1
|
|
Error: popping token 'a' (<*>/<field2>/e printer)
|
|
Stack now 0
|
|
Cleanup: discarding lookahead token $end ()
|
|
Stack now 0
|
|
]])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
## ------------------------------------------------------------- ##
|
|
## Default %printer and %destructor for user-defined end token. ##
|
|
## ------------------------------------------------------------- ##
|
|
|
|
AT_SETUP([Default %printer and %destructor for user-defined end token])
|
|
|
|
# _AT_CHECK_DEFAULT_PRINTER_AND_DESTRUCTOR_FOR_END_TOKEN(TYPED)
|
|
# -----------------------------------------------------------------------------
|
|
m4_define([_AT_CHECK_DEFAULT_PRINTER_AND_DESTRUCTOR_FOR_END_TOKEN],
|
|
[m4_if($1, 0,
|
|
[m4_pushdef([kind], []) m4_pushdef([not_kind], [*])],
|
|
[m4_pushdef([kind], [*]) m4_pushdef([not_kind], [])])
|
|
|
|
AT_DATA_GRAMMAR([[input]]$1[[.y]],
|
|
[[%error-verbose
|
|
%debug
|
|
%locations
|
|
%initial-action {
|
|
@$.first_line = @$.last_line = 1;
|
|
@$.first_column = @$.last_column = 1;
|
|
}
|
|
|
|
%{
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
static void yyerror (const char *msg);
|
|
static int yylex (void);
|
|
# define USE(SYM)
|
|
%}
|
|
|
|
%destructor {
|
|
fprintf (yyoutput, "<]]not_kind[[> destructor should not be called.\n");
|
|
} <]]not_kind[[>
|
|
|
|
%token END 0
|
|
%printer {
|
|
fprintf (yyoutput, "<]]kind[[> for '%c' @ %d", $$, @$.first_column);
|
|
} <]]kind[[>
|
|
%destructor {
|
|
fprintf (stdout, "<]]kind[[> for '%c' @ %d.\n", $$, @$.first_column);
|
|
} <]]kind[[>
|
|
|
|
%printer {
|
|
fprintf (yyoutput, "<]]not_kind[[> printer should not be called.\n");
|
|
} <]]not_kind[[>
|
|
|
|
]]m4_if($1, 0, [[[
|
|
]]],
|
|
[[[%union { char tag; }
|
|
%type <tag> start END]]])[[
|
|
|
|
%%
|
|
|
|
start: { $$ = 'S'; } ;
|
|
|
|
%%
|
|
|
|
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;
|
|
}
|
|
|
|
static void
|
|
yyerror (const char *msg)
|
|
{
|
|
fprintf (stderr, "%s\n", msg);
|
|
}
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
yydebug = 1;
|
|
return yyparse ();
|
|
}
|
|
]])
|
|
|
|
AT_BISON_CHECK([-o input$1.c input$1.y])
|
|
AT_COMPILE([input$1])
|
|
AT_PARSER_CHECK([./input$1], 0,
|
|
[[<]]kind[[> for 'E' @ 1.
|
|
<]]kind[[> for 'S' @ 1.
|
|
]],
|
|
[[Starting parse
|
|
Entering state 0
|
|
Reducing stack by rule 1 (line 46):
|
|
-> $$ = nterm start (1.1-1.1: <]]kind[[> for 'S' @ 1)
|
|
Stack now 0
|
|
Entering state 1
|
|
Reading a token: Now at end of input.
|
|
Shifting token END (1.1-1.1: <]]kind[[> for 'E' @ 1)
|
|
Entering state 2
|
|
Stack now 0 1 2
|
|
Cleanup: popping token END (1.1-1.1: <]]kind[[> for 'E' @ 1)
|
|
Cleanup: popping nterm start (1.1-1.1: <]]kind[[> for 'S' @ 1)
|
|
]])
|
|
|
|
m4_popdef([kind])
|
|
m4_popdef([not_kind])
|
|
])
|
|
|
|
_AT_CHECK_DEFAULT_PRINTER_AND_DESTRUCTOR_FOR_END_TOKEN(0)
|
|
_AT_CHECK_DEFAULT_PRINTER_AND_DESTRUCTOR_FOR_END_TOKEN(1)
|
|
|
|
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_DATA_GRAMMAR([[input.y]],
|
|
[[%debug
|
|
|
|
%{
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
static void yyerror (const char *msg);
|
|
static int yylex (void);
|
|
# define USE(SYM)
|
|
%}
|
|
|
|
%printer {
|
|
fprintf (yyoutput, "'%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'; }
|
|
;
|
|
|
|
%%
|
|
|
|
static int
|
|
yylex (void)
|
|
{
|
|
static char const input[] = "abd";
|
|
static size_t toknum;
|
|
if (! (toknum < sizeof input))
|
|
abort ();
|
|
yylval = input[toknum++];
|
|
return yylval;
|
|
}
|
|
|
|
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])
|
|
AT_COMPILE([input])
|
|
AT_PARSER_CHECK([./input], [1], [],
|
|
[[Starting parse
|
|
Entering state 0
|
|
Reading a token: Next token is token 'a' ('a')
|
|
Shifting token 'a' ('a')
|
|
Entering state 1
|
|
Reading a token: Next token is token 'b' ('b')
|
|
syntax error
|
|
Shifting token error ()
|
|
Entering state 3
|
|
Next token is token 'b' ('b')
|
|
Shifting token 'b' ('b')
|
|
Entering state 5
|
|
Reading a token: Next token is token $undefined ()
|
|
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
|
|
Next token is token $undefined ()
|
|
Error: discarding token $undefined ()
|
|
Error: popping token error ()
|
|
Stack now 0 1
|
|
Shifting token error ()
|
|
Entering state 3
|
|
Reading a token: Now at end of input.
|
|
Cleanup: discarding lookahead token $end ()
|
|
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 <field> $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_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)
|
|
%}
|
|
|
|
%printer {
|
|
char chr = $$;
|
|
fprintf (yyoutput, "'%c'", chr);
|
|
} <> <*>
|
|
%destructor {
|
|
char chr = $$;
|
|
fprintf (stderr, "DESTROY '%c'\n", chr);
|
|
} <> <*>
|
|
|
|
%union { char chr; }
|
|
%type <chr> start
|
|
|
|
%%
|
|
|
|
start: { USE($$); } ;
|
|
|
|
%%
|
|
|
|
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)
|
|
{
|
|
return yyparse ();
|
|
}
|
|
]])
|
|
|
|
AT_BISON_CHECK([-o input.c input.y])
|
|
AT_COMPILE([input])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
## ------------------------------------------------------ ##
|
|
## 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)
|
|
# 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
|