Skip to content

Commit ba596de

Browse files
committed
cex: display shifts before reductions
When reporting counterexamples for s/r conflicts, put the shift first. This is more natural, and displays the default resolution first, which is also what happens for r/r conflicts where the smallest rule number is displayed first, and "wins". * src/counterexample.c (counterexample): Add a shift_reduce member. (new_counterexample): Adjust. Swap the derivations when this is a s/r conflict. (print_counterexample): For s/r conflicts, prefer "Shift derivation" and "Reduce derivation" rather than "First/Second derivation". * tests/conflicts.at, tests/counterexample.at, tests/report.at: Adjust. * NEWS, doc/bison.texi: Ditto.
1 parent 577f594 commit ba596de

File tree

6 files changed

+126
-108
lines changed

6 files changed

+126
-108
lines changed

NEWS

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ GNU Bison NEWS
22

33
* Noteworthy changes in release ?.? (????-??-??) [?]
44

5+
Changes in the display of counterexamples.
6+
57
** Documentation
68

79
*** Examples
@@ -45,8 +47,8 @@ GNU Bison NEWS
4547
conflict. For example:
4648

4749
Example exp '+' exp • '/' exp
48-
First derivation exp → [ exp → [ exp '+' exp • ] '/' exp ]
49-
Second derivation exp → [ exp '+' exp → [ exp '/' exp ] ]
50+
Shift derivation exp → [ exp '+' exp → [ exp • '/' exp ] ]
51+
Reduce derivation exp → [ exp → [ exp '+' exp • ] '/' exp ]
5052

5153
When Bison is installed with text styling enabled, the example is actually
5254
shown twice, with colors highlighting the ambiguity.
@@ -56,10 +58,10 @@ GNU Bison NEWS
5658
bison cannot find an example that can be derived in two ways, it instead
5759
generates two examples that are the same up until the dot:
5860

59-
First example expr • ID $end
60-
First derivation $accept → [ s → [ a → [ expr ] ID ] $end ]
61-
Second example expr • ID ',' ID $end
62-
Second derivation $accept → [ s → [ a → [ expr → [ expr • ID ',' ] ] ID ] $end ]
61+
First example expr • ID ',' ID $end
62+
Shift derivation $accept → [ s → [ a → [ expr → [ expr • ID ',' ] ] ID ] $end ]
63+
Second example expr • ID $end
64+
Reduce derivation $accept → [ s → [ a → [ expr ] ID ] $end ]
6365

6466
In these cases, the parser usually doesn't have enough lookahead to
6567
differentiate the two given examples.

doc/bison.texi

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9936,25 +9936,25 @@ output is actually in color)}:
99369936
@example
99379937
Shift/reduce conflict on token "else":
99389938
@group
9939-
Example @yellow{"if" expr "then"} @blue{"if" expr "then" stmt} @red{•} @yellow{"else" stmt}
9940-
First derivation @yellow{if_stmt @arrow{} [ "if" expr "then"} @green{stmt @arrow{} [} @blue{if_stmt @arrow{} [ "if" expr "then" stmt} @red{•} @blue{]} @green{]} @yellow{"else" stmt ]}
99419939
Example @yellow{"if" expr "then"} @blue{"if" expr "then" stmt} @red{•} @blue{"else" stmt}
9942-
Second derivation @yellow{if_stmt @arrow{} [ "if" expr "then"} @green{stmt @arrow{} [} @blue{if_stmt @arrow{} [ "if" expr "then" stmt} @red{•} @blue{"else" stmt ]} @green{]} @yellow{]}
9940+
Shift derivation @yellow{if_stmt @arrow{} [ "if" expr "then"} @green{stmt @arrow{} [} @blue{if_stmt @arrow{} [ "if" expr "then" stmt} @red{•} @blue{"else" stmt ]} @green{]} @yellow{]}
9941+
Example @yellow{"if" expr "then"} @blue{"if" expr "then" stmt} @red{•} @yellow{"else" stmt}
9942+
Reduce derivation @yellow{if_stmt @arrow{} [ "if" expr "then"} @green{stmt @arrow{} [} @blue{if_stmt @arrow{} [ "if" expr "then" stmt} @red{•} @blue{]} @green{]} @yellow{"else" stmt ]}
99439943
@end group
99449944
@end example
99459945
@end ifhtml
99469946
@ifnothtml
99479947
@smallexample
99489948
Shift/reduce conflict on token "else":
99499949
@group
9950-
Example
9951-
@yellow{"if" expr "then"} @blue{"if" expr "then" stmt} @red{•} @yellow{"else" stmt}
9952-
First derivation
9953-
@yellow{if_stmt @arrow{} [ "if" expr "then"} @green{stmt @arrow{} [} @blue{if_stmt @arrow{} [ "if" expr "then" stmt} @red{•} @blue{]} @green{]} @yellow{"else" stmt ]}
99549950
Example
99559951
@yellow{"if" expr "then"} @blue{"if" expr "then" stmt} @red{•} @blue{"else" stmt}
9956-
Second derivation
9952+
Shift derivation
99579953
@yellow{if_stmt @arrow{} [ "if" expr "then"} @green{stmt @arrow{} [} @blue{if_stmt @arrow{} [ "if" expr "then" stmt} @red{•} @blue{"else" stmt ]} @green{]} @yellow{]}
9954+
Example
9955+
@yellow{"if" expr "then"} @blue{"if" expr "then" stmt} @red{•} @yellow{"else" stmt}
9956+
Reduce derivation
9957+
@yellow{if_stmt @arrow{} [ "if" expr "then"} @green{stmt @arrow{} [} @blue{if_stmt @arrow{} [ "if" expr "then" stmt} @red{•} @blue{]} @green{]} @yellow{"else" stmt ]}
99589958
@end group
99599959
@end smallexample
99609960
@end ifnothtml
@@ -9987,10 +9987,10 @@ $ @kbd{bison -Wcex sequence.y}
99879987
sequence.y: @dwarning{warning}: 1 shift/reduce conflict [@dwarning{-Wconflicts-sr}]
99889988
sequence.y: @dwarning{warning}: 2 reduce/reduce conflicts [@dwarning{-Wconflicts-rr}]
99899989
Shift/reduce conflict on token "word":
9990-
Example @red{•} @yellow{"word"}
9991-
First derivation @yellow{sequence @arrow{} [} @green{sequence @arrow{} [} @red{•} @green{]} @yellow{"word" ]}
99929990
Example @red{•} @green{"word"}
9993-
Second derivation @yellow{sequence @arrow{} [} @green{maybeword @arrow{} [} @red{•} @green{"word" ]} @yellow{]}
9991+
Shift derivation @yellow{sequence @arrow{} [} @green{maybeword @arrow{} [} @red{•} @green{"word" ]} @yellow{]}
9992+
Example @red{•} @yellow{"word"}
9993+
Reduce derivation @yellow{sequence @arrow{} [} @green{sequence @arrow{} [} @red{•} @green{]} @yellow{"word" ]}
99949994

99959995
Reduce/reduce conflict on tokens $end, "word":
99969996
Example @red{•}
@@ -9999,10 +9999,10 @@ Reduce/reduce conflict on tokens $end, "word":
99999999
Second derivation @yellow{sequence @arrow{} [} @green{maybeword @arrow{} [} @red{•} @green{]} @yellow{]}
1000010000

1000110001
Shift/reduce conflict on token "word":
10002-
Example @red{•} @yellow{"word"}
10003-
First derivation @yellow{sequence @arrow{} [} @green{sequence @arrow{} [} @blue{maybeword @arrow{} [} @red{•} @blue{]} @green{]} @yellow{"word" ]}
1000410002
Example @red{•} @green{"word"}
10005-
Second derivation @yellow{sequence @arrow{} [} @green{maybeword @arrow{} [} @red{•} @green{"word" ]} @yellow{]}
10003+
Shift derivation @yellow{sequence @arrow{} [} @green{maybeword @arrow{} [} @red{•} @green{"word" ]} @yellow{]}
10004+
Example @red{•} @yellow{"word"}
10005+
Reduce derivation @yellow{sequence @arrow{} [} @green{sequence @arrow{} [} @blue{maybeword @arrow{} [} @red{•} @blue{]} @green{]} @yellow{"word" ]}
1000610006

1000710007
sequence.y:8.3-45: @dwarning{warning}: rule useless in parser due to conflicts [@dwarning{-Wother}]
1000810008
8 | @dwarning{%empty @{ printf ("empty maybeword\n"); @}}
@@ -10033,10 +10033,10 @@ expr: %empty | expr ID ','
1003310033

1003410034
@smallexample
1003510035
Shift/reduce conflict on token ID:
10036-
First example @blue{expr} @red{•} @green{ID} @yellow{$end}
10037-
First derivation @yellow{$accept @arrow{} [} @green{s @arrow{} [} @blue{a @arrow{} [ expr} @red{•} @blue{]} @green{ID ]} @yellow{$end ]}
10038-
Second example @purple{expr} @red{•} @purple{ID ','} @green{ID} @yellow{$end}
10039-
Second derivation @yellow{$accept @arrow{} [} @green{s @arrow{} [} @blue{a @arrow{} [} @purple{expr @arrow{} [ expr} @red{•} @purple{ID ',' ]} @blue{]} @green{ID ]} @yellow{$end ]}
10036+
First example @purple{expr} @red{•} @purple{ID ','} @green{ID} @yellow{$end}
10037+
Shift derivation @yellow{$accept @arrow{} [} @green{s @arrow{} [} @blue{a @arrow{} [} @purple{expr @arrow{} [ expr} @red{•} @purple{ID ',' ]} @blue{]} @green{ID ]} @yellow{$end ]}
10038+
Second example @blue{expr} @red{•} @green{ID} @yellow{$end}
10039+
Reduce derivation @yellow{$accept @arrow{} [} @green{s @arrow{} [} @blue{a @arrow{} [ expr} @red{•} @blue{]} @green{ID ]} @yellow{$end ]}
1004010040
@end smallexample
1004110041

1004210042
This conflict is caused by the parser not having enough information to know
@@ -10432,10 +10432,10 @@ counterexamples within the report, augmented with the corresponding items
1043210432
Shift/reduce conflict on token '/':
1043310433
1 exp: exp '+' exp •
1043410434
4 exp: exp • '/' exp
10435-
Example @green{exp '+' exp} @red{•} @yellow{'/' exp}
10436-
First derivation @yellow{exp @arrow{} [} @green{exp @arrow{} [ exp '+' exp} @red{•} @green{]} @yellow{'/' exp ]}
1043710435
Example @yellow{exp '+'} @green{exp} @red{•} @green{'/' exp}
10438-
Second derivation @yellow{exp @arrow{} [ exp '+'} @green{exp @arrow{} [ exp} @red{•} @green{'/' exp ]} @yellow{]}
10436+
Shift derivation @yellow{exp @arrow{} [ exp '+'} @green{exp @arrow{} [ exp} @red{•} @green{'/' exp ]} @yellow{]}
10437+
Example @green{exp '+' exp} @red{•} @yellow{'/' exp}
10438+
Reduce derivation @yellow{exp @arrow{} [} @green{exp @arrow{} [ exp '+' exp} @red{•} @green{]} @yellow{'/' exp ]}
1043910439
@end example
1044010440

1044110441
This shows two separate derivations in the grammar for the same @code{exp}:

src/counterexample.c

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,29 @@ typedef struct
7676
{
7777
derivation *d1;
7878
derivation *d2;
79+
bool shift_reduce;
7980
bool unifying;
8081
bool timeout;
8182
} counterexample;
8283

8384
static counterexample *
8485
new_counterexample (derivation *d1, derivation *d2,
86+
bool shift_reduce,
8587
bool u, bool t)
8688
{
8789
counterexample *res = xmalloc (sizeof *res);
88-
res->d1 = d1;
89-
res->d2 = d2;
90+
res->shift_reduce = shift_reduce;
91+
if (shift_reduce)
92+
{
93+
// Display the shift first.
94+
res->d1 = d2;
95+
res->d2 = d1;
96+
}
97+
else
98+
{
99+
res->d1 = d1;
100+
res->d2 = d2;
101+
}
90102
res->unifying = u;
91103
res->timeout = t;
92104
return res;
@@ -107,7 +119,7 @@ print_counterexample (counterexample *cex, FILE *out, const char *prefix)
107119
prefix, cex->unifying ? _("Example") : _("First example"));
108120
derivation_print_leaves (cex->d1, out, prefix);
109121
fprintf (out, " %s%-20s ",
110-
prefix, _("First derivation"));
122+
prefix, cex->shift_reduce ? _("Shift derivation") : _("First derivation"));
111123
derivation_print (cex->d1, out, prefix);
112124

113125
// If we output to the terminal (via stderr) and we have color
@@ -120,7 +132,7 @@ print_counterexample (counterexample *cex, FILE *out, const char *prefix)
120132
derivation_print_leaves (cex->d2, out, prefix);
121133
}
122134
fprintf (out, " %s%-20s ",
123-
prefix, _("Second derivation"));
135+
prefix, cex->shift_reduce ? _("Reduce derivation") : _("Second derivation"));
124136
derivation_print (cex->d2, out, prefix);
125137

126138
fputc ('\n', out);
@@ -506,7 +518,7 @@ example_from_path (bool shift_reduce,
506518
: shortest_path_from_start (itm2, next_sym);
507519
derivation *deriv2 = complete_diverging_example (next_sym, path_2, NULL);
508520
gl_list_free (path_2);
509-
return new_counterexample (deriv1, deriv2, false, true);
521+
return new_counterexample (deriv1, deriv2, shift_reduce, false, true);
510522
}
511523

512524
/*
@@ -619,7 +631,8 @@ ss_set_parse_state (search_state *ss, int idx, parse_state *ps)
619631
*/
620632
static counterexample *
621633
complete_diverging_examples (search_state *ss,
622-
symbol_number next_sym)
634+
symbol_number next_sym,
635+
bool shift_reduce)
623636
{
624637
derivation *new_derivs[2];
625638
for (int i = 0; i < 2; ++i)
@@ -630,7 +643,8 @@ complete_diverging_examples (search_state *ss,
630643
new_derivs[i] = complete_diverging_example (next_sym, sitems, derivs);
631644
gl_list_free (sitems);
632645
}
633-
return new_counterexample (new_derivs[0], new_derivs[1], false, true);
646+
return new_counterexample (new_derivs[0], new_derivs[1],
647+
shift_reduce, false, true);
634648
}
635649

636650
/*
@@ -1111,7 +1125,7 @@ unifying_example (state_item_number itm1,
11111125
{
11121126
// Once we have two derivations for the same symbol,
11131127
// we've found a unifying counterexample.
1114-
cex = new_counterexample (d1, d2, true, false);
1128+
cex = new_counterexample (d1, d2, shift_reduce, true, false);
11151129
derivation_retain (d1);
11161130
derivation_retain (d2);
11171131
goto cex_search_end;
@@ -1149,7 +1163,7 @@ cex_search_end:;
11491163
// If a search state from Stage 3 is available, use it
11501164
// to construct a more compact nonunifying counterexample.
11511165
if (stage3result)
1152-
cex = complete_diverging_examples (stage3result, next_sym);
1166+
cex = complete_diverging_examples (stage3result, next_sym, shift_reduce);
11531167
// Otherwise, construct a nonunifying counterexample that
11541168
// begins from the start state using the shortest
11551169
// lookahead-sensitive path to the reduce item.
@@ -1226,6 +1240,8 @@ counterexample_report (state_item_number itm1, state_item_number itm2,
12261240
free_counterexample (cex);
12271241
}
12281242

1243+
1244+
// ITM1 denotes a shift, ITM2 a reduce.
12291245
static void
12301246
counterexample_report_shift_reduce (state_item_number itm1, state_item_number itm2,
12311247
symbol_number next_sym,

tests/conflicts.at

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -865,8 +865,8 @@ State 5
865865
1 exp: exp OP exp .
866866
1 exp: exp . OP exp
867867
Example exp OP exp . OP exp
868-
First derivation exp -> [ exp -> [ exp OP exp . ] OP exp ]
869-
Second derivation exp -> [ exp OP exp -> [ exp . OP exp ] ]
868+
Shift derivation exp -> [ exp OP exp -> [ exp . OP exp ] ]
869+
Reduce derivation exp -> [ exp -> [ exp OP exp . ] OP exp ]
870870

871871
]])
872872

0 commit comments

Comments
 (0)