1 (**************************************************************************)
5 (* François Pottier, INRIA Rocquencourt *)
6 (* Yann Régis-Gianas, PPS, Université Paris Diderot *)
8 (* Copyright 2005-2008 Institut National de Recherche en Informatique *)
9 (* et en Automatique. All rights reserved. This file is distributed *)
10 (* under the terms of the Q Public License version 1.0, with the change *)
11 (* described in file LICENSE. *)
13 (**************************************************************************)
15 (* This module extends the LR(0) automaton with lookahead information in order
16 to construct an SLR(1) automaton. The lookahead information is obtained by
17 considering the FOLLOW sets. *)
19 (* This construction is not used by Menhir, but can be used to check whether
20 the grammar is in the class SLR(1). This check is performed when the log
21 level [lg] is at least 1. *)
25 (* This flag, which is reserved for internal use, causes more information
26 about SLR(1) conflict states to be printed. *)
28 let tell_me_everything =
31 (* The following function turns an LR(0) state into an SLR(1) state. *)
33 let make_slr_state (s
: Lr0.node
) : Lr0.concretelr1state
=
35 (* Obtain the set of LR(0) items associated with the state [s]. *)
37 let items = Lr0.items s
in
39 (* Unfortunately, this set is not closed. We do not have a function that
40 computes the closure of a set of LR(0) items -- we could build one using
41 [Item.Closure], but that would be overkill. So, we first convert this
42 set to a set of LR(1) items, then compute the closure at this level, and
43 finally we turn this LR(1) state into an SLR(1) state by letting the
44 lookahead sets be the FOLLOW sets. This is somewhat ugly and naïve, but
47 (* Convert this set to a set of LR(1) items. Here, we can use any set of
48 tokens as the lookahead set. We use the empty set. *)
50 let s = Item.Map.lift
(fun item
-> TerminalSet.empty
) items in
52 (* Compute the LR(1) closure. *)
54 let s = Lr0.closure
s in
56 (* We now have an LR(1) state that has the correct set of LR(0) items but
57 phony lookahead information. We convert it into an SLR(1) state by
58 deciding that, for each item, the lookahead set is the FOLLOW set of the
59 symbol that appears on the left-hand side of the item. *)
61 Item.Map.fold
(fun item toks accu
->
62 let _, nt
, _, _, _ = Item.def item
in
63 let follow_nt = Analysis.follow nt
in
64 assert (TerminalSet.subset toks
follow_nt); (* sanity check *)
65 Item.Map.add item
follow_nt accu
68 (* Insertion of a new reduce action into the table of reductions. Copied
69 from [Lr1] (boo, hiss). *)
71 let addl prod tok reductions
=
74 TerminalMap.lookup tok reductions
78 TerminalMap.add tok
(prod
:: prods) reductions
80 (* Same thing, for a set of tokens. *)
82 let addl prod toks reductions
=
83 TerminalSet.fold
(addl prod
) toks reductions
85 (* The following function turns a closed LR(1) state into a map of terminal
86 symbols to reduction actions. Copied from a related function in [Lr0]. *)
88 let reductions (s : Lr0.concretelr1state
) : Production.index list
TerminalMap.t
=
89 Item.Map.fold
(fun item toks
reductions ->
90 match Item.classify item
with
92 addl prod toks
reductions
97 (* The following function turns a closed LR(1) state into a set of shift
100 let transitions (s : Lr0.concretelr1state
) : TerminalSet.t
=
101 Item.Map.fold
(fun item
_ transitions ->
102 match Item.classify item
with
103 | Item.Shift
(Symbol.T tok
, _) ->
104 TerminalSet.add tok
transitions
105 | Item.Shift
(Symbol.N
_, _)
108 ) s TerminalSet.empty
110 (* This function computes the domain of a terminal map, producing a terminal
113 let domain (m
: 'a
TerminalMap.t
) : TerminalSet.t
=
114 TerminalMap.fold
(fun tok
_ accu
->
115 TerminalSet.add tok accu
116 ) m
TerminalSet.empty
118 (* The following function checks whether a closed LR(1) state is free of
121 let state_is_ok (s : Lr0.concretelr1state
) : bool =
123 let reductions = reductions s
124 and transitions = transitions s in
126 (* Check for shift/reduce conflicts. *)
128 TerminalSet.disjoint
transitions (domain reductions) &&
130 (* Check for reduce/reduce conflicts. *)
132 TerminalMap.fold
(fun _ prods ok
->
133 ok
&& match prods with
141 (* The following function counts the number of states in the SLR(1) automaton
142 that have a conflict. *)
144 let count_slr_violations () : int =
148 for s = 0 to Lr0.n
- 1 do
149 let s = make_slr_state s in
150 if not
(state_is_ok s) then begin
152 if tell_me_everything then
155 "The following SLR(1) state has a conflict:\n%s"
156 (Lr0.print_concrete
s)
162 (* At log level 1, indicate whether the grammar is SLR(1). *)
165 Error.logG
1 (fun f
->
166 let count = count_slr_violations() in
168 Printf.fprintf f
"The grammar is SLR(1).\n"
170 Printf.fprintf f
"The grammar is not SLR(1) -- %d states have a conflict.\n" count