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 file contains experimental code that has been used in an attempt to
16 explore the validation of the termination property for LR(1) automata. *)
18 (* This code is currently unused. It could be plugged in again (at the end of
19 the module [Invariant]. *)
21 (* ------------------------------------------------------------------------ *)
22 (* Build a graph of all reductions. The vertices are the states of the automaton;
23 there is an edge from [s1] to [s2] if a reduction (including the goto step)
24 can take us from [s1] to [s2]. Every edge is labeled with its effect on the
27 (* This graph is built with respect to a fixed lookahead token [tok]. We
28 consider only the reductions that are permitted with [tok] is the next
29 token on the stream. *)
36 let make_reduction_graph tok
=
38 (* Build the reduction graph for this token. The main part of the work
39 is to define the edges; the rest is immediate. *)
41 let module ReductionGraph
= struct
52 (* This auxiliary function pops [n] cells off an abstract stack. The
53 parameter [states] is a set of states that we might be in before
54 popping. The function returns a set of states that we might be in
57 let rec pop_word n states w
=
63 (* the invariant is too weak to ensure that reduction is possible! *)
66 pop_word (n-1) states w
68 let pop_stack n states
(w
, _
) =
71 (* The following function allows listing the successors of a node. Each
72 edge is labeled with an integer value that represents the decrease in
73 the size of the stack. *)
75 let successors (action
: int -> node
-> unit) node
: unit =
76 (* Find which reductions are permitted at this node. *)
78 match has_default_reduction node
with
83 TerminalMap.find tok
(Lr1.reductions node
)
87 (* Get a description of the stack at this node. *)
88 let stack = lfp node
in
89 (* For each production [prod], ... *)
90 List.iter
(fun prod
->
91 (* If this is a start production, ignore it. We are not interested in
92 accept actions, only in true reduce actions. *)
93 match Production.classify prod
with
97 (* Find out how many cells are popped. *)
98 let decrease = Production.length prod
in
99 (* Find out what states we might be in after popping. *)
100 let states = pop_stack decrease (Lr1.NodeSet.singleton node
) stack in
101 (* Now, the goto step pushes one cell... *)
103 let net = decrease - increase in
104 (* Find out which states we might be in after the goto step. *)
105 let symbol = Symbol.N
(Production.nt prod
) in
106 let goto (state
: Lr1.node
) : Lr1.node
=
108 SymbolMap.find
symbol (Lr1.transitions state
)
110 (* the invariant is too weak to ensure that goto is possible! *)
113 (* There is a transition, labelled [decrease - increase], from [node]
114 to every state in the image through [goto] of the set [states]. *)
115 Lr1.NodeSet.iter
(fun state
->
116 action
net (goto state
)
123 (* The [successors] function describes a multi-graph: there might be multiple
124 edges with the same source and target nodes. In that case, we would like
125 to keep only one, the one with minimum weight, as this is the most dangerous
126 one. Do so (naively). *)
128 let adjacency : (int * node
) list
Lr1.NodeMap.t
ref =
129 ref Lr1.NodeMap.empty
133 (* Compute a list of outgoing edges. *)
134 let edges = ref [] in
135 successors (fun weight target
->
136 edges := (weight
, target
) :: !edges;
139 List.sort
(fun (weight1
, _
) (weight2
, _
) -> weight1
- weight2
) !edges
141 (* Define a predicate that accepts an edge only the first time
142 its target node is seen. *)
144 ref Lr1.NodeSet.empty
146 let acceptable (_
, node
) =
147 if Lr1.NodeSet.mem node
!seen then
150 seen := Lr1.NodeSet.add node
!seen;
154 (* Filter the list of edges. This relies on [filter] evaluating the
155 predicate left-to-right. *)
156 let edges = List.filter
acceptable edges in
157 (* Augment the table. *)
158 adjacency := Lr1.NodeMap.add source
edges !adjacency
161 let successors (action
: int -> node
-> unit) source
: unit =
164 Lr1.NodeMap.find source
!adjacency
168 List.iter (fun (weight
, target
) ->
174 (* We are interested in determining whether the reduction graph contains
175 simple cycles of nonpositive weight. In order to answer this question,
176 it is sufficient (and more tractable) to consider each strongly connected
177 component separately. *)
179 (* Compute the strongly connected components. *)
183 include ReductionGraph
184 (* Forget the edge labels. *)
185 let successors action node
=
186 successors (fun _ target
-> action target
) node
190 (* Examine the components, one at a time. *)
192 SCC.iter (fun representative elements
->
202 (* We have a non-trivial component. [representative] is its
203 representative, and [elements] is the list of its elements. *)
205 (* This auxiliary function tests whether a node is a member of
209 Lr1.number
(SCC.representative node
) = Lr1.number representative
212 (* Build a description of this component. *)
214 let module Clique
= struct
219 (* Re-index the nodes. *)
221 let n, node_to_new_index
=
222 List.fold_left
(fun (n, map
) node
->
223 n + 1, Lr1.NodeMap.add node
n map
224 ) (0, Lr1.NodeMap.empty
) elements
228 Lr1.NodeMap.find node node_to_new_index
232 (* Restrict the edges to only those that remain within this component. *)
234 let successors (action
: int -> node
-> unit) node
: unit =
235 ReductionGraph.successors (fun label successor
->
236 if member successor
then
237 action label successor
240 (* Restrict the vertices to only the elements of this component. *)
242 let iter (action
: node
-> unit) : unit =
243 List.iter action elements
247 (* In the following, we perform several tests, of increasing strength and
248 cost, to determine whether there is a dangerous cycle in the clique. *)
250 (* Check whether at least one edge has nonpositive weight. If that is not
251 the case, then there is clearly no dangerous cycle. *)
257 Clique.iter (fun node
->
258 Clique.successors (fun weight _
->
267 (* Check whether there is at least one edge of negative weight. If not,
268 look for a non-trivial strongly connected component among the edges
275 Clique.iter (fun node
->
276 Clique.successors (fun weight _
->
282 if not
!negative then begin
283 let module ZeroWeight
= struct
285 let successors action source
=
286 successors (fun weight target
->
291 let module ZeroWeightSCC
=
292 Tarjan.Run
(ZeroWeight
)
295 ZeroWeightSCC.iter (fun _ elements
->
296 if List.length elements
> 1 then
304 (* Use Floyd and Warshall's algorithm to determine if there is a dangerous
308 NonpositiveCycles.Run
(Clique
)
311 if not
NC.graph_has_nonpositive_simple_cycle
then
314 (* If there might be danger, then print this clique for manual examination. *)
316 let module PrintableClique
= struct
324 Printf.sprintf
"s%d" (Lr1.number node
)
326 let successors (action
: ?style
:Dot.style
-> label
:string -> vertex
-> unit) node
: unit =
327 successors (fun label successor
->
328 action ~label
:(string_of_int label
) successor
331 let iter (action
: ?style
:Dot.style
-> label
:string -> vertex
-> unit) : unit =
333 action ~label
:(name node
) node
339 Printf.sprintf
"%s.%d.dot"
341 (Misc.postincrement
clique_count)
343 let c = open_out
filename in
344 let module P
= Dot.Print
(PrintableClique
) in
345 P.print ~orientation
:Dot.Portrait ~size
:(8.,5.) c;
354 (* The graphs are built, and printed to .dot files, only if requested by
355 the user via [--make-reduction-graphs]. The reduction graphs are not
356 used by Menhir itself. *)
357 if Settings.make_reduction_graphs
then begin
358 Terminal.iter make_reduction_graph;
359 Printf.fprintf stderr
"Constructed %d potentially interesting reduction cliques.\n" !clique_count