1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
A em(reduce/reduce conflict) occurs if there are two or more rules that apply
to the same sequence of input. This usually indicates a serious error in the
grammar.
For example, here is an erroneous attempt to define a sequence of zero or more
words:
verbinsert(-as4 demos/rr1)
The error is an ambiguity: there is more than one way to parse a single
word into a sequence. It could be reduced to a maybeword and then into a
sequence via the second rule. Alternatively, nothing-at-all could be reduced
into a sequence via the first rule, and this could be combined with the word
using the third rule for sequence.
There is also more than one way to reduce nothing-at-all into a sequence. This
can be done directly via the first rule, or indirectly via maybeword and then
the second rule.
You might think that this is a distinction without a difference, because it
does not change whether any particular input is valid or not. But it does
affect which actions are run. One parsing order runs the second rule's action;
the other runs the first rule's action and the third rule's action. In this
example, the output of the program changes.
B() resolves a reduce/reduce conflict by choosing to use the rule that appears
first in the grammar, but it is very risky to rely on this. Every
reduce/reduce conflict must be studied and usually eliminated. Here is the
proper way to define sequence:
verb(
sequence:
// empty
{ printf ("empty sequence\n"); }
| sequence word
{ printf ("added word %s\n", $2); }
;
)
Here is another common error that yields a reduce/reduce conflict:
verb(
sequence:
// empty
| sequence words
| sequence redirects
;
words:
// empty
| words word
;
redirects:
// empty
| redirects redirect
;
)
The intention here is to define a sequence containing either tt(word) or
tt(redirect) nonterminals. The individual definitions of sequence, words and
redirects are error-free, but the three together make a subtle ambiguity: even
an empty input can be parsed in infinitely many ways!
Consider: nothing-at-all could be a words. Or it could be two words in a row,
or three, or any number. It could equally well be a redirects, or two, or any
number. Or it could be a words followed by three redirects and another
words. And so on.
Here are two ways to correct these rules. First, to make it a single level of
sequence:
verb(
sequence:
// empty
| sequence word
| sequence redirect
;
)
Second, to prevent either a words or a redirects from being empty:
verb(
sequence:
// empty
| sequence words
| sequence redirects
;
words:
word
| words word
;
redirects:
redirect
| redirects redirect
;
)
|