167 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			OCaml
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			OCaml
		
	
	
	
	
	
| (***********************************************************************)
 | |
| (*                                                                     *)
 | |
| (*                             Heptagon                                *)
 | |
| (*                                                                     *)
 | |
| (* Gwenael Delaval, LIG/INRIA, UJF                                     *)
 | |
| (* Leonard Gerard, Parkas, ENS                                         *)
 | |
| (* Adrien Guatto, Parkas, ENS                                          *)
 | |
| (* Cedric Pasteur, Parkas, ENS                                         *)
 | |
| (* Marc Pouzet, Parkas, ENS                                            *)
 | |
| (*                                                                     *)
 | |
| (* Copyright 2012 ENS, INRIA, UJF                                      *)
 | |
| (*                                                                     *)
 | |
| (* This file is part of the Heptagon compiler.                         *)
 | |
| (*                                                                     *)
 | |
| (* Heptagon is free software: you can redistribute it and/or modify it *)
 | |
| (* under the terms of the GNU General Public License as published by   *)
 | |
| (* the Free Software Foundation, either version 3 of the License, or   *)
 | |
| (* (at your option) any later version.                                 *)
 | |
| (*                                                                     *)
 | |
| (* Heptagon is distributed in the hope that it will be useful,         *)
 | |
| (* but WITHOUT ANY WARRANTY; without even the implied warranty of      *)
 | |
| (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       *)
 | |
| (* GNU General Public License for more details.                        *)
 | |
| (*                                                                     *)
 | |
| (* You should have received a copy of the GNU General Public License   *)
 | |
| (* along with Heptagon.  If not, see <http://www.gnu.org/licenses/>    *)
 | |
| (*                                                                     *)
 | |
| (***********************************************************************)
 | |
| (* graph manipulation *)
 | |
| 
 | |
| type 'a graph =
 | |
|     { g_top: 'a node list;
 | |
|       g_bot: 'a node list }
 | |
| 
 | |
| and 'a node =
 | |
|     { g_containt: 'a;
 | |
|       g_tag: int;
 | |
|       mutable g_visited: bool;
 | |
|       mutable g_mark: int;
 | |
|       mutable g_depends_on: 'a node list;
 | |
|       mutable g_depends_by: 'a node list;
 | |
|     }
 | |
| 
 | |
| exception Cycle of int (* returns the index of the node *)
 | |
| 
 | |
| let tag = ref 0
 | |
| let new_tag () = incr tag; !tag
 | |
| let containt g = g.g_containt
 | |
| let linked g1 g2 =
 | |
|   (List.memq g2 g1.g_depends_on) or (List.memq g1 g2.g_depends_on)
 | |
| let make c =
 | |
|   { g_containt = c; g_tag = new_tag (); g_visited = false;
 | |
|     g_mark = -1; g_depends_on = []; g_depends_by = [] }
 | |
| let add_depends node1 node2 =
 | |
|   if not (node1.g_tag = node2.g_tag) then (
 | |
|     node1.g_depends_on <- node2 :: node1.g_depends_on;
 | |
|     node2.g_depends_by <- node1 :: node2.g_depends_by
 | |
|   )
 | |
| 
 | |
| let remove_depends node1 node2 =
 | |
|   if not (node1.g_tag = node2.g_tag)
 | |
|   then (
 | |
|     node1.g_depends_on <-
 | |
|       List.filter (fun n -> n.g_tag <> node2.g_tag) node1.g_depends_on;
 | |
|     node2.g_depends_by <-
 | |
|       List.filter (fun n -> n.g_tag <> node1.g_tag) node2.g_depends_by
 | |
|   )
 | |
| 
 | |
| let graph top_list bot_list = { g_top = top_list; g_bot = bot_list }
 | |
| 
 | |
| 
 | |
| let topological g_list =
 | |
|   let rec sortrec g_list seq =
 | |
|     match g_list with
 | |
|       | [] -> seq
 | |
|       | g :: g_list ->
 | |
|           if g.g_visited then sortrec g_list seq
 | |
|           else
 | |
|             begin
 | |
|               g.g_visited <- true;
 | |
|               let seq = sortrec g.g_depends_on seq in
 | |
|               sortrec g_list (g :: seq)
 | |
|             end in
 | |
|   let seq = sortrec g_list [] in
 | |
|   List.iter
 | |
|     (fun ({ g_visited = _ } as node) -> node.g_visited <- false) g_list;
 | |
|   List.rev seq
 | |
| 
 | |
| (** Detection of cycles *)
 | |
| (* Mark nodes with:
 | |
|    - -1 initially, for unvisited nodes
 | |
|    - 0 for "opened" nodes, currently visited, while visiting its descendents
 | |
|    - 1 for "closed" nodes, visited once, no circuits found from it.
 | |
|    A circuit is found when a node marked with 0 is visited again.
 | |
| *)
 | |
| 
 | |
| let cycle g_list =
 | |
|   (* store nodes in a stack *)
 | |
|   let s = Stack.create () in
 | |
|   (* flush the connected component *)
 | |
|   let rec flush index =
 | |
|     if Stack.is_empty s then []
 | |
|     else let v = Stack.pop s in
 | |
|     v.g_containt :: flush v.g_tag in
 | |
| 
 | |
|   let rec visit g =
 | |
|     match g.g_mark with
 | |
|       | -1 ->
 | |
|           (* Unvisited yet *)
 | |
|           (* Open node *)
 | |
|           Stack.push g s;
 | |
|           g.g_mark <- 0;
 | |
|           (* Visit descendents *)
 | |
|           List.iter visit g.g_depends_on;
 | |
|           (* Close node *)
 | |
|           ignore (Stack.pop s);
 | |
|           g.g_mark <- 1
 | |
|       | 0 ->
 | |
|           (* Visit an opened node (visited and not close) : circuit *)
 | |
|           raise (Cycle g.g_tag)
 | |
|       | 1 | _ ->
 | |
|           (* Visit a closed node (no existing circuit) : pass *)
 | |
|           () in
 | |
|   try
 | |
|     List.iter visit g_list; None
 | |
|   with
 | |
|     | Cycle(index) -> Some(flush index)
 | |
| 
 | |
| (** [accessible useful_nodes g_list] returns the list of
 | |
|     accessible nodes starting from useful_nodes and belonging to
 | |
|     g_list. *)
 | |
| let accessible useful_nodes g_list =
 | |
|   let rec follow g =
 | |
|     if not g.g_visited then
 | |
|       begin
 | |
|         g.g_visited <- true;
 | |
|         List.iter follow g.g_depends_on
 | |
|       end in
 | |
|   let read acc g =
 | |
|     if g.g_visited then begin g.g_visited <- false; g :: acc end else acc in
 | |
|   List.iter follow useful_nodes;
 | |
|   List.fold_left read [] g_list
 | |
| 
 | |
| (** [exists_path nodes n1 n2] returns whether there is a path
 | |
|     from n1 to n2 in the graph. nodes is the list of all the nodes
 | |
|     in the graph. *)
 | |
| let exists_path nodes n1 n2 =
 | |
|   List.mem n2 (accessible [n1] nodes)
 | |
| 
 | |
| (*
 | |
| open Format
 | |
| 
 | |
| let print_node print g =
 | |
|   printf "Node : @[<hov>";
 | |
|   print_int g.g_tag;
 | |
|   printf "@]";
 | |
|   printf "  Depends on :@\n";
 | |
|   printf "  @[<v>";
 | |
|   List.iter
 | |
|     (fun node ->
 | |
|        printf "@[<hov 2>";
 | |
|        print_int node.g_tag;
 | |
|        printf "@]@ ")
 | |
|     g.g_depends_on;
 | |
|   printf "@]"
 | |
| *)
 | |
| 
 | 
