X-Git-Url: https://git.saurik.com/bison.git/blobdiff_plain/760b53a84bfc0c79bac266414136936400769e33..5407fc7d76bc0c1d15b0e281ba12ea20c77751e7:/tests/reduce.at diff --git a/tests/reduce.at b/tests/reduce.at index 08601d50..7e4977a0 100644 --- a/tests/reduce.at +++ b/tests/reduce.at @@ -1,20 +1,19 @@ # Exercising Bison Grammar Reduction. -*- Autotest -*- -# Copyright 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. +# Copyright (C) 2001-2002, 2007-2013 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -# 02111-1307, USA. +# along with this program. If not, see . AT_BANNER([[Grammar Reduction.]]) @@ -27,7 +26,7 @@ AT_SETUP([Useless Terminals]) AT_DATA([[input.y]], [[%verbose -%output="input.c" +%output "input.c" %token useless1 %token useless2 @@ -44,10 +43,10 @@ AT_DATA([[input.y]], exp: useful; ]]) -AT_CHECK([[bison input.y]]) +AT_BISON_CHECK([[input.y]]) AT_CHECK([[sed -n '/^Grammar/q;/^$/!p' input.output]], 0, -[[Terminals which are not used: +[[Terminals unused in grammar useless1 useless2 useless3 @@ -71,7 +70,7 @@ AT_SETUP([Useless Nonterminals]) AT_DATA([[input.y]], [[%verbose -%output="input.c" +%output "input.c" %nterm useless1 %nterm useless2 @@ -88,12 +87,123 @@ AT_DATA([[input.y]], exp: useful; ]]) -AT_CHECK([[bison input.y]], 0, [], -[[input.y contains 9 useless nonterminals +AT_BISON_CHECK([[input.y]], 0, [], +[[input.y: warning: 9 nonterminals useless in grammar [-Wother] +input.y:4.8-15: warning: nonterminal useless in grammar: useless1 [-Wother] +input.y:5.8-15: warning: nonterminal useless in grammar: useless2 [-Wother] +input.y:6.8-15: warning: nonterminal useless in grammar: useless3 [-Wother] +input.y:7.8-15: warning: nonterminal useless in grammar: useless4 [-Wother] +input.y:8.8-15: warning: nonterminal useless in grammar: useless5 [-Wother] +input.y:9.8-15: warning: nonterminal useless in grammar: useless6 [-Wother] +input.y:10.8-15: warning: nonterminal useless in grammar: useless7 [-Wother] +input.y:11.8-15: warning: nonterminal useless in grammar: useless8 [-Wother] +input.y:12.8-15: warning: nonterminal useless in grammar: useless9 [-Wother] +]]) + +AT_CHECK([[sed -n '/^Grammar/q;/^$/!p' input.output]], 0, +[[Nonterminals useless in grammar + useless1 + useless2 + useless3 + useless4 + useless5 + useless6 + useless7 + useless8 + useless9 +]]) + +AT_CLEANUP + + + +## --------------- ## +## Useless Rules. ## +## --------------- ## + +AT_SETUP([Useless Rules]) + +AT_KEYWORDS([report]) + +AT_DATA([[input.y]], +[[%verbose +%output "input.c" +%token useful +%% +exp: useful; +useless1: '1'; +useless2: '2'; +useless3: '3'; +useless4: '4'; +useless5: '5'; +useless6: '6'; +useless7: '7'; +useless8: '8'; +useless9: '9'; +]]) + +AT_BISON_CHECK([[-fcaret input.y]], 0, [], +[[input.y: warning: 9 nonterminals useless in grammar [-Wother] +input.y: warning: 9 rules useless in grammar [-Wother] +input.y:6.1-8: warning: nonterminal useless in grammar: useless1 [-Wother] + useless1: '1'; + ^^^^^^^^ +input.y:7.1-8: warning: nonterminal useless in grammar: useless2 [-Wother] + useless2: '2'; + ^^^^^^^^ +input.y:8.1-8: warning: nonterminal useless in grammar: useless3 [-Wother] + useless3: '3'; + ^^^^^^^^ +input.y:9.1-8: warning: nonterminal useless in grammar: useless4 [-Wother] + useless4: '4'; + ^^^^^^^^ +input.y:10.1-8: warning: nonterminal useless in grammar: useless5 [-Wother] + useless5: '5'; + ^^^^^^^^ +input.y:11.1-8: warning: nonterminal useless in grammar: useless6 [-Wother] + useless6: '6'; + ^^^^^^^^ +input.y:12.1-8: warning: nonterminal useless in grammar: useless7 [-Wother] + useless7: '7'; + ^^^^^^^^ +input.y:13.1-8: warning: nonterminal useless in grammar: useless8 [-Wother] + useless8: '8'; + ^^^^^^^^ +input.y:14.1-8: warning: nonterminal useless in grammar: useless9 [-Wother] + useless9: '9'; + ^^^^^^^^ +input.y:6.11-13: warning: rule useless in grammar [-Wother] + useless1: '1'; + ^^^ +input.y:7.11-13: warning: rule useless in grammar [-Wother] + useless2: '2'; + ^^^ +input.y:8.11-13: warning: rule useless in grammar [-Wother] + useless3: '3'; + ^^^ +input.y:9.11-13: warning: rule useless in grammar [-Wother] + useless4: '4'; + ^^^ +input.y:10.11-13: warning: rule useless in grammar [-Wother] + useless5: '5'; + ^^^ +input.y:11.11-13: warning: rule useless in grammar [-Wother] + useless6: '6'; + ^^^ +input.y:12.11-13: warning: rule useless in grammar [-Wother] + useless7: '7'; + ^^^ +input.y:13.11-13: warning: rule useless in grammar [-Wother] + useless8: '8'; + ^^^ +input.y:14.11-13: warning: rule useless in grammar [-Wother] + useless9: '9'; + ^^^ ]]) + AT_CHECK([[sed -n '/^Grammar/q;/^$/!p' input.output]], 0, -[[Useless nonterminals: +[[Nonterminals useless in grammar useless1 useless2 useless3 @@ -103,6 +213,1421 @@ AT_CHECK([[sed -n '/^Grammar/q;/^$/!p' input.output]], 0, useless7 useless8 useless9 +Terminals unused in grammar + '1' + '2' + '3' + '4' + '5' + '6' + '7' + '8' + '9' +Rules useless in grammar + 2 useless1: '1' + 3 useless2: '2' + 4 useless3: '3' + 5 useless4: '4' + 6 useless5: '5' + 7 useless6: '6' + 8 useless7: '7' + 9 useless8: '8' + 10 useless9: '9' +]]) + +AT_CLEANUP + + + +## ------------------- ## +## Reduced Automaton. ## +## ------------------- ## + +# Check that the automaton is that as the for the grammar reduced by +# hand. + +AT_SETUP([Reduced Automaton]) + +AT_KEYWORDS([report]) + +# The non reduced grammar. +# ------------------------ +AT_DATA([[not-reduced.y]], +[[/* A useless token. */ +%token useless_token +/* A useful one. */ +%token useful +%verbose +%output "not-reduced.c" + +%% + +exp: useful { /* A useful action. */ } + | non_productive { /* A non productive action. */ } + ; + +not_reachable: useful { /* A not reachable action. */ } + ; + +non_productive: non_productive useless_token + { /* Another non productive action. */ } + ; +%% +]]) + +AT_BISON_CHECK([[-fcaret not-reduced.y]], 0, [], +[[not-reduced.y: warning: 2 nonterminals useless in grammar [-Wother] +not-reduced.y: warning: 3 rules useless in grammar [-Wother] +not-reduced.y:14.1-13: warning: nonterminal useless in grammar: not_reachable [-Wother] + not_reachable: useful { /* A not reachable action. */ } + ^^^^^^^^^^^^^ +not-reduced.y:11.6-19: warning: nonterminal useless in grammar: non_productive [-Wother] + | non_productive { /* A non productive action. */ } + ^^^^^^^^^^^^^^ +not-reduced.y:11.6-57: warning: rule useless in grammar [-Wother] + | non_productive { /* A non productive action. */ } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +not-reduced.y:14.16-56: warning: rule useless in grammar [-Wother] + not_reachable: useful { /* A not reachable action. */ } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +not-reduced.y:17.17-18.63: warning: rule useless in grammar [-Wother] + non_productive: non_productive useless_token + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +]]) + +AT_CHECK([[sed -n '/^Grammar/q;/^$/!p' not-reduced.output]], 0, +[[Nonterminals useless in grammar + not_reachable + non_productive +Terminals unused in grammar + useless_token +Rules useless in grammar + 2 exp: non_productive + 3 not_reachable: useful + 4 non_productive: non_productive useless_token +]]) + +# The reduced grammar. +# -------------------- +AT_DATA([[reduced.y]], +[[/* A useless token. */ +%token useless_token +/* A useful one. */ +%token useful +%verbose +%output "reduced.c" + +%% + +exp: useful { /* A useful action. */ } +// | non_productive { /* A non productive action. */ } */ + ; + +//not_reachable: useful { /* A not reachable action. */ } +// ; + +//non_productive: non_productive useless_token +// { /* Another non productive action. */ } +// ; +%% +]]) + +AT_BISON_CHECK([[reduced.y]]) + +# Comparing the parsers. +cp reduced.c expout +AT_CHECK([sed 's/not-reduced/reduced/g' not-reduced.c], 0, [expout]) + +AT_CLEANUP + + + +## ------------------- ## +## Underivable Rules. ## +## ------------------- ## + +AT_SETUP([Underivable Rules]) + +AT_KEYWORDS([report]) + +AT_DATA([[input.y]], +[[%verbose +%output "input.c" +%token useful +%% +exp: useful | underivable; +underivable: indirection; +indirection: underivable; +]]) + +AT_BISON_CHECK([[input.y]], 0, [], +[[input.y: warning: 2 nonterminals useless in grammar [-Wother] +input.y: warning: 3 rules useless in grammar [-Wother] +input.y:5.15-25: warning: nonterminal useless in grammar: underivable [-Wother] +input.y:6.14-24: warning: nonterminal useless in grammar: indirection [-Wother] +input.y:5.15-25: warning: rule useless in grammar [-Wother] +input.y:6.14-24: warning: rule useless in grammar [-Wother] +input.y:7.14-24: warning: rule useless in grammar [-Wother] +]]) + +AT_CHECK([[sed -n '/^Grammar/q;/^$/!p' input.output]], 0, +[[Nonterminals useless in grammar + underivable + indirection +Rules useless in grammar + 2 exp: underivable + 3 underivable: indirection + 4 indirection: underivable +]]) + +AT_CLEANUP + + + +## ---------------- ## +## Empty Language. ## +## ---------------- ## + +AT_SETUP([Empty Language]) + +AT_DATA([[input.y]], +[[%output "input.c" +%% +exp: exp; +]]) + +AT_BISON_CHECK([[input.y]], 1, [], +[[input.y: warning: 2 nonterminals useless in grammar [-Wother] +input.y: warning: 2 rules useless in grammar [-Wother] +input.y:3.1-3: fatal error: start symbol exp does not derive any sentence ]]) AT_CLEANUP + + + +## ----------------- ## +## %define lr.type. ## +## ----------------- ## + +# AT_TEST_LR_TYPE(DESCRIPTION, +# DECLS, GRAMMAR, INPUT, +# BISON-STDERR, TABLES, +# [OTHER-CHECKS], +# [PARSER-EXIT-VALUE], +# [PARSER-STDOUT], [PARSER-STDERR]) +# ------------------------------------------------- +m4_define([AT_TEST_LR_TYPE], +[ +AT_TEST_TABLES_AND_PARSE([[no %define lr.type: ]$1], + [[LALR]], [[]], + [$2], m4_shiftn(2, $@)) +AT_TEST_TABLES_AND_PARSE([[%define lr.type lalr: ]$1], + [[LALR]], [[]], + [[%define lr.type lalr +]$2], + m4_shiftn(2, $@)) +AT_TEST_TABLES_AND_PARSE([[%define lr.type ielr: ]$1], + [[IELR]], [[]], + [[%define lr.type ielr +]$2], + m4_shiftn(2, $@)) +AT_TEST_TABLES_AND_PARSE([[%define lr.type canonical-lr: ]$1], + [[canonical LR]], [[]], + [[%define lr.type canonical-lr +]$2], + m4_shiftn(2, $@)) +]) + +AT_TEST_LR_TYPE([[Single State Split]], +[[%left 'a' +// Conflict resolution renders state 12 unreachable for canonical LR(1). We +// keep it so that the paser table diff is easier to code. +%define lr.keep-unreachable-state]], +[[ +S: 'a' A 'a' /* rule 1 */ + | 'b' A 'b' /* rule 2 */ + | 'c' c /* rule 3 */ + ; + +/* A conflict should appear after the first 'a' in rules 4 and 5 but only after + having shifted the first 'a' in rule 1. However, when LALR(1) merging is + chosen, the state containing that conflict is reused after having seen the + first 'b' in rule 2 and then the first 'a' in rules 4 and 5. In both cases, + because of the merged state, if the next token is an 'a', the %left forces a + reduction action with rule 5. In the latter case, only a shift is actually + grammatically correct. Thus, the parser would report a syntax error for the + grammatically correct sentence "baab" because it would encounter a syntax + error after that incorrect reduction. + + Despite not being LALR(1), Menhir version 20070322 suffers from this problem + as well. It uses David Pager's weak compatibility test for merging states. + Bison and Menhir accept non-LR(1) grammars with conflict resolution. Pager + designed his algorithm only for LR(1) grammars. */ +A: 'a' 'a' /* rule 4 */ + | 'a' /* rule 5 */ + ; + +/* Rule 3, rule 6, and rule 7 ensure that Bison does not report rule 4 as + useless after conflict resolution. This proves that, even though LALR(1) + generates incorrect parser tables sometimes, Bison will not necessarily + produce any warning to help the user realize it. */ +c: 'a' 'b' /* rule 6 */ + | A /* rule 7 */ + ; +]], + +dnl INPUT +[['b', 'a', 'a', 'b']], + +dnl BISON-STDERR +[], + +dnl TABLES +[[State 0 + + 0 $accept: . S $end + 1 S: . 'a' A 'a' + 2 | . 'b' A 'b' + 3 | . 'c' c + + 'a' shift, and go to state 1 + 'b' shift, and go to state 2 + 'c' shift, and go to state 3 + + S go to state 4 + + +State 1 + + 1 S: 'a' . A 'a' + 4 A: . 'a' 'a' + 5 | . 'a' + + 'a' shift, and go to state 5 + + A go to state 6 + + +State 2 + + 2 S: 'b' . A 'b' + 4 A: . 'a' 'a' + 5 | . 'a' + + 'a' shift, and go to state ]AT_COND_CASE([[LALR]], [[5]], [[16]])[ + + A go to state 7 + + +State 3 + + 3 S: 'c' . c + 4 A: . 'a' 'a' + 5 | . 'a' + 6 c: . 'a' 'b' + 7 | . A + + 'a' shift, and go to state 8 + + A go to state 9 + c go to state 10 + + +State 4 + + 0 $accept: S . $end + + $end shift, and go to state 11 + + +State 5 + + 4 A: 'a' . 'a' + 5 | 'a' . ]AT_COND_CASE([[LALR]], [[['a', 'b']]], [[['a']]])[ + + ]AT_COND_CASE([[canonical LR]], [['a']], + [[$default]])[ reduce using rule 5 (A) + + Conflict between rule 5 and token 'a' resolved as reduce (%left 'a'). + + +State 6 + + 1 S: 'a' A . 'a' + + 'a' shift, and go to state 13 + + +State 7 + + 2 S: 'b' A . 'b' + + 'b' shift, and go to state 14 + + +State 8 + + 4 A: 'a' . 'a' + 5 | 'a' . [$end] + 6 c: 'a' . 'b' + + 'a' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[17]], + [[12]])[ + 'b' shift, and go to state 15 + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 5 (A) + + +State 9 + + 7 c: A .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 7 (c) + + +State 10 + + 3 S: 'c' c .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 3 (S) + + +State 11 + + 0 $accept: S $end . + + $default accept + + +State 12 + + 4 A: 'a' 'a' .]AT_COND_CASE([[canonical LR]], [[ ['a']]])[ + + ]AT_COND_CASE([[canonical LR]], [['a']], + [[$default]])[ reduce using rule 4 (A) + + +State 13 + + 1 S: 'a' A 'a' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 1 (S) + + +State 14 + + 2 S: 'b' A 'b' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 2 (S) + + +State 15 + + 6 c: 'a' 'b' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 6 (c)]AT_COND_CASE([[LALR]], + [[]], [[ + + +State 16 + + 4 A: 'a' . 'a' + 5 | 'a' . ['b'] + + 'a' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[18]], + [[12]])[ + + ]AT_COND_CASE([[canonical LR]], [['b']], + [[$default]])[ reduce using rule 5 (A)]AT_COND_CASE([[canonical LR]], [[ + + +State 17 + + 4 A: 'a' 'a' . [$end] + + $end reduce using rule 4 (A) + + +State 18 + + 4 A: 'a' 'a' . ['b'] + + 'b' reduce using rule 4 (A)]])])[ +]], + +dnl OTHER-CHECKS +[], + +dnl PARSER-EXIT-VALUE, PARSER-STDOUT, PARSER-STDERR +[AT_COND_CASE([[LALR]], [[1]], [[0]])], +[], +[AT_COND_CASE([[LALR]], +[[syntax error +]])]) + +AT_TEST_LR_TYPE([[Lane Split]], +[[%left 'a' +// Conflict resolution renders state 16 unreachable for canonical LR(1). We +// keep it so that the paser table diff is easier to code. +%define lr.keep-unreachable-state]], +[[ +/* Similar to the last test case set but two states must be split. */ +S: 'a' A 'a' /* rule 1 */ + | 'b' A 'b' /* rule 2 */ + | 'c' c /* rule 3 */ + ; + +A: 'a' 'a' 'a' /* rule 4 */ + | 'a' 'a' /* rule 5 */ + ; + +c: 'a' 'a' 'b' /* rule 6 */ + | A /* rule 7 */ + ; +]], + +dnl INPUT +[['b', 'a', 'a', 'a', 'b']], + +dnl BISON-STDERR +[], + +dnl TABLES +[[State 0 + + 0 $accept: . S $end + 1 S: . 'a' A 'a' + 2 | . 'b' A 'b' + 3 | . 'c' c + + 'a' shift, and go to state 1 + 'b' shift, and go to state 2 + 'c' shift, and go to state 3 + + S go to state 4 + + +State 1 + + 1 S: 'a' . A 'a' + 4 A: . 'a' 'a' 'a' + 5 | . 'a' 'a' + + 'a' shift, and go to state 5 + + A go to state 6 + + +State 2 + + 2 S: 'b' . A 'b' + 4 A: . 'a' 'a' 'a' + 5 | . 'a' 'a' + + 'a' shift, and go to state ]AT_COND_CASE([[LALR]], [[5]], [[18]])[ + + A go to state 7 + + +State 3 + + 3 S: 'c' . c + 4 A: . 'a' 'a' 'a' + 5 | . 'a' 'a' + 6 c: . 'a' 'a' 'b' + 7 | . A + + 'a' shift, and go to state 8 + + A go to state 9 + c go to state 10 + + +State 4 + + 0 $accept: S . $end + + $end shift, and go to state 11 + + +State 5 + + 4 A: 'a' . 'a' 'a' + 5 | 'a' . 'a' + + 'a' shift, and go to state 12 + + +State 6 + + 1 S: 'a' A . 'a' + + 'a' shift, and go to state 13 + + +State 7 + + 2 S: 'b' A . 'b' + + 'b' shift, and go to state 14 + + +State 8 + + 4 A: 'a' . 'a' 'a' + 5 | 'a' . 'a' + 6 c: 'a' . 'a' 'b' + + 'a' shift, and go to state 15 + + +State 9 + + 7 c: A .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 7 (c) + + +State 10 + + 3 S: 'c' c .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 3 (S) + + +State 11 + + 0 $accept: S $end . + + $default accept + + +State 12 + + 4 A: 'a' 'a' . 'a' + 5 | 'a' 'a' . ]AT_COND_CASE([[LALR]], [[['a', 'b']]], [[['a']]])[ + + ]AT_COND_CASE([[canonical LR]], [['a']], + [[$default]])[ reduce using rule 5 (A) + + Conflict between rule 5 and token 'a' resolved as reduce (%left 'a'). + + +State 13 + + 1 S: 'a' A 'a' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 1 (S) + + +State 14 + + 2 S: 'b' A 'b' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 2 (S) + + +State 15 + + 4 A: 'a' 'a' . 'a' + 5 | 'a' 'a' . [$end] + 6 c: 'a' 'a' . 'b' + + 'a' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[19]], + [[16]])[ + 'b' shift, and go to state 17 + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 5 (A) + + +State 16 + + 4 A: 'a' 'a' 'a' .]AT_COND_CASE([[canonical LR]], [[ ['a']]])[ + + ]AT_COND_CASE([[canonical LR]], [['a']], + [[$default]])[ reduce using rule 4 (A) + + +State 17 + + 6 c: 'a' 'a' 'b' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 6 (c)]AT_COND_CASE([[LALR]], + [[]], [[ + + +State 18 + + 4 A: 'a' . 'a' 'a' + 5 | 'a' . 'a' + + 'a' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[20]], + [[19]])[ + + +State 19]AT_COND_CASE([[canonical LR]], [[ + + 4 A: 'a' 'a' 'a' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 4 (A) + + +State 20]])[ + + 4 A: 'a' 'a' . 'a' + 5 | 'a' 'a' . ['b'] + + 'a' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[21]], + [[16]])[ + + ]AT_COND_CASE([[canonical LR]], [['b']], + [[$default]])[ reduce using rule 5 (A)]AT_COND_CASE([[canonical LR]], [[ + + +State 21 + + 4 A: 'a' 'a' 'a' .]AT_COND_CASE([[canonical LR]], [[ ['b']]])[ + + ]AT_COND_CASE([[canonical LR]], [['b']], + [[$default]])[ reduce using rule 4 (A)]])])[ +]], + +dnl OTHER-CHECKS +[], + +dnl PARSER-EXIT-VALUE, PARSER-STDOUT, PARSER-STDERR +[AT_COND_CASE([[LALR]], [[1]], [[0]])], +[], +[AT_COND_CASE([[LALR]], +[[syntax error +]])]) + +AT_TEST_LR_TYPE([[Complex Lane Split]], +[[%left 'a' +// Conflict resolution renders state 16 unreachable for canonical LR(1). We +// keep it so that the paser table diff is easier to code. +%define lr.keep-unreachable-state]], +[[ +/* Similar to the last test case set but forseeing the S/R conflict from the + first state that must be split is becoming difficult. Imagine if B were + even more complex. Imagine if A had other RHS's ending in other + nonterminals. */ +S: 'a' A 'a' + | 'b' A 'b' + | 'c' c + ; +A: 'a' 'a' B + ; +B: 'a' + | %empty %prec 'a' + ; +c: 'a' 'a' 'b' + | A + ; +]], + +dnl INPUT +[['b', 'a', 'a', 'a', 'b']], + +dnl BISON-STDERR +[], + +dnl TABLES +[[State 0 + + 0 $accept: . S $end + 1 S: . 'a' A 'a' + 2 | . 'b' A 'b' + 3 | . 'c' c + + 'a' shift, and go to state 1 + 'b' shift, and go to state 2 + 'c' shift, and go to state 3 + + S go to state 4 + + +State 1 + + 1 S: 'a' . A 'a' + 4 A: . 'a' 'a' B + + 'a' shift, and go to state 5 + + A go to state 6 + + +State 2 + + 2 S: 'b' . A 'b' + 4 A: . 'a' 'a' B + + 'a' shift, and go to state ]AT_COND_CASE([[LALR]], [[5]], [[19]])[ + + A go to state 7 + + +State 3 + + 3 S: 'c' . c + 4 A: . 'a' 'a' B + 7 c: . 'a' 'a' 'b' + 8 | . A + + 'a' shift, and go to state 8 + + A go to state 9 + c go to state 10 + + +State 4 + + 0 $accept: S . $end + + $end shift, and go to state 11 + + +State 5 + + 4 A: 'a' . 'a' B + + 'a' shift, and go to state 12 + + +State 6 + + 1 S: 'a' A . 'a' + + 'a' shift, and go to state 13 + + +State 7 + + 2 S: 'b' A . 'b' + + 'b' shift, and go to state 14 + + +State 8 + + 4 A: 'a' . 'a' B + 7 c: 'a' . 'a' 'b' + + 'a' shift, and go to state 15 + + +State 9 + + 8 c: A .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 8 (c) + + +State 10 + + 3 S: 'c' c .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 3 (S) + + +State 11 + + 0 $accept: S $end . + + $default accept + + +State 12 + + 4 A: 'a' 'a' . B + 5 B: . 'a' + 6 | . ]AT_COND_CASE([[LALR]], [[['a', 'b']]], [[['a']]])[ + + ]AT_COND_CASE([[canonical LR]], [['a']], + [[$default]])[ reduce using rule 6 (B) + + B go to state 17 + + Conflict between rule 6 and token 'a' resolved as reduce (%left 'a'). + + +State 13 + + 1 S: 'a' A 'a' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 1 (S) + + +State 14 + + 2 S: 'b' A 'b' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 2 (S) + + +State 15 + + 4 A: 'a' 'a' . B + 5 B: . 'a' + 6 | . [$end] + 7 c: 'a' 'a' . 'b' + + 'a' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[20]], + [[16]])[ + 'b' shift, and go to state 18 + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 6 (B) + + B go to state ]AT_COND_CASE([[canonical LR]], [[21]], [[17]])[ + + +State 16 + + 5 B: 'a' .]AT_COND_CASE([[canonical LR]], [[ ['a']]])[ + + ]AT_COND_CASE([[canonical LR]], [['a']], + [[$default]])[ reduce using rule 5 (B) + + +State 17 + + 4 A: 'a' 'a' B .]AT_COND_CASE([[canonical LR]], [[ ['a']]])[ + + ]AT_COND_CASE([[canonical LR]], [['a']], + [[$default]])[ reduce using rule 4 (A) + + +State 18 + + 7 c: 'a' 'a' 'b' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 7 (c)]AT_COND_CASE([[LALR]], [], [[ + + +State 19 + + 4 A: 'a' . 'a' B + + 'a' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[22]], + [[20]])[ + + +State 20]AT_COND_CASE([[canonical LR]], [[ + + 5 B: 'a' . [$end] + + $end reduce using rule 5 (B) + + +State 21 + + 4 A: 'a' 'a' B . [$end] + + $end reduce using rule 4 (A) + + +State 22]])[ + + 4 A: 'a' 'a' . B + 5 B: . 'a' + 6 | . ['b'] + + 'a' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[23]], + [[16]])[ + + ]AT_COND_CASE([[canonical LR]], [['b']], + [[$default]])[ reduce using rule 6 (B) + + B go to state ]AT_COND_CASE([[canonical LR]], [[24 + + +State 23 + + 5 B: 'a' . ['b'] + + 'b' reduce using rule 5 (B) + + +State 24 + + 4 A: 'a' 'a' B . ['b'] + + 'b' reduce using rule 4 (A)]], [[17]])])[ +]], + +dnl OTHER-CHECKS +[], + +dnl PARSER-EXIT-VALUE, PARSER-STDOUT, PARSER-STDERR +[AT_COND_CASE([[LALR]], [[1]], [[0]])], +[], +[AT_COND_CASE([[LALR]], +[[syntax error +]])]) + +AT_TEST_LR_TYPE([[Split During Added Lookahead Propagation]], +[[%define lr.keep-unreachable-state]], +[[ +/* The partial state chart diagram below is for LALR(1). State 0 is the start + state. States are iterated for successor construction in numerical order. + Transitions are downwards. + + State 13 has a R/R conflict that cannot be predicted by Bison's LR(1) + algorithm using annotations alone. That is, when state 11's successor on + 'd' is merged with state 5 (which is originally just state 1's successor on + 'd'), state 5's successor on 'e' must then be changed because the resulting + lookaheads that propagate to it now make it incompatible with state 8's + successor on 'e'. In other words, state 13 must be split to avoid the + conflict. + + 0 + / | \ + a / c| \ b + 1 3 2 + | | | + d| |c | d + | 11 | + | | | + \ /d | + 5 8 + \ | + e \ / e + 13 + R/R + + This grammar is designed carefully to make sure that, despite Bison's LR(1) + algorithm's bread-first iteration of transitions to reconstruct states, + state 11's successors are constructed after state 5's and state 8's. + Otherwise (for example, if you remove the first 'c' in each of rules 6 and + 7), state 5's successor on 'e' would never be merged with state 8's, so the + split of the resulting state 13 would never need to be performed. */ +S: 'a' A 'f' + | 'a' B + | 'b' A 'f' + | 'b' B 'g' + | 'b' 'd' + | 'c' 'c' A 'g' + | 'c' 'c' B + ; +A: 'd' 'e' ; +B: 'd' 'e' ; +]], + +dnl INPUT +[['b', 'd', 'e', 'g']], + +dnl BISON-STDERR +[AT_COND_CASE([[LALR]], +[[input.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr] +]], [])], + +dnl TABLES +[[State 0 + + 0 $accept: . S $end + 1 S: . 'a' A 'f' + 2 | . 'a' B + 3 | . 'b' A 'f' + 4 | . 'b' B 'g' + 5 | . 'b' 'd' + 6 | . 'c' 'c' A 'g' + 7 | . 'c' 'c' B + + 'a' shift, and go to state 1 + 'b' shift, and go to state 2 + 'c' shift, and go to state 3 + + S go to state 4 + + +State 1 + + 1 S: 'a' . A 'f' + 2 | 'a' . B + 8 A: . 'd' 'e' + 9 B: . 'd' 'e' + + 'd' shift, and go to state 5 + + A go to state 6 + B go to state 7 + + +State 2 + + 3 S: 'b' . A 'f' + 4 | 'b' . B 'g' + 5 | 'b' . 'd' + 8 A: . 'd' 'e' + 9 B: . 'd' 'e' + + 'd' shift, and go to state 8 + + A go to state 9 + B go to state 10 + + +State 3 + + 6 S: 'c' . 'c' A 'g' + 7 | 'c' . 'c' B + + 'c' shift, and go to state 11 + + +State 4 + + 0 $accept: S . $end + + $end shift, and go to state 12 + + +State 5 + + 8 A: 'd' . 'e' + 9 B: 'd' . 'e' + + 'e' shift, and go to state ]AT_COND_CASE([[LALR]], [[13]], + [[canonical LR]], [[13]], + [[20]])[ + + +State 6 + + 1 S: 'a' A . 'f' + + 'f' shift, and go to state 14 + + +State 7 + + 2 S: 'a' B .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 2 (S) + + +State 8 + + 5 S: 'b' 'd' . [$end] + 8 A: 'd' . 'e' + 9 B: 'd' . 'e' + + 'e' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[20]], + [[13]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 5 (S) + + +State 9 + + 3 S: 'b' A . 'f' + + 'f' shift, and go to state 15 + + +State 10 + + 4 S: 'b' B . 'g' + + 'g' shift, and go to state 16 + + +State 11 + + 6 S: 'c' 'c' . A 'g' + 7 | 'c' 'c' . B + 8 A: . 'd' 'e' + 9 B: . 'd' 'e' + + 'd' shift, and go to state ]AT_COND_CASE([[canonical LR]], [[21]], + [[5]])[ + + A go to state 17 + B go to state 18 + + +State 12 + + 0 $accept: S $end . + + $default accept]AT_COND_CASE([[LALR]], [[ + + +State 13 + + 8 A: 'd' 'e' . ['f', 'g'] + 9 B: 'd' 'e' . [$end, 'g'] + + $end reduce using rule 9 (B) + 'g' reduce using rule 8 (A) + 'g' [reduce using rule 9 (B)] + $default reduce using rule 8 (A)]], [[ + + +State 13 + + 8 A: 'd' 'e' . ['f'] + 9 B: 'd' 'e' . ]AT_COND_CASE([[canonical LR]], [[[$end]]], [[['g']]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [['g' ]])[ reduce using rule 9 (B) + ]AT_COND_CASE([[canonical LR]], [['f' ]], + [[$default]])[ reduce using rule 8 (A)]])[ + + +State 14 + + 1 S: 'a' A 'f' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 1 (S) + + +State 15 + + 3 S: 'b' A 'f' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 3 (S) + + +State 16 + + 4 S: 'b' B 'g' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 4 (S) + + +State 17 + + 6 S: 'c' 'c' A . 'g' + + 'g' shift, and go to state 19 + + +State 18 + + 7 S: 'c' 'c' B .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 7 (S) + + +State 19 + + 6 S: 'c' 'c' A 'g' .]AT_COND_CASE([[canonical LR]], [[ [$end]]])[ + + ]AT_COND_CASE([[canonical LR]], [[$end]], + [[$default]])[ reduce using rule 6 (S)]AT_COND_CASE([[LALR]], + [[]], [[ + + +State 20]AT_COND_CASE([[canonical LR]], [[ + + 8 A: 'd' 'e' . ['f'] + 9 B: 'd' 'e' . ['g'] + + 'f' reduce using rule 8 (A) + 'g' reduce using rule 9 (B) + + +State 21 + + 8 A: 'd' . 'e' + 9 B: 'd' . 'e' + + 'e' shift, and go to state 22 + + +State 22 + + 8 A: 'd' 'e' . ['g'] + 9 B: 'd' 'e' . [$end] + + $end reduce using rule 9 (B) + 'g' reduce using rule 8 (A)]], [[ + + 8 A: 'd' 'e' . ['f', 'g'] + 9 B: 'd' 'e' . [$end] + + $end reduce using rule 9 (B) + $default reduce using rule 8 (A)]])])[ +]], + +dnl OTHER-CHECKS +[], + +dnl PARSER-EXIT-VALUE, PARSER-STDOUT, PARSER-STDERR +[AT_COND_CASE([[LALR]], [[1]], [[0]])], +[], +[AT_COND_CASE([[LALR]], +[[syntax error +]])]) + + + +## ------------------------------- ## +## %define lr.default-reduction. ## +## ------------------------------- ## + +# AT_TEST_LR_DEFAULT_REDUCTIONS(GRAMMAR, INPUT, TABLES) +# ----------------------------------------------------- +m4_define([AT_TEST_LR_DEFAULT_REDUCTIONS], +[ +AT_TEST_TABLES_AND_PARSE([[no %define lr.default-reduction]], + [[most]], [[]], + [[]], + [$1], [$2], [[]], [$3]) +AT_TEST_TABLES_AND_PARSE([[%define lr.default-reduction most]], + [[most]], [[]], + [[%define lr.default-reduction most]], + [$1], [$2], [[]], [$3]) +AT_TEST_TABLES_AND_PARSE([[%define lr.default-reduction consistent]], + [[consistent]], [[]], + [[%define lr.default-reduction consistent]], + [$1], [$2], [[]], [$3]) +AT_TEST_TABLES_AND_PARSE([[%define lr.default-reduction accepting]], + [[accepting]], [[]], + [[%define lr.default-reduction accepting]], + [$1], [$2], [[]], [$3]) +]) + +AT_TEST_LR_DEFAULT_REDUCTIONS([[ +/* The start state is consistent and has a shift on 'a' and no reductions. + After pushing the b below, enter an inconsistent state that has a shift and + one reduction with one lookahead. */ +start: + a b + | a b 'a' + | a c 'b' + ; + +/* After shifting this 'a', enter a consistent state that has no shift and 1 + reduction with multiple lookaheads. */ +a: 'a' ; + +/* After the previous reduction, enter an inconsistent state that has no shift + and multiple reductions. The first reduction has more lookaheads than the + second, so the first should always be preferred as the default reduction if + enabled. The second reduction has one lookahead. */ +b: %empty; +c: %empty; +]], +dnl Visit each state mentioned above. +[['a', 'a']], +[[State 0 + + 0 $accept: . start $end + 1 start: . a b + 2 | . a b 'a' + 3 | . a c 'b' + 4 a: . 'a' + + 'a' shift, and go to state 1 + + start go to state 2 + a go to state 3 + + +State 1 + + 4 a: 'a' .]AT_COND_CASE([[accepting]], [[ [$end, 'a', 'b'] + + $end reduce using rule 4 (a) + 'a' reduce using rule 4 (a) + 'b' reduce using rule 4 (a)]], [[ + + $default reduce using rule 4 (a)]])[ + + +State 2 + + 0 $accept: start . $end + + $end shift, and go to state 4 + + +State 3 + + 1 start: a . b + 2 | a . b 'a' + 3 | a . c 'b' + 5 b: . [$end, 'a'] + 6 c: . ['b']]AT_COND_CASE([[most]], [[ + + 'b' reduce using rule 6 (c) + $default reduce using rule 5 (b)]], [[ + + $end reduce using rule 5 (b) + 'a' reduce using rule 5 (b) + 'b' reduce using rule 6 (c)]])[ + + b go to state 5 + c go to state 6 + + +State 4 + + 0 $accept: start $end . + + $default accept + + +State 5 + + 1 start: a b . [$end] + 2 | a b . 'a' + + 'a' shift, and go to state 7 + + ]AT_COND_CASE([[most]], [[$default]], + [[$end]])[ reduce using rule 1 (start) + + +State 6 + + 3 start: a c . 'b' + + 'b' shift, and go to state 8 + + +State 7 + + 2 start: a b 'a' .]AT_COND_CASE([[accepting]], [[ [$end] + + $end reduce using rule 2 (start)]], [[ + + $default reduce using rule 2 (start)]])[ + + +State 8 + + 3 start: a c 'b' .]AT_COND_CASE([[accepting]], [[ [$end] + + $end reduce using rule 3 (start)]], [[ + + $default reduce using rule 3 (start)]])[ +]])