Commit 74b133ce authored by POTTIER Francois's avatar POTTIER Francois

Replaced the old FOLLOW computation with the new one.

parent 8e270475
......@@ -936,38 +936,6 @@ let solve (eqs : equations) : Nonterminal.t -> TerminalSet.t =
F.lfp nonterminal
(* ------------------------------------------------------------------------ *)
(* Generic support for fixpoint computations.
A fixpoint computation associates a property with every nonterminal.
A monotone function tells how properties are computed. [compute nt]
updates the property associated with nonterminal [nt] and returns a
flag that tells whether the property actually needed an update. The
state of the computation is maintained entirely inside [compute] and
is invisible here.
Whenever a property of [nt] is updated, the properties of the
terminals whose definitions depend on [nt] are updated. The
dependency graph must be explicitly supplied. *)
let fixpoint (dependencies : NonterminalSet.t array) (compute : Nonterminal.t -> bool) : unit =
let queue : Nonterminal.t Queue.t = Queue.create () in
let onqueue : bool array = Array.make Nonterminal.n true in
for i = 0 to Nonterminal.n - 1 do
Queue.add i queue
done;
Misc.qiter (fun nt ->
onqueue.(nt) <- false;
let changed = compute nt in
if changed then
NonterminalSet.iter (fun nt ->
if not onqueue.(nt) then begin
Queue.add nt queue;
onqueue.(nt) <- true
end
) dependencies.(nt)
) queue
(* ------------------------------------------------------------------------ *)
(* Compute which nonterminals are nonempty, that is, recognize a
nonempty language. Also, compute which nonterminals are
......@@ -1080,76 +1048,49 @@ let () =
this is useful for the SLR(1) test. Thus, we perform this analysis only
on demand. *)
let follow : TerminalSet.t array Lazy.t =
lazy (
let follow =
Array.make Nonterminal.n TerminalSet.empty
and forward : NonterminalSet.t array =
Array.make Nonterminal.n NonterminalSet.empty
and backward : NonterminalSet.t array =
Array.make Nonterminal.n NonterminalSet.empty
in
(* Iterate over all start symbols. *)
for nt = 0 to Nonterminal.start - 1 do
assert (Nonterminal.is_start nt);
(* Add # to FOLLOW(nt). *)
follow.(nt) <- TerminalSet.singleton Terminal.sharp
done;
(* We need to do this explicitly because our start productions are
of the form S' -> S, not S' -> S #, so # will not automatically
appear into FOLLOW(S) when the start productions are examined. *)
(* Iterate over all productions. *)
Array.iteri (fun prod (nt1, rhs) ->
(* Iterate over all nonterminal symbols [nt2] in the right-hand side. *)
Array.iteri (fun i symbol ->
match symbol with
| Symbol.T _ ->
()
| Symbol.N nt2 ->
let nullable = NULLABLE.production prod (i+1)
and first = FIRST.production prod (i+1) in
(* The FIRST set of the remainder of the right-hand side
contributes to the FOLLOW set of [nt2]. *)
follow.(nt2) <- TerminalSet.union first follow.(nt2);
(* If the remainder of the right-hand side is nullable,
FOLLOW(nt1) contributes to FOLLOW(nt2). *)
if nullable then begin
forward.(nt1) <- NonterminalSet.add nt2 forward.(nt1);
backward.(nt2) <- NonterminalSet.add nt1 backward.(nt2)
end
) rhs
) Production.table;
let follow : Nonterminal.t -> TerminalSet.t =
(* The fixpoint computation used here is not the most efficient
algorithm -- one could do better by first collapsing the
strongly connected components, then walking the graph in
topological order. But this will do. *)
(* First pass. Build a system of equations between sets of nonterminal
symbols. *)
fixpoint forward (fun nt ->
let original = follow.(nt) in
(* union over all contributors *)
let updated = NonterminalSet.fold (fun nt' accu ->
TerminalSet.union follow.(nt') accu
) backward.(nt) original in
follow.(nt) <- updated;
TerminalSet.compare original updated <> 0
);
let follow : equations =
Array.make Nonterminal.n []
in
follow
(* Iterate over all start symbols. *)
let sharp = MemberConstant (TerminalSet.singleton Terminal.sharp) in
for nt = 0 to Nonterminal.start - 1 do
assert (Nonterminal.is_start nt);
(* Add # to FOLLOW(nt). *)
follow.(nt) <- sharp :: follow.(nt)
done;
(* We need to do this explicitly because our start productions are
of the form S' -> S, not S' -> S #, so # will not automatically
appear into FOLLOW(S) when the start productions are examined. *)
)
(* Iterate over all productions. *)
Array.iteri (fun prod (nt1, rhs) ->
(* Iterate over all nonterminal symbols [nt2] in the right-hand side. *)
Array.iteri (fun i symbol ->
match symbol with
| Symbol.T _ ->
()
| Symbol.N nt2 ->
let nullable = NULLABLE.production prod (i+1)
and first = FIRST.production prod (i+1) in
(* The FIRST set of the remainder of the right-hand side
contributes to the FOLLOW set of [nt2]. *)
follow.(nt2) <- MemberConstant first :: follow.(nt2);
(* If the remainder of the right-hand side is nullable,
FOLLOW(nt1) contributes to FOLLOW(nt2). *)
if nullable then
follow.(nt2) <- MemberVar nt1 :: follow.(nt2)
) rhs
) Production.table;
(* Define an accessor that triggers the computation of the FOLLOW sets
if it has not been performed already. *)
(* Second pass. Solve the equations (on demand). *)
let follow nt =
(Lazy.force follow).(nt)
solve follow
(* At log level 2, display the FOLLOW sets. *)
......@@ -1213,56 +1154,6 @@ let () =
done
)
(* ------------------------------------------------------------------------ *)
(* Compute FOLLOW sets. *)
let follow' =
let follow : equations =
Array.make Nonterminal.n []
in
(* Iterate over all start symbols. *)
let sharp = MemberConstant (TerminalSet.singleton Terminal.sharp) in
for nt = 0 to Nonterminal.start - 1 do
assert (Nonterminal.is_start nt);
(* Add # to FOLLOW(nt). *)
follow.(nt) <- sharp :: follow.(nt)
done;
(* We need to do this explicitly because our start productions are
of the form S' -> S, not S' -> S #, so # will not automatically
appear into FOLLOW(S) when the start productions are examined. *)
(* Iterate over all productions. *)
Array.iteri (fun prod (nt1, rhs) ->
(* Iterate over all nonterminal symbols [nt2] in the right-hand side. *)
Array.iteri (fun i symbol ->
match symbol with
| Symbol.T _ ->
()
| Symbol.N nt2 ->
let nullable = NULLABLE.production prod (i+1)
and first = FIRST.production prod (i+1) in
(* The FIRST set of the remainder of the right-hand side
contributes to the FOLLOW set of [nt2]. *)
follow.(nt2) <- MemberConstant first :: follow.(nt2);
(* If the remainder of the right-hand side is nullable,
FOLLOW(nt1) contributes to FOLLOW(nt2). *)
if nullable then
follow.(nt2) <- MemberVar nt1 :: follow.(nt2)
) rhs
) Production.table;
solve follow
(* Sanity check. *)
let () =
for nt = 0 to Nonterminal.n - 1 do
let f = follow nt
and f'= follow' nt in
assert (TerminalSet.equal f f')
done
(* ------------------------------------------------------------------------ *)
(* Provide explanations about FIRST sets. *)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment