mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 20:33:03 +00:00
full stderr, and strip it according to the bison options, instead of composing the error message from different bits. This makes it easier to check for several error messages. Adjust all the invocations. Add an invocation exercising the error token. Add an invocation demonstrating a stupid error message. (_AT_DATA_CALC_Y): Follow the GCS: initial column is 1, not 0. Adjust the tests. Error message are for stderr, not stdout.
442 lines
11 KiB
Plaintext
442 lines
11 KiB
Plaintext
# Checking the output filenames. -*- Autotest -*-
|
|
# Copyright 2000, 2001 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([[Simple Calculator.]])
|
|
|
|
## ---------------------------------------------------- ##
|
|
## Compile the grammar described in the documentation. ##
|
|
## ---------------------------------------------------- ##
|
|
|
|
|
|
# ------------------------- #
|
|
# Helping Autotest macros. #
|
|
# ------------------------- #
|
|
|
|
|
|
# _AT_DATA_CALC_Y($1, $2, $3, [CPP-DIRECTIVES])
|
|
# ---------------------------------------------
|
|
# Produce `calc.y'. Don't call this macro directly, because it contains
|
|
# some occurrences of `$1' etc. which will be interpreted by m4. So
|
|
# you should call it with $1, $2, and $3 as arguments, which is what
|
|
# AT_DATA_CALC_Y does.
|
|
m4_define([_AT_DATA_CALC_Y],
|
|
[m4_if([$1$2$3], $[1]$[2]$[3], [],
|
|
[m4_fatal([$0: Invalid arguments: $@])])dnl
|
|
AT_DATA([calc.y],
|
|
[[/* Infix notation calculator--calc */
|
|
|
|
%{
|
|
#include <config.h>
|
|
/* We don't need a perfect malloc for these tests. */
|
|
#undef malloc
|
|
#include <stdio.h>
|
|
|
|
#if STDC_HEADERS
|
|
# include <stdlib.h>
|
|
# include <string.h>
|
|
#else
|
|
char *strcat(char *dest, const char *src);
|
|
#endif
|
|
#include <ctype.h>
|
|
|
|
static int power (int base, int exponent);
|
|
static void yyerror (const char *s);
|
|
static int yylex (void);
|
|
static int yygetc (void);
|
|
static void yyungetc (int c);
|
|
|
|
extern void perror (const char *s);
|
|
%}
|
|
|
|
/* Also exercise %union. */
|
|
%union
|
|
{
|
|
int ival; /* A comment to exercise an old bug. */
|
|
};
|
|
|
|
/* Bison Declarations */
|
|
%token CALC_EOF 0 "end of file"
|
|
%token <ival> NUM "number"
|
|
%type <ival> exp
|
|
|
|
%nonassoc '=' /* comparison */
|
|
%left '-' '+'
|
|
%left '*' '/'
|
|
%left NEG /* negation--unary minus */
|
|
%right '^' /* exponentiation */
|
|
|
|
]$4[
|
|
|
|
/* Grammar follows */
|
|
%%
|
|
input:
|
|
line
|
|
| input line
|
|
;
|
|
|
|
line:
|
|
'\n'
|
|
| exp '\n' {}
|
|
;
|
|
|
|
exp:
|
|
NUM { $$ = $1; }
|
|
| exp '=' exp
|
|
{
|
|
if ($1 != $3)
|
|
fprintf (stderr, "calc: error: %d != %d\n", $1, $3);
|
|
$$ = $1 == $3;
|
|
}
|
|
| exp '+' exp { $$ = $1 + $3; }
|
|
| exp '-' exp { $$ = $1 - $3; }
|
|
| exp '*' exp { $$ = $1 * $3; }
|
|
| exp '/' exp { $$ = $1 / $3; }
|
|
| '-' exp %prec NEG { $$ = -$2; }
|
|
| exp '^' exp { $$ = power ($1, $3); }
|
|
| '(' exp ')' { $$ = $2; }
|
|
| '(' error ')' { $$ = 0; }
|
|
;
|
|
%%
|
|
/* The input. */
|
|
FILE *yyin;
|
|
|
|
static void
|
|
yyerror (const char *s)
|
|
{
|
|
#if YYLSP_NEEDED
|
|
fprintf (stderr, "%d.%d-%d.%d: ",
|
|
yylloc.first_line, yylloc.first_column,
|
|
yylloc.last_line, yylloc.last_column);
|
|
#endif
|
|
fprintf (stderr, "%s\n", s);
|
|
}
|
|
|
|
|
|
#if YYLSP_NEEDED
|
|
static YYLTYPE last_yylloc;
|
|
#endif
|
|
static int
|
|
yygetc (void)
|
|
{
|
|
int res = getc (yyin);
|
|
#if YYLSP_NEEDED
|
|
last_yylloc = yylloc;
|
|
if (res == '\n')
|
|
{
|
|
yylloc.last_line++;
|
|
yylloc.last_column = 1;
|
|
}
|
|
else
|
|
yylloc.last_column++;
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
|
|
static void
|
|
yyungetc (int c)
|
|
{
|
|
#if YYLSP_NEEDED
|
|
/* Wrong when C == `\n'. */
|
|
yylloc = last_yylloc;
|
|
#endif
|
|
ungetc (c, yyin);
|
|
}
|
|
|
|
static int
|
|
read_signed_integer (void)
|
|
{
|
|
int c = yygetc ();
|
|
int sign = 1;
|
|
int n = 0;
|
|
|
|
if (c == '-')
|
|
{
|
|
c = yygetc ();
|
|
sign = -1;
|
|
}
|
|
|
|
while (isdigit (c))
|
|
{
|
|
n = 10 * n + (c - '0');
|
|
c = yygetc ();
|
|
}
|
|
|
|
yyungetc (c);
|
|
|
|
return sign * n;
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------.
|
|
| Lexical analyzer returns an integer on the stack and the token |
|
|
| NUM, or the ASCII character read if not a number. Skips all |
|
|
| blanks and tabs, returns 0 for EOF. |
|
|
`---------------------------------------------------------------*/
|
|
|
|
static int
|
|
yylex (void)
|
|
{
|
|
int c;
|
|
|
|
#if YYLSP_NEEDED
|
|
yylloc.first_column = yylloc.last_column;
|
|
yylloc.first_line = yylloc.last_line;
|
|
#endif
|
|
|
|
/* Skip white space. */
|
|
while ((c = yygetc ()) == ' ' || c == '\t')
|
|
{
|
|
#if YYLSP_NEEDED
|
|
yylloc.first_column = yylloc.last_column;
|
|
yylloc.first_line = yylloc.last_line;
|
|
#endif
|
|
}
|
|
|
|
/* process numbers */
|
|
if (c == '.' || isdigit (c))
|
|
{
|
|
yyungetc (c);
|
|
yylval.ival = read_signed_integer ();
|
|
return NUM;
|
|
}
|
|
|
|
/* Return end-of-file. */
|
|
if (c == EOF)
|
|
return CALC_EOF;
|
|
|
|
/* Return single chars. */
|
|
return c;
|
|
}
|
|
|
|
static int
|
|
power (int base, int exponent)
|
|
{
|
|
int res = 1;
|
|
if (exponent < 0)
|
|
exit (1);
|
|
for (/* Niente */; exponent; --exponent)
|
|
res *= base;
|
|
return res;
|
|
}
|
|
|
|
int
|
|
main (int argc, const char **argv)
|
|
{
|
|
yyin = NULL;
|
|
|
|
if (argc == 2)
|
|
yyin = fopen (argv[1], "r");
|
|
else
|
|
yyin = stdin;
|
|
|
|
if (!yyin)
|
|
{
|
|
perror (argv[1]);
|
|
exit (1);
|
|
}
|
|
|
|
#if YYDEBUG
|
|
yydebug = 1;
|
|
#endif
|
|
#if YYLSP_NEEDED
|
|
yylloc.last_column = 1;
|
|
yylloc.last_line = 1;
|
|
#endif
|
|
yyparse ();
|
|
return 0;
|
|
}
|
|
]])
|
|
])# _AT_DATA_CALC_Y
|
|
|
|
|
|
# AT_DATA_CALC_Y([BISON-OPTIONS])
|
|
# -------------------------------
|
|
# Produce `calc.y'.
|
|
m4_define([AT_DATA_CALC_Y],
|
|
[_AT_DATA_CALC_Y($[1], $[2], $[3],
|
|
[m4_bmatch([$1], [--yyerror-verbose],
|
|
[[%error-verbose]])])])
|
|
|
|
|
|
|
|
# _AT_CHECK_CALC(BISON-OPTIONS, INPUT, [NUM-STDERR-LINES = 0])
|
|
# ------------------------------------------------------------
|
|
# Run `calc' on INPUT and expect no STDOUT nor STDERR.
|
|
#
|
|
# If BISON-OPTIONS contains `--debug', then NUM-STDERR-LINES is the number
|
|
# of expected lines on stderr.
|
|
m4_define([_AT_CHECK_CALC],
|
|
[AT_DATA([[input]],
|
|
[[$2
|
|
]])
|
|
AT_CHECK([./calc input], 0, [], [stderr])dnl
|
|
AT_CHECK([wc -l <stderr | sed 's/[[^0-9]]//g'], 0,
|
|
[m4_bmatch([$1], [--debug],
|
|
[$3], [0])
|
|
])
|
|
])
|
|
|
|
|
|
# _AT_CHECK_CALC_ERROR(BISON-OPTIONS, INPUT, [NUM-DEBUG-LINES],
|
|
# [ERROR-LOCATION], [IF-YYERROR-VERBOSE])
|
|
# ------------------------------------------------------------
|
|
# Run `calc' on INPUT, and expect a `parse error' message.
|
|
#
|
|
# If INPUT starts with a slash, it is used as absolute input file name,
|
|
# otherwise as contents.
|
|
#
|
|
# If BISON-OPTIONS contains `--location', then make sure the ERROR-LOCATION
|
|
# is correctly output on stderr.
|
|
#
|
|
# If BISON-OPTIONS contains `--yyerror-verbose', then make sure the
|
|
# IF-YYERROR-VERBOSE message is properly output after `parse error, '
|
|
# on STDERR.
|
|
#
|
|
# If BISON-OPTIONS contains `--debug', then NUM-STDERR-LINES is the number
|
|
# of expected lines on stderr.
|
|
m4_define([_AT_CHECK_CALC_ERROR],
|
|
[m4_bmatch([$2], [^/],
|
|
[AT_CHECK([./calc $2], 0, [], [stderr])],
|
|
[AT_DATA([[input]],
|
|
[[$2
|
|
]])
|
|
AT_CHECK([./calc input], 0, [], [stderr])])
|
|
|
|
m4_bmatch([$1], [--debug],
|
|
[AT_CHECK([wc -l <stderr | sed 's/[[^0-9]]//g'], 0, [$3
|
|
])])
|
|
|
|
# Normalize the observed and expected error messages, depending upon the
|
|
# options.
|
|
# 1. Remove the traces from observed.
|
|
egrep -v '^((Start|Enter|Read|Reduc|Shift)ing|state|Error:|Next|Discarding) ' stderr >at-stderr
|
|
mv at-stderr stderr
|
|
# 2. Create the reference error message.
|
|
AT_DATA([[expout]],
|
|
[$4
|
|
])
|
|
# 3. If locations are not used, remove them.
|
|
m4_bmatch([$1], [--location], [],
|
|
[[sed 's/^[-0-9.]*: //' expout >at-expout
|
|
mv at-expout expout]])
|
|
# 4. If error-verbose is not used, strip the`, unexpected....' part.
|
|
m4_bmatch([$1], [--yyerror-verbose], [],
|
|
[[sed 's/parse error, .*$/parse error/' expout >at-expout
|
|
mv at-expout expout]])
|
|
# 5. Check
|
|
AT_CHECK([cat stderr], 0, [expout])
|
|
])
|
|
|
|
|
|
# AT_CHECK_CALC([BISON-OPTIONS], [PARSER-EXPECTED-STDERR])
|
|
# --------------------------------------------------------
|
|
# Start a testing chunk which compiles `calc' grammar with
|
|
# BISON-OPTIONS, and performs several tests over the parser.
|
|
m4_define([AT_CHECK_CALC],
|
|
[# We use integers to avoid dependencies upon the precision of doubles.
|
|
AT_SETUP([Calculator $1])
|
|
|
|
AT_DATA_CALC_Y([$1])
|
|
|
|
# Specify the output files to avoid problems on different file systems.
|
|
AT_CHECK([bison calc.y -o calc.c m4_bpatsubst([$1], [--yyerror-verbose])],
|
|
[0], [], [])
|
|
|
|
AT_CHECK([$CC $CFLAGS $CPPFLAGS calc.c -o calc], 0, [], [ignore])
|
|
|
|
# Test the priorities.
|
|
_AT_CHECK_CALC([$1],
|
|
[1 + 2 * 3 = 7
|
|
1 + 2 * -3 = -5
|
|
|
|
-1^2 = -1
|
|
(-1)^2 = 1
|
|
|
|
---1 = -1
|
|
|
|
1 - 2 - 3 = -4
|
|
1 - (2 - 3) = 2
|
|
|
|
2^2^3 = 256
|
|
(2^2)^3 = 64], [486])
|
|
|
|
# Some parse errors.
|
|
_AT_CHECK_CALC_ERROR([$1], [0 0], [10],
|
|
[1.3-1.4: parse error, unexpected "number"])
|
|
_AT_CHECK_CALC_ERROR([$1], [1//2], [13],
|
|
[1.3-1.4: parse error, unexpected '/', expecting "number" or '-' or '('])
|
|
_AT_CHECK_CALC_ERROR([$1], [error], [4],
|
|
[1.1-1.2: parse error, unexpected $undefined., expecting "number" or '-' or '\n' or '('])
|
|
_AT_CHECK_CALC_ERROR([$1], [1 = 2 = 3], [19],
|
|
[1.7-1.8: parse error, unexpected '='])
|
|
_AT_CHECK_CALC_ERROR([$1],
|
|
[
|
|
+1],
|
|
[13],
|
|
[2.1-2.2: parse error, unexpected '+'])
|
|
# Exercise error messages with EOF: work on an empty file.
|
|
_AT_CHECK_CALC_ERROR([$1],
|
|
[/dev/null],
|
|
[4],
|
|
[1.1-1.2: parse error, unexpected "end of file", expecting "number" or '-' or '\n' or '('])
|
|
|
|
# Exercise the error token: without it, we die at the first error,
|
|
# hence be sure i. to have several errors, ii. to test the action
|
|
# associated to `error'.
|
|
_AT_CHECK_CALC_ERROR([$1],
|
|
[(1 ++ 2) + (0 0) = 1],
|
|
[76],
|
|
[1.5-1.6: parse error, unexpected '+', expecting "number" or '-' or '('
|
|
1.15-1.16: parse error, unexpected "number"
|
|
calc: error: 0 != 1])
|
|
|
|
# Add a studid example demonstrating that Bison can further improve the
|
|
# error message. FIXME: Fix this ridiculous message.
|
|
_AT_CHECK_CALC_ERROR([$1],
|
|
[()],
|
|
[21],
|
|
[1.2-1.3: parse error, unexpected ')', expecting error or "number" or '-' or '('])
|
|
|
|
AT_CLEANUP
|
|
])# AT_CHECK_CALC
|
|
|
|
|
|
|
|
|
|
# ------------------ #
|
|
# Test the parsers. #
|
|
# ------------------ #
|
|
|
|
AT_CHECK_CALC()
|
|
|
|
AT_CHECK_CALC([--defines])
|
|
AT_CHECK_CALC([--locations])
|
|
AT_CHECK_CALC([--name-prefix=calc])
|
|
AT_CHECK_CALC([--verbose])
|
|
AT_CHECK_CALC([--yacc])
|
|
AT_CHECK_CALC([--yyerror-verbose])
|
|
|
|
AT_CHECK_CALC([--locations --yyerror-verbose])
|
|
|
|
AT_CHECK_CALC([--defines --locations --name-prefix=calc --verbose --yacc --yyerror-verbose])
|
|
|
|
AT_CHECK_CALC([--debug])
|
|
AT_CHECK_CALC([--debug --defines --locations --name-prefix=calc --verbose --yacc --yyerror-verbose])
|