e9d1ab6a2aa99d85d7dc831daa19536318b785e8
[bpt/coccinelle.git] / bundles / menhirLib / menhir-20120123 / src / reductionGraphs.ml
1 (**************************************************************************)
2 (* *)
3 (* Menhir *)
4 (* *)
5 (* François Pottier, INRIA Rocquencourt *)
6 (* Yann Régis-Gianas, PPS, Université Paris Diderot *)
7 (* *)
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. *)
12 (* *)
13 (**************************************************************************)
14
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. *)
17
18 (* This code is currently unused. It could be plugged in again (at the end of
19 the module [Invariant]. *)
20
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
25 size of the stack. *)
26
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. *)
30
31 exception NoDanger
32
33 let clique_count =
34 ref 0
35
36 let make_reduction_graph tok =
37
38 (* Build the reduction graph for this token. The main part of the work
39 is to define the edges; the rest is immediate. *)
40
41 let module ReductionGraph = struct
42
43 type node =
44 Lr1.node
45
46 let n =
47 Lr1.n
48
49 let index =
50 Lr1.number
51
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
55 after popping. *)
56
57 let rec pop_word n states w =
58 if n = 0 then
59 states
60 else
61 match w with
62 | [] ->
63 (* the invariant is too weak to ensure that reduction is possible! *)
64 assert false
65 | (_, states) :: w ->
66 pop_word (n-1) states w
67
68 let pop_stack n states (w, _) =
69 pop_word n states w
70
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. *)
74
75 let successors (action : int -> node -> unit) node : unit =
76 (* Find which reductions are permitted at this node. *)
77 let prods =
78 match has_default_reduction node with
79 | Some (prod, _) ->
80 [ prod ]
81 | None ->
82 try
83 TerminalMap.find tok (Lr1.reductions node)
84 with Not_found ->
85 []
86 in
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
94 | Some _ ->
95 ()
96 | None ->
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... *)
102 let increase = 1 in
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 =
107 try
108 SymbolMap.find symbol (Lr1.transitions state)
109 with Not_found ->
110 (* the invariant is too weak to ensure that goto is possible! *)
111 assert false
112 in
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)
117 ) states
118 ) prods
119
120 let iter =
121 Lr1.iter
122
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). *)
127
128 let adjacency : (int * node) list Lr1.NodeMap.t ref =
129 ref Lr1.NodeMap.empty
130
131 let () =
132 iter (fun source ->
133 (* Compute a list of outgoing edges. *)
134 let edges = ref [] in
135 successors (fun weight target ->
136 edges := (weight, target) :: !edges;
137 ) source;
138 let edges =
139 List.sort (fun (weight1, _) (weight2, _) -> weight1 - weight2) !edges
140 in
141 (* Define a predicate that accepts an edge only the first time
142 its target node is seen. *)
143 let seen =
144 ref Lr1.NodeSet.empty
145 in
146 let acceptable (_, node) =
147 if Lr1.NodeSet.mem node !seen then
148 false
149 else begin
150 seen := Lr1.NodeSet.add node !seen;
151 true
152 end
153 in
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
159 )
160
161 let successors (action : int -> node -> unit) source : unit =
162 let edges =
163 try
164 Lr1.NodeMap.find source !adjacency
165 with Not_found ->
166 assert false
167 in
168 List.iter (fun (weight, target) ->
169 action weight target
170 ) edges
171
172 end in
173
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. *)
178
179 (* Compute the strongly connected components. *)
180
181 let module SCC =
182 Tarjan.Run (struct
183 include ReductionGraph
184 (* Forget the edge labels. *)
185 let successors action node =
186 successors (fun _ target -> action target) node
187 end)
188 in
189
190 (* Examine the components, one at a time. *)
191
192 SCC.iter (fun representative elements ->
193 match elements with
194 | [] ->
195 assert false
196 | [ _ ] ->
197 ()
198 | _ ->
199
200 try
201
202 (* We have a non-trivial component. [representative] is its
203 representative, and [elements] is the list of its elements. *)
204
205 (* This auxiliary function tests whether a node is a member of
206 this component. *)
207
208 let member node =
209 Lr1.number (SCC.representative node) = Lr1.number representative
210 in
211
212 (* Build a description of this component. *)
213
214 let module Clique = struct
215
216 type node =
217 Lr1.node
218
219 (* Re-index the nodes. *)
220
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
225
226 let index node =
227 try
228 Lr1.NodeMap.find node node_to_new_index
229 with Not_found ->
230 assert false
231
232 (* Restrict the edges to only those that remain within this component. *)
233
234 let successors (action : int -> node -> unit) node : unit =
235 ReductionGraph.successors (fun label successor ->
236 if member successor then
237 action label successor
238 ) node
239
240 (* Restrict the vertices to only the elements of this component. *)
241
242 let iter (action : node -> unit) : unit =
243 List.iter action elements
244
245 end in
246
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. *)
249
250 (* Check whether at least one edge has nonpositive weight. If that is not
251 the case, then there is clearly no dangerous cycle. *)
252
253 let danger =
254 ref false
255 in
256
257 Clique.iter (fun node ->
258 Clique.successors (fun weight _ ->
259 if weight <= 0 then
260 danger := true
261 ) node
262 );
263
264 if not !danger then
265 raise NoDanger;
266
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
269 of zero weight. *)
270
271 let negative =
272 ref false
273 in
274
275 Clique.iter (fun node ->
276 Clique.successors (fun weight _ ->
277 if weight < 0 then
278 negative := true
279 ) node
280 );
281
282 if not !negative then begin
283 let module ZeroWeight = struct
284 include Clique
285 let successors action source =
286 successors (fun weight target ->
287 if weight = 0 then
288 action target
289 ) source
290 end in
291 let module ZeroWeightSCC =
292 Tarjan.Run (ZeroWeight)
293 in
294 danger := false;
295 ZeroWeightSCC.iter (fun _ elements ->
296 if List.length elements > 1 then
297 danger := true
298 )
299 end;
300
301 if not !danger then
302 raise NoDanger;
303
304 (* Use Floyd and Warshall's algorithm to determine if there is a dangerous
305 cycle. *)
306
307 let module NC =
308 NonpositiveCycles.Run(Clique)
309 in
310
311 if not NC.graph_has_nonpositive_simple_cycle then
312 raise NoDanger;
313
314 (* If there might be danger, then print this clique for manual examination. *)
315
316 let module PrintableClique = struct
317
318 include Clique
319
320 type vertex =
321 node
322
323 let name node =
324 Printf.sprintf "s%d" (Lr1.number node)
325
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
329 ) node
330
331 let iter (action: ?style:Dot.style -> label:string -> vertex -> unit) : unit =
332 iter (fun node ->
333 action ~label:(name node) node
334 )
335
336 end in
337
338 let filename =
339 Printf.sprintf "%s.%d.dot"
340 (Terminal.print tok)
341 (Misc.postincrement clique_count)
342 in
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;
346 close_out c
347
348 with NoDanger ->
349 ()
350
351 )
352
353 let () =
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
360 end
361