If conflict resolution makes states unreachable, remove those states,

report rules that are then unused, and don't report conflicts in those
states.
* src/conflicts.c, src/conflicts.h (conflicts_update_state_numbers):
New global function.
* src/lalr.c, src/lalr.h (lalr_update_state_numbers): New global
function.
* src/main.c (main): After conflict resolution, remove the unreachable
states and update all data structures that reference states by number.
* src/state.c (state_new): Initialize each state's reachable member to
false.
(state_mark_reachable_states): New static function.
(state_remove_unreachable_states): New global function.
* src/state.h (struct state): Add member bool reachable.
(state_remove_unreachable_states): Prototype.
* tests/conflicts.at (Unreachable States After Conflict Resolution):
New test case.
* tests/existing.at (GNU pic Grammar): Update test case output now that
an unused rule is discovered.
This commit is contained in:
Joel E. Denny
2007-05-07 02:56:56 +00:00
parent b09f4f4851
commit 5967f0cf59
10 changed files with 347 additions and 1 deletions

View File

@@ -617,3 +617,201 @@ e: e '+' e
AT_CHECK([bison -o input.c input.y])
AT_CLEANUP
## ---------------------------------------------- ##
## Unreachable States After Conflict Resolution. ##
## ---------------------------------------------- ##
AT_SETUP([[Unreachable States After Conflict Resolution]])
# If conflict resolution makes states unreachable, remove those states, report
# rules that are then unused, and don't report conflicts in those states. Test
# what happens when a nonterminal becomes useless as a result of state removal
# since that causes lalr.o's goto map to be rewritten.
AT_DATA([[input.y]],
[[%output "input.c"
%left 'a'
%%
start: resolved_conflict 'a' reported_conflicts 'a' ;
/* S/R conflict resolved as shift, so the state with item
* (resolved_conflict: 'a' . unreachable1) and all it transition successors are
* unreachable, and the associated production is useless. */
resolved_conflict:
'a' unreachable1
| %prec 'a'
;
/* S/R conflict that need not be reported since it is unreachable because of
* the previous conflict resolution. Nonterminal unreachable1 and all its
* productions are useless. */
unreachable1:
'a' unreachable2
|
;
/* Likewise for a R/R conflict and nonterminal unreachable2. */
unreachable2: | ;
/* Make sure remaining S/R and R/R conflicts are still reported correctly even
* when their states are renumbered due to state removal. */
reported_conflicts:
'a'
| 'a'
|
;
]])
AT_CHECK([[bison --report=all input.y]], 0, [],
[[input.y: conflicts: 1 shift/reduce, 1 reduce/reduce
input.y:12.5-20: warning: rule never reduced because of conflicts: resolved_conflict: 'a' unreachable1
input.y:20.5-20: warning: rule never reduced because of conflicts: unreachable1: 'a' unreachable2
input.y:21.4: warning: rule never reduced because of conflicts: unreachable1: /* empty */
input.y:25.13: warning: rule never reduced because of conflicts: unreachable2: /* empty */
input.y:25.16: warning: rule never reduced because of conflicts: unreachable2: /* empty */
input.y:31.5-7: warning: rule never reduced because of conflicts: reported_conflicts: 'a'
input.y:32.4: warning: rule never reduced because of conflicts: reported_conflicts: /* empty */
]])
AT_CHECK([[cat input.output]], 0,
[[Rules never reduced
2 resolved_conflict: 'a' unreachable1
4 unreachable1: 'a' unreachable2
5 | /* empty */
6 unreachable2: /* empty */
7 | /* empty */
9 reported_conflicts: 'a'
10 | /* empty */
State 4 conflicts: 1 shift/reduce
State 5 conflicts: 1 reduce/reduce
Grammar
0 $accept: start $end
1 start: resolved_conflict 'a' reported_conflicts 'a'
2 resolved_conflict: 'a' unreachable1
3 | /* empty */
4 unreachable1: 'a' unreachable2
5 | /* empty */
6 unreachable2: /* empty */
7 | /* empty */
8 reported_conflicts: 'a'
9 | 'a'
10 | /* empty */
Terminals, with rules where they appear
$end (0) 0
'a' (97) 1 2 4 8 9
error (256)
Nonterminals, with rules where they appear
$accept (4)
on left: 0
start (5)
on left: 1, on right: 0
resolved_conflict (6)
on left: 2 3, on right: 1
unreachable1 (7)
on left: 4 5, on right: 2
unreachable2 (8)
on left: 6 7, on right: 4
reported_conflicts (9)
on left: 8 9 10, on right: 1
state 0
0 $accept: . start $end
1 start: . resolved_conflict 'a' reported_conflicts 'a'
2 resolved_conflict: . 'a' unreachable1
3 | . ['a']
$default reduce using rule 3 (resolved_conflict)
start go to state 1
resolved_conflict go to state 2
Conflict between rule 3 and token 'a' resolved as reduce (%left 'a').
state 1
0 $accept: start . $end
$end shift, and go to state 3
state 2
1 start: resolved_conflict . 'a' reported_conflicts 'a'
'a' shift, and go to state 4
state 3
0 $accept: start $end .
$default accept
state 4
1 start: resolved_conflict 'a' . reported_conflicts 'a'
8 reported_conflicts: . 'a'
9 | . 'a'
10 | . ['a']
'a' shift, and go to state 5
'a' [reduce using rule 10 (reported_conflicts)]
reported_conflicts go to state 6
state 5
8 reported_conflicts: 'a' . ['a']
9 | 'a' . ['a']
'a' reduce using rule 8 (reported_conflicts)
'a' [reduce using rule 9 (reported_conflicts)]
$default reduce using rule 8 (reported_conflicts)
state 6
1 start: resolved_conflict 'a' reported_conflicts . 'a'
'a' shift, and go to state 7
state 7
1 start: resolved_conflict 'a' reported_conflicts 'a' .
$default reduce using rule 1 (start)
]])
AT_CLEANUP

View File

@@ -1520,6 +1520,8 @@ expr:
# Pass plenty of options, to exercise plenty of code, even if we
# don't actually check the output. But SEGV is watching us, and
# so might do dmalloc.
AT_CHECK([[bison --verbose --defines input.y]], 0, [], [])
AT_CHECK([[bison --verbose --defines input.y]], 0, [],
[[input.y:453.11-48: warning: rule never reduced because of conflicts: path: ORDINAL LAST object_type relative_path
]])
AT_CLEANUP