Skip to content

Commit 56df80a

Browse files
committed
Fix #354 by relaxing %expect to check for upper bound
1 parent 4613f8b commit 56df80a

4 files changed

Lines changed: 49 additions & 7 deletions

File tree

app/Main.lhs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,19 @@ Pretty print the AbsSyn.
165165
Report any conflicts in the grammar.
166166

167167
> case expect common_options of
168-
> Just n | n == sr && rr == 0 -> return ()
169168
> Just _ | rr > 0 ->
170169
> die ("The grammar has reduce/reduce conflicts.\n" ++
171170
> "This is not allowed when an expect directive is given\n")
172-
> Just _ ->
171+
> Just n | sr > n ->
173172
> die ("The grammar has " ++ show sr ++
174173
> " shift/reduce conflicts.\n" ++
175-
> "This is different from the number given in the " ++
176-
> "expect directive\n")
174+
> "This is more than the " ++ show n ++
175+
> " given in the %expect directive\n")
176+
> Just n | sr < n ->
177+
> do hPutStrLn stderr ("shift/reduce conflicts: " ++ show sr)
178+
> hPutStrLn stderr ("(the %expect directive permits up to " ++
179+
> show n ++ ")")
180+
> Just _ -> return ()
177181
> _ -> do
178182

179183
> (if sr /= 0

doc/syntax.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,11 +255,13 @@ These conflicts generate warnings.
255255
But when you have checked the warnings and made sure that Happy handles them correctly these warnings are just annoying.
256256
The ``%expect`` directive gives a way of avoiding them.
257257
Declaring ``%expect n`` is a way of telling Happy
258-
“There are exactly <n> shift/reduce conflicts and zero reduce/reduce conflicts in this grammar.
258+
“There are at most <n> shift/reduce conflicts and zero reduce/reduce conflicts in this grammar.
259259
I promise I have checked them and they are resolved correctly”.
260260
When processing the grammar, Happy will check the actual number of conflicts against the ``%expect`` declaration if any, and if there is a discrepancy then an error will be reported.
261261

262-
Happy's ``%expect`` directive works exactly like that of yacc.
262+
Happy's ``%expect`` directive works like that of yacc, but relaxes the check to
263+
“at most <n> conflicts” instead of “exactly <n> conflicts” to allow for
264+
backward compatible improvements of the state machine.
263265

264266
.. _sec-error-directive:
265267

tests/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ TESTS = Test.ly TestMulti.ly TestPrecedence.ly bug001.ly \
4141
AttrGrammar001.y AttrGrammar002.y \
4242
Pragma.y
4343

44-
ERROR_TESTS = error001.y issue335.y
44+
ERROR_TESTS = error001.y expect_upper.y issue335.y
4545

4646
# NOTE: `cabal` will set the `happy_datadir` env-var accordingly before invoking the test-suite
4747
#TEST_HAPPY_OPTS = --strict --template=..

tests/expect_upper.y

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
-- Test that %expect checks for an upper bound, not an exact match.
2+
-- This grammar has exactly 1 shift/reduce conflict (the dangling else).
3+
-- %expect 2 should succeed because 1 <= 2.
4+
5+
{
6+
module Main where
7+
}
8+
9+
%expect 2
10+
%expect 1
11+
%name parse
12+
%tokentype { Token }
13+
14+
%token
15+
'if' { If }
16+
'then' { Then }
17+
'else' { Else }
18+
'x' { X }
19+
20+
%%
21+
22+
stmt : 'if' 'x' 'then' stmt 'else' stmt { $4 ++ $6 }
23+
| 'if' 'x' 'then' stmt { $4 }
24+
| 'x' { "x" }
25+
26+
{
27+
main :: IO ()
28+
main =
29+
if parse [If, X, Then, X] == "x"
30+
then return ()
31+
else error "bad parse"
32+
33+
data Token = If | Then | Else | X
34+
35+
happyError _ = error "parse error"
36+
}

0 commit comments

Comments
 (0)