mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-11 13:23:04 +00:00
Bison directive from the Bison file to the invocation of this macro, so that these directives are passed to AT_BISON_OPTION_PUSHDEFS to get correct help macros. Use these helping macros (e.g., AT_LOC, AT_VAL and so forth). Move the AT_SETUP/AT_CLEANUP outside, to report as test title the extra Bison directives instead of the whole series. Change the grammar so that there are recoverable errors, and unrecoverable errors. Now we can have the parser give up before consuming the whole input. As a result we now can observe that the lookahead is freed when needed. Change the parser source to parse argv[1] instead of a hard coded string. Simplify yylex, and give a value and location to EOF. Simplify some invocations of AT_CHECK_PRINTER_AND_DESTRUCTOR that passed directives already coded in the file. Add some tests to check the location of "error". For some tests, the C++ parser is correct, and not yacc.c. For other tests, they provide different, but unsatisfying, values, so keep the C++ value so that at least one parser is "correct" according to the test suite. (Actions after errors): Remove, this is subsumed by the AT_CHECK_PRINTER_AND_DESTRUCTOR series.
478 lines
11 KiB
Plaintext
478 lines
11 KiB
Plaintext
# Executing Actions. -*- Autotest -*-
|
|
# Copyright (C) 2001, 2002, 2003, 2004 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 2, 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, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
# 02111-1307, USA.
|
|
|
|
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]],
|
|
[[%{
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
static void yyerror (const char *msg);
|
|
static int yylex (void);
|
|
# define YYDEBUG 1
|
|
# define YYERROR_VERBOSE 1
|
|
%}
|
|
%%
|
|
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 const char *input = "123456789";
|
|
return *input++;
|
|
}
|
|
|
|
static void
|
|
yyerror (const char *msg)
|
|
{
|
|
fprintf (stderr, "%s\n", msg);
|
|
}
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
return yyparse ();
|
|
}
|
|
]])
|
|
|
|
AT_CHECK([bison -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]],
|
|
[[%{
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
static void yyerror (const char *msg);
|
|
static int yylex (void);
|
|
# define YYDEBUG 1
|
|
# define YYERROR_VERBOSE 1
|
|
%}
|
|
|
|
%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
|
|
{
|
|
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)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
static void
|
|
yyerror (const char *msg)
|
|
{
|
|
fprintf (stderr, "%s\n", msg);
|
|
}
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
return yyparse ();
|
|
}
|
|
]])
|
|
|
|
AT_CHECK([bison -d -v -o input.c input.y])
|
|
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]],
|
|
[[%{
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
]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([typedef yy::Location YYLTYPE;
|
|
m4_ifval([$6], , [#define YYSTYPE int])])
|
|
[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])[
|
|
|
|
%printer
|
|
{
|
|
]AT_LALR1_CC_IF([cdebug_ << @$ << ": " << $$;],
|
|
[fprintf (yyoutput, "%d@%d-%d", $$, RANGE (@$))])[;
|
|
}
|
|
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'
|
|
|
|
%%
|
|
/*
|
|
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
|
|
EOF 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 *yysource = 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 (yysource[c])
|
|
printf ("sending: '%c'", yysource[c]);
|
|
else
|
|
printf ("sending: EOF");
|
|
printf (" (%d@%d-%d)\n", c, RANGE (]AT_LOC[));
|
|
return yysource[c];
|
|
}
|
|
|
|
]AT_LALR1_CC_IF(
|
|
[/* Currently, print_ is required in C++. */
|
|
void
|
|
yy::Parser::print_ ()
|
|
{
|
|
std::cerr << location;
|
|
}
|
|
|
|
/* A C++ error reporting function. */
|
|
void
|
|
yy::Parser::error_ ()
|
|
{
|
|
printf ("%d-%d: %s\n", RANGE (location), message.c_str());
|
|
}
|
|
|
|
static bool yydebug;
|
|
int
|
|
yyparse ()
|
|
{
|
|
yy::Parser parser (yydebug, yy::Location ());
|
|
return parser.parse ();
|
|
}
|
|
],
|
|
[static void
|
|
yyerror (const char *msg)
|
|
{
|
|
printf ("%d-%d: %s\n", RANGE (yylloc), msg);
|
|
}])[
|
|
|
|
int
|
|
main (int argc, const char *argv[])
|
|
{
|
|
yydebug = !!getenv ("YYDEBUG");
|
|
assert (argc == 2);
|
|
yysource = argv[1];
|
|
if (yyparse ())
|
|
{
|
|
printf ("Parsing FAILED.\n");
|
|
exit (1);
|
|
}
|
|
printf ("Successful parse.\n");
|
|
return 0;
|
|
}
|
|
]])
|
|
|
|
AT_LALR1_CC_IF(
|
|
[AT_CHECK([bison -o input.cc input.y])
|
|
AT_COMPILE_CXX([input])],
|
|
[AT_CHECK([bison -o input.c input.y])
|
|
AT_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: EOF (3@30-39)
|
|
input (0@0-29): /* Nothing */
|
|
input (2@0-29): line (0@0-29) input (0@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: EOF (3@30-39)
|
|
input (0@0-29): /* Nothing */
|
|
input (2@0-29): line (-1@0-29) input (0@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@100-129): /* Nothing */
|
|
input (2@100-129): line (10@100-129) input (0@100-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.
|
|
]])
|
|
|
|
])
|
|
|
|
|
|
# AT_CHECK_PRINTER_AND_DESTRUCTOR([BISON-OPTIONS], [UNION-FLAG])
|
|
# --------------------------------------------------------------
|
|
m4_define([AT_CHECK_PRINTER_AND_DESTRUCTOR],
|
|
[AT_SETUP([Printers and Destructors $2: $1])
|
|
|
|
_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])
|
|
|
|
# FIXME. These test cases fail.
|
|
#AT_CHECK_PRINTER_AND_DESTRUCTOR([%glr-parser])
|
|
#AT_CHECK_PRINTER_AND_DESTRUCTOR([%glr-parser], [with union])
|