# Exercising Bison on conflicts. -*- Autotest -*-
-# Copyright (C) 2002, 2003, 2004, 2005, 2007, 2009, 2010 Free
-# Software Foundation, Inc.
+# Copyright (C) 2002-2005, 2007-2012 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
]])
AT_BISON_CHECK([-o input.c input.y], 0, [],
-[[input.y:4.9: warning: rule useless in parser due to conflicts: e: /* empty */
+[[input.y:4.9: warning: rule useless in parser due to conflicts: e: /* empty */ [-Wother]
+]])
+
+AT_BISON_CHECK([-fcaret -o input.c input.y], 0, [],
+[[input.y:4.9: warning: rule useless in parser due to conflicts [-Wother]
+ e: 'e' | /* Nothing. */;
+ ^
]])
AT_CLEANUP
AT_SETUP([%nonassoc and eof])
+AT_BISON_OPTION_PUSHDEFS
AT_DATA_GRAMMAR([input.y],
[[
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
#define YYERROR_VERBOSE 1
-static void
-yyerror (const char *msg)
-{
- fprintf (stderr, "%s\n", msg);
-}
-
+]AT_YYERROR_DEFINE[
/* The current argument. */
static const char *input;
yylex (void)
{
static size_t toknum;
- if (! (toknum <= strlen (input)))
- abort ();
+ assert (toknum <= strlen (input));
return input[toknum++];
}
return yyparse ();
}
]])
+AT_BISON_OPTION_POPDEFS
m4_pushdef([AT_NONASSOC_AND_EOF_CHECK],
[AT_BISON_CHECK([$1[ -o input.c input.y]])
# We must disable default reductions in inconsistent states in order to
# have an explicit list of all expected tokens.
-AT_NONASSOC_AND_EOF_CHECK([[-Dlr.default-reductions=consistent]],
+AT_NONASSOC_AND_EOF_CHECK([[-Dlr.default-reduction=consistent]],
[[correct]])
-# lr.default-reductions=consistent happens to work for this test case.
+# lr.default-reduction=consistent happens to work for this test case.
# However, for other grammars, lookahead sets can be merged for
# different left contexts, so it is still possible to have an incorrect
# expected list. Canonical LR is almost a general solution (that is, it
-## -------------------------------------- ##
-## %error-verbose and consistent errors. ##
-## -------------------------------------- ##
+## ------------------------------------------- ##
+## parse.error=verbose and consistent errors. ##
+## ------------------------------------------- ##
-AT_SETUP([[%error-verbose and consistent errors]])
+AT_SETUP([[parse.error=verbose and consistent errors]])
m4_pushdef([AT_CONSISTENT_ERRORS_CHECK], [
}]], [[
%code {]AT_SKEL_CC_IF([[
- #include <cassert>
#include <string>]], [[
#include <assert.h>
#include <stdio.h>
- void yyerror (char const *msg);]])[
+ ]AT_YYERROR_DECLARE])[
]AT_YYLEX_PROTOTYPE[;
#define USE(Var)
}
]$1[
-%error-verbose
+%define parse.error verbose
%%
*lvalp = 1;
return *input++;
}]])[
-
-/*----------.
-| yyerror. |
-`----------*/]AT_SKEL_JAVA_IF([[
-
-public void yyerror (String msg)
-{
- System.err.println (msg);
-}
-
+]AT_YYERROR_DEFINE[
+]AT_SKEL_JAVA_IF([[
};
-%%]], [AT_SKEL_CC_IF([[
-
-void
-yy::parser::error (const yy::location &, std::string const &msg)
-{
- std::cerr << msg << std::endl;
-}]], [[
-
-void
-yyerror (char const *msg)
-{
- fprintf (stderr, "%s\n", msg);
-}]])])[
+%%]])[
/*-------.
| main. |
# Even canonical LR doesn't foresee the error for 'a'!
AT_CONSISTENT_ERRORS_CHECK([[%define lr.type ielr
- %define lr.default-reductions consistent]],
+ %define lr.default-reduction consistent]],
[AT_PREVIOUS_STATE_GRAMMAR],
[AT_PREVIOUS_STATE_INPUT],
[[$end]], [[ab]])
AT_CONSISTENT_ERRORS_CHECK([[%define lr.type ielr
- %define lr.default-reductions accepting]],
+ %define lr.default-reduction accepting]],
[AT_PREVIOUS_STATE_GRAMMAR],
[AT_PREVIOUS_STATE_INPUT],
[[$end]], [[ab]])
;
consistent-reduction: /*empty*/ {
- assert (yychar == ]AT_SKEL_CC_IF([[yyempty_]], [[YYEMPTY]])[);
+ assert (yychar == YYEMPTY);
yylval = 0;
yychar = 'b';
} ;
[AT_USER_ACTION_GRAMMAR],
[AT_USER_ACTION_INPUT],
[['b']], [[none]])
-AT_CONSISTENT_ERRORS_CHECK([[%language "c++"]],
- [AT_USER_ACTION_GRAMMAR],
- [AT_USER_ACTION_INPUT],
- [['b']], [[none]])
-# No Java test because yychar cannot be manipulated by users.
+# No C++ or Java test because yychar cannot be manipulated by users.
-AT_CONSISTENT_ERRORS_CHECK([[%define lr.default-reductions consistent]],
+AT_CONSISTENT_ERRORS_CHECK([[%define lr.default-reduction consistent]],
[AT_USER_ACTION_GRAMMAR],
[AT_USER_ACTION_INPUT],
[['b']], [[none]])
# Canonical LR doesn't foresee the error for 'a'!
-AT_CONSISTENT_ERRORS_CHECK([[%define lr.default-reductions accepting]],
+AT_CONSISTENT_ERRORS_CHECK([[%define lr.default-reduction accepting]],
[AT_USER_ACTION_GRAMMAR],
[AT_USER_ACTION_INPUT],
[[$end]], [[a]])
[AT_USER_ACTION_INPUT],
[['b']], [[none]])
AT_CONSISTENT_ERRORS_CHECK([[%define parse.lac full
- %define lr.default-reductions accepting]],
+ %define lr.default-reduction accepting]],
[AT_USER_ACTION_GRAMMAR],
[AT_USER_ACTION_INPUT],
[[$end]], [[none]])
# with minimal LR parser tables.
AT_SETUP([[LAC: %nonassoc requires splitting canonical LR states]])
-
+AT_BISON_OPTION_PUSHDEFS
AT_DATA_GRAMMAR([[input.y]],
[[%code {
#include <stdio.h>
- void yyerror (char const *);
- int yylex (void);
+ ]AT_YYERROR_DECLARE[
+ ]AT_YYLEX_DECLARE[
}
%error-verbose
reduce-nonassoc: %prec 'a';
%%
-
-void
-yyerror (char const *msg)
-{
- fprintf (stderr, "%s\n", msg);
-}
-
-int
-yylex (void)
-{
- char const *input = "aaa";
- return *input++;
-}
+]AT_YYERROR_DEFINE[
+]AT_YYLEX_DEFINE(["aaa"])[
int
main (void)
return yyparse ();
}
]])
+AT_BISON_OPTION_POPDEFS
# Show canonical LR's failure.
AT_BISON_CHECK([[-Dlr.type=canonical-lr -o input.c input.y]],
[[0]], [[]],
-[[input.y: conflicts: 2 shift/reduce
+[[input.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
]])
AT_COMPILE([[input]])
AT_PARSER_CHECK([[./input]], [[1]], [[]],
# It's corrected by LAC.
AT_BISON_CHECK([[-Dlr.type=canonical-lr -Dparse.lac=full \
-o input.c input.y]], [[0]], [[]],
-[[input.y: conflicts: 2 shift/reduce
+[[input.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
]])
AT_COMPILE([[input]])
AT_PARSER_CHECK([[./input]], [[1]], [[]],
# IELR is sufficient when LAC is used.
AT_BISON_CHECK([[-Dlr.type=ielr -Dparse.lac=full -o input.c input.y]],
[[0]], [[]],
-[[input.y: conflicts: 2 shift/reduce
+[[input.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
]])
AT_COMPILE([[input]])
AT_PARSER_CHECK([[./input]], [[1]], [[]],
]])
AT_BISON_CHECK([-o input.c --report=all input.y], 0, [],
-[input.y: conflicts: 1 shift/reduce
-])
+[[input.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+]])
# Check the contents of the report.
AT_CHECK([cat input.output], [],
on left: 1 2, on right: 0 1
-state 0
+State 0
0 $accept: . exp $end
1 exp: . exp OP exp
exp go to state 2
-state 1
+State 1
2 exp: NUM .
$default reduce using rule 2 (exp)
-state 2
+State 2
0 $accept: exp . $end
1 exp: exp . OP exp
OP shift, and go to state 4
-state 3
+State 3
0 $accept: exp $end .
$default accept
-state 4
+State 4
1 exp: . exp OP exp
1 | exp OP . exp
exp go to state 5
-state 5
+State 5
1 exp: exp . OP exp
1 | exp OP exp . [$end, OP]
on left: 1 2, on right: 0 1
-state 0
+State 0
0 $accept: . exp $end
1 exp: . exp OP exp
exp go to state 2
-state 1
+State 1
2 exp: NUM .
$default reduce using rule 2 (exp)
-state 2
+State 2
0 $accept: exp . $end
1 exp: exp . OP exp
OP shift, and go to state 4
-state 3
+State 3
0 $accept: exp $end .
$default accept
-state 4
+State 4
1 exp: . exp OP exp
1 | exp OP . exp
exp go to state 5
-state 5
+State 5
1 exp: exp . OP exp
1 | exp OP exp . [$end, OP]
AT_CLEANUP
+## ---------------------- ##
+## %precedence suffices. ##
+## ---------------------- ##
+
+AT_SETUP([%precedence suffices])
+
+AT_DATA([input.y],
+[[%precedence "then"
+%precedence "else"
+%%
+stmt:
+ "if" cond "then" stmt
+| "if" cond "then" stmt "else" stmt
+| "stmt"
+;
+
+cond:
+ "exp"
+;
+]])
+
+AT_BISON_CHECK([-o input.c input.y])
+
+AT_CLEANUP
+
+
+## ------------------------------ ##
+## %precedence does not suffice. ##
+## ------------------------------ ##
+
+AT_SETUP([%precedence does not suffice])
+
+AT_DATA([input.y],
+[[%precedence "then"
+%precedence "else"
+%%
+stmt:
+ "if" cond "then" stmt
+| "if" cond "then" stmt "else" stmt
+| "stmt"
+;
+
+cond:
+ "exp"
+| cond "then" cond
+;
+]])
+
+AT_BISON_CHECK([-o input.c input.y], 0, [],
+[[input.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+input.y:12.3-18: warning: rule useless in parser due to conflicts: cond: cond "then" cond [-Wother]
+]])
+
+AT_CLEANUP
+
+
## -------------------------------- ##
## Defaulted Conflicted Reduction. ##
## -------------------------------- ##
]])
AT_BISON_CHECK([-o input.c --report=all input.y], 0, [],
-[[input.y: conflicts: 1 reduce/reduce
-input.y:4.6-8: warning: rule useless in parser due to conflicts: id: '0'
+[[input.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
+input.y:4.6-8: warning: rule useless in parser due to conflicts: id: '0' [-Wother]
]])
# Check the contents of the report.
on left: 4, on right: 2
-state 0
+State 0
0 $accept: . exp $end
1 exp: . num
id go to state 4
-state 1
+State 1
3 num: '0' . [$end]
4 id: '0' . [$end]
$default reduce using rule 3 (num)
-state 2
+State 2
0 $accept: exp . $end
$end shift, and go to state 5
-state 3
+State 3
1 exp: num .
$default reduce using rule 1 (exp)
-state 4
+State 4
2 exp: id .
$default reduce using rule 2 (exp)
-state 5
+State 5
0 $accept: exp $end .
]])
AT_BISON_CHECK([-o input.c input.y], 1, [],
-[input.y: conflicts: 1 shift/reduce
-input.y: expected 0 shift/reduce conflicts
-])
+[[input.y: error: shift/reduce conflicts: 1 found, 0 expected
+]])
AT_CLEANUP
]])
AT_BISON_CHECK([-o input.c input.y], 1, [],
-[input.y: conflicts: 1 shift/reduce
-input.y: expected 2 shift/reduce conflicts
-])
+[[input.y: error: shift/reduce conflicts: 1 found, 2 expected
+]])
AT_CLEANUP
]])
AT_BISON_CHECK([-o input.c input.y], 1, [],
-[input.y: conflicts: 1 reduce/reduce
-input.y: expected 0 reduce/reduce conflicts
-])
+[[input.y: error: reduce/reduce conflicts: 1 found, 0 expected
+]])
AT_CLEANUP
]])
AT_BISON_CHECK([-o input.c input.y], 0, [],
-[[input.y: conflicts: 4 shift/reduce
+[[input.y: warning: 4 shift/reduce conflicts [-Wconflicts-sr]
]])
AT_CLEANUP
]])
AT_BISON_CHECK([[--report=all input.y]], 0, [],
-[[input.y: conflicts: 1 shift/reduce, 1 reduce/reduce
-input.y:12.5-20: warning: rule useless in parser due to conflicts: resolved_conflict: 'a' unreachable1
-input.y:20.5-20: warning: rule useless in parser due to conflicts: unreachable1: 'a' unreachable2
-input.y:21.4: warning: rule useless in parser due to conflicts: unreachable1: /* empty */
-input.y:25.13: warning: rule useless in parser due to conflicts: unreachable2: /* empty */
-input.y:25.16: warning: rule useless in parser due to conflicts: unreachable2: /* empty */
-input.y:31.5-7: warning: rule useless in parser due to conflicts: reported_conflicts: 'a'
-input.y:32.4: warning: rule useless in parser due to conflicts: reported_conflicts: /* empty */
+[[input.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+input.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
+input.y:12.5-20: warning: rule useless in parser due to conflicts: resolved_conflict: 'a' unreachable1 [-Wother]
+input.y:20.5-20: warning: rule useless in parser due to conflicts: unreachable1: 'a' unreachable2 [-Wother]
+input.y:21.4: warning: rule useless in parser due to conflicts: unreachable1: /* empty */ [-Wother]
+input.y:25.13: warning: rule useless in parser due to conflicts: unreachable2: /* empty */ [-Wother]
+input.y:25.16: warning: rule useless in parser due to conflicts: unreachable2: /* empty */ [-Wother]
+input.y:31.5-7: warning: rule useless in parser due to conflicts: reported_conflicts: 'a' [-Wother]
+input.y:32.4: warning: rule useless in parser due to conflicts: reported_conflicts: /* empty */ [-Wother]
]])
AT_CHECK([[cat input.output]], 0,
on left: 8 9 10, on right: 1
-state 0
+State 0
0 $accept: . start $end
1 start: . resolved_conflict 'a' reported_conflicts 'a'
Conflict between rule 3 and token 'a' resolved as reduce (%left 'a').
-state 1
+State 1
0 $accept: start . $end
$end shift, and go to state 3
-state 2
+State 2
1 start: resolved_conflict . 'a' reported_conflicts 'a'
'a' shift, and go to state 4
-state 3
+State 3
0 $accept: start $end .
$default accept
-state 4
+State 4
1 start: resolved_conflict 'a' . reported_conflicts 'a'
8 reported_conflicts: . 'a'
reported_conflicts go to state 6
-state 5
+State 5
8 reported_conflicts: 'a' . ['a']
9 | 'a' . ['a']
$default reduce using rule 8 (reported_conflicts)
-state 6
+State 6
1 start: resolved_conflict 'a' reported_conflicts . 'a'
'a' shift, and go to state 7
-state 7
+State 7
1 start: resolved_conflict 'a' reported_conflicts 'a' .
]])
AT_DATA([[input-keep.y]],
-[[%define lr.keep-unreachable-states
+[[%define lr.keep-unreachable-state
]])
AT_CHECK([[cat input.y >> input-keep.y]])
AT_BISON_CHECK([[input-keep.y]], 0, [],
-[[input-keep.y: conflicts: 2 shift/reduce, 2 reduce/reduce
-input-keep.y:22.4: warning: rule useless in parser due to conflicts: unreachable1: /* empty */
-input-keep.y:26.16: warning: rule useless in parser due to conflicts: unreachable2: /* empty */
-input-keep.y:32.5-7: warning: rule useless in parser due to conflicts: reported_conflicts: 'a'
-input-keep.y:33.4: warning: rule useless in parser due to conflicts: reported_conflicts: /* empty */
+[[input-keep.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
+input-keep.y: warning: 2 reduce/reduce conflicts [-Wconflicts-rr]
+input-keep.y:22.4: warning: rule useless in parser due to conflicts: unreachable1: /* empty */ [-Wother]
+input-keep.y:26.16: warning: rule useless in parser due to conflicts: unreachable2: /* empty */ [-Wother]
+input-keep.y:32.5-7: warning: rule useless in parser due to conflicts: reported_conflicts: 'a' [-Wother]
+input-keep.y:33.4: warning: rule useless in parser due to conflicts: reported_conflicts: /* empty */ [-Wother]
]])
AT_CLEANUP
empty_c3: %prec 'd' ;
]])
AT_BISON_CHECK([[--report=all -o input.c input.y]], 0, [], [ignore])
-AT_CHECK([[cat input.output | sed -n '/^state 0$/,/^state 1$/p']], 0,
-[[state 0
+AT_CHECK([[cat input.output | sed -n '/^State 0$/,/^State 1$/p']], 0,
+[[State 0
0 $accept: . start $end
1 start: . 'a'
13 empty_c3: . ['c']
'b' shift, and go to state 1
-
+
'c' reduce using rule 13 (empty_c3)
$default reduce using rule 9 (empty_a)
Conflict between rule 13 and token 'c' resolved as reduce ('c' < 'd').
-state 1
+State 1
]])
AT_CLEANUP
]])
AT_BISON_CHECK([[--report=all -o input.c input.y]], 0, [], [ignore])
-AT_CHECK([[cat input.output | sed -n '/^state 0$/,/^state 1$/p']], 0,
-[[state 0
+AT_CHECK([[cat input.output | sed -n '/^State 0$/,/^State 1$/p']], 0,
+[[State 0
0 $accept: . start $end
1 start: . 'a'
Conflict between rule 11 and token 'c' resolved as an error (%nonassoc 'c').
-state 1
+State 1
+]])
+AT_CLEANUP
+
+
+## -------------------- ##
+## %expect-rr non GLR. ##
+## -------------------- ##
+
+AT_SETUP([[%expect-rr non GLR]])
+
+AT_DATA([[1.y]],
+[[%expect-rr 0
+%%
+exp: 'a'
+]])
+
+AT_BISON_CHECK([[1.y]], [[0]], [],
+[[1.y: warning: %expect-rr applies only to GLR parsers [-Wother]
+]])
+
+AT_DATA([[2.y]],
+[[%expect-rr 1
+%%
+exp: 'a' | 'a';
+]])
+
+AT_BISON_CHECK([[2.y]], [[0]], [],
+[[2.y: warning: %expect-rr applies only to GLR parsers [-Wother]
+2.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
+2.y:3.12-14: warning: rule useless in parser due to conflicts: exp: 'a' [-Wother]
+]])
+
+AT_CLEANUP
+
+
+## ---------------------------------- ##
+## -W versus %expect and %expect-rr. ##
+## ---------------------------------- ##
+
+AT_SETUP([[-W versus %expect and %expect-rr]])
+
+AT_DATA([[sr-rr.y]],
+[[%glr-parser
+%%
+start: 'a' | A 'a' | B 'a' ;
+A: ;
+B: ;
+]])
+AT_DATA([[sr.y]],
+[[%glr-parser
+%%
+start: 'a' | A 'a' ;
+A: ;
+]])
+AT_DATA([[rr.y]],
+[[%glr-parser
+%%
+start: A | B ;
+A: ;
+B: ;
]])
+
+AT_BISON_CHECK([[sr-rr.y]], [[0]], [[]],
+[[sr-rr.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+sr-rr.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
+]])
+AT_BISON_CHECK([[-Wno-conflicts-sr sr-rr.y]], [[0]], [[]],
+[[sr-rr.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
+]])
+AT_BISON_CHECK([[-Wno-conflicts-rr sr-rr.y]], [[0]], [[]],
+[[sr-rr.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+]])
+
+[
+# This is piece of code is rather complex for a simple task: try every
+# combinaison of (0 or 1 real SR) x (0 or 1 real RR) x (don't %expect
+# or %expect 0, 1, or 2 SR) x (don't %expect-rr or %expect-rr 0, 1, or 2
+# RR).
+
+# Number and types of genuine conflicts in the grammar.
+for gram in sr-rr sr rr; do
+ # Number of expected s/r conflicts.
+ for sr_exp_i in '' 0 1 2; do
+ # Number of expected r/r conflicts.
+ for rr_exp_i in '' 0 1 2; do
+ test -z "$sr_exp_i" && test -z "$rr_exp_i" && continue
+
+ # Build grammar file.
+ sr_exp=0
+ rr_exp=0
+ file=$gram
+ directives=
+ if test -n "$sr_exp_i"; then
+ sr_exp=$sr_exp_i
+ file=$file-expect-$sr_exp
+ directives="%expect $sr_exp"
+ fi
+ if test -n "$rr_exp_i"; then
+ rr_exp=$rr_exp_i
+ file=$file-expect-rr-$rr_exp
+ directives="$directives %expect-rr $rr_exp"
+ fi
+ file=$file.y
+ echo "$directives" > $file
+ cat $gram.y >> $file
+
+ # Number of found conflicts.
+ case $gram in
+ (sr) sr_count=1; rr_count=0;;
+ (rr) sr_count=0; rr_count=1;;
+ (sr-rr) sr_count=1; rr_count=1;;
+ esac
+
+ # Update number of expected conflicts: if %expect is given then
+ # %expect-rr defaults to 0, and vice-versa. Leave empty if
+ # nothing expected.
+ case $sr_exp_i:$rr_exp_i in
+ ?:) rr_exp_i=0;;
+ :?) sr_exp_i=0;;
+ esac
+
+ # Run tests.
+ if test $sr_count -eq $sr_exp && test $rr_count -eq $rr_exp; then
+ ]AT_BISON_CHECK([[-Wnone $file]])[
+ ]AT_BISON_CHECK([[-Werror $file]])[
+ else
+ {
+ if test -z "$sr_exp_i" && test "$sr_count" -ne 0; then
+ echo "warning: $sr_count shift/reduce conflicts"
+ elif test "$sr_exp_i" -ne "$sr_count"; then
+ echo "error: shift/reduce conflicts: $sr_count found, $sr_exp_i expected"
+ fi
+ if test -z "$rr_exp_i" && test "$rr_count" -ne 0; then
+ echo "warning: $rr_count reduce/reduce conflicts"
+ elif test "$rr_exp_i" -ne "$rr_count"; then
+ echo "error: reduce/reduce conflicts: $rr_count found, $rr_exp_i expected"
+ fi
+ } | sed -e "s/^/$file: /" > experr
+ ]AT_BISON_CHECK([[-Wnone $file]], [[1]], [[]], [[experr]])[
+ ]AT_BISON_CHECK([[-Werror $file]], [[1]], [[]], [[experr]])[
+ fi
+ done
+ done
+done]
+
AT_CLEANUP