objective categorical abstract machine language personal data server
65
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 226 lines 7.8 kB view raw
1open Lexicon_types 2 3(** returns SCCs in reverse topological order (dependencies first) 4 each SCC is a list of nodes *) 5let find_sccs (type node) (nodes : node list) ~(get_id : node -> string) 6 ~(get_deps : node -> string list) : node list list = 7 (* build node map: id -> node *) 8 let node_map = 9 List.fold_left (fun m node -> (get_id node, node) :: m) [] nodes 10 in 11 let node_ids = List.map get_id nodes in 12 (* build dependency map *) 13 let deps = List.map (fun node -> (get_id node, get_deps node)) nodes in 14 (* Tarjan's algorithm state *) 15 let index_counter = ref 0 in 16 let indices = Hashtbl.create 64 in 17 let lowlinks = Hashtbl.create 64 in 18 let on_stack = Hashtbl.create 64 in 19 let stack = ref [] in 20 let sccs = ref [] in 21 let rec strongconnect id = 22 let index = !index_counter in 23 incr index_counter ; 24 Hashtbl.add indices id index ; 25 Hashtbl.add lowlinks id index ; 26 Hashtbl.add on_stack id true ; 27 stack := id :: !stack ; 28 (* visit successors *) 29 let successors = 30 try List.assoc id deps |> List.filter (fun s -> List.mem s node_ids) 31 with Not_found -> [] 32 in 33 List.iter 34 (fun succ -> 35 if not (Hashtbl.mem indices succ) then begin 36 (* successor not yet visited *) 37 strongconnect succ ; 38 Hashtbl.replace lowlinks id 39 (min (Hashtbl.find lowlinks id) (Hashtbl.find lowlinks succ)) 40 end 41 else if Hashtbl.find_opt on_stack succ = Some true then 42 (* successor is on stack, part of current SCC *) 43 Hashtbl.replace lowlinks id 44 (min (Hashtbl.find lowlinks id) (Hashtbl.find indices succ)) ) 45 successors ; 46 (* if this is a root node, pop the SCC *) 47 if Hashtbl.find lowlinks id = Hashtbl.find indices id then begin 48 let rec pop_scc acc = 49 match !stack with 50 | [] -> 51 acc 52 | top :: rest -> 53 stack := rest ; 54 Hashtbl.replace on_stack top false ; 55 if top = id then top :: acc else pop_scc (top :: acc) 56 in 57 let scc_ids = pop_scc [] in 58 (* convert IDs to nodes, preserving original order *) 59 let scc_nodes = 60 List.filter_map 61 (fun n -> List.assoc_opt n node_map) 62 (List.filter (fun n -> List.mem n scc_ids) node_ids) 63 in 64 if scc_nodes <> [] then sccs := scc_nodes :: !sccs 65 end 66 in 67 (* run on all nodes *) 68 List.iter 69 (fun id -> if not (Hashtbl.mem indices id) then strongconnect id) 70 node_ids ; 71 (* SCCs are prepended, so reverse to get topological order *) 72 List.rev !sccs 73 74(** returns list of definition names that this type depends on within the same nsid *) 75let rec collect_local_refs nsid acc = function 76 | Array {items; _} -> 77 collect_local_refs nsid acc items 78 | Ref {ref_; _} -> 79 if String.length ref_ > 0 && ref_.[0] = '#' then 80 (* local ref: #foo *) 81 let def_name = String.sub ref_ 1 (String.length ref_ - 1) in 82 def_name :: acc 83 else 84 (* check if it's a self-reference: nsid#foo *) 85 begin match String.split_on_char '#' ref_ with 86 | [ext_nsid; def_name] when ext_nsid = nsid -> 87 def_name :: acc 88 | _ -> 89 acc 90 end 91 | Union {refs; _} -> 92 List.fold_left 93 (fun a r -> 94 if String.length r > 0 && r.[0] = '#' then 95 let def_name = String.sub r 1 (String.length r - 1) in 96 def_name :: a 97 else 98 match String.split_on_char '#' r with 99 | [ext_nsid; def_name] when ext_nsid = nsid -> 100 def_name :: a 101 | _ -> 102 a ) 103 acc refs 104 | Object {properties; _} -> 105 List.fold_left 106 (fun a (_, (prop : property)) -> collect_local_refs nsid a prop.type_def) 107 acc properties 108 | Record {record; _} -> 109 List.fold_left 110 (fun a (_, (prop : property)) -> collect_local_refs nsid a prop.type_def) 111 acc record.properties 112 | Query {parameters; output; _} -> ( 113 let acc = 114 match parameters with 115 | Some params -> 116 List.fold_left 117 (fun a (_, (prop : property)) -> 118 collect_local_refs nsid a prop.type_def ) 119 acc params.properties 120 | None -> 121 acc 122 in 123 match output with 124 | Some body -> 125 Option.fold ~none:acc ~some:(collect_local_refs nsid acc) body.schema 126 | None -> 127 acc ) 128 | Procedure {parameters; input; output; _} -> ( 129 let acc = 130 match parameters with 131 | Some params -> 132 List.fold_left 133 (fun a (_, (prop : property)) -> 134 collect_local_refs nsid a prop.type_def ) 135 acc params.properties 136 | None -> 137 acc 138 in 139 let acc = 140 match input with 141 | Some body -> 142 Option.fold ~none:acc 143 ~some:(collect_local_refs nsid acc) 144 body.schema 145 | None -> 146 acc 147 in 148 match output with 149 | Some body -> 150 Option.fold ~none:acc ~some:(collect_local_refs nsid acc) body.schema 151 | None -> 152 acc ) 153 | _ -> 154 acc 155 156(** find SCCs among definitions within a single lexicon 157 returns SCCs in reverse topological order *) 158let find_def_sccs nsid (defs : def_entry list) : def_entry list list = 159 find_sccs defs 160 ~get_id:(fun def -> def.name) 161 ~get_deps:(fun def -> collect_local_refs nsid [] def.type_def) 162 163(** get external nsid dependencies for a lexicon *) 164let get_external_nsids (doc : lexicon_doc) : string list = 165 let nsids = ref [] in 166 let add_nsid s = if not (List.mem s !nsids) then nsids := s :: !nsids in 167 let rec collect_from_type = function 168 | Array {items; _} -> 169 collect_from_type items 170 | Ref {ref_; _} -> 171 if String.length ref_ > 0 && ref_.[0] <> '#' then 172 begin match String.split_on_char '#' ref_ with 173 | ext_nsid :: _ -> 174 add_nsid ext_nsid 175 | [] -> 176 () 177 end 178 | Union {refs; _} -> 179 List.iter 180 (fun r -> 181 if String.length r > 0 && r.[0] <> '#' then 182 match String.split_on_char '#' r with 183 | ext_nsid :: _ -> 184 add_nsid ext_nsid 185 | [] -> 186 () ) 187 refs 188 | Object {properties; _} -> 189 List.iter 190 (fun (_, (prop : property)) -> collect_from_type prop.type_def) 191 properties 192 | Query {parameters; output; _} -> 193 Option.iter 194 (fun p -> 195 List.iter 196 (fun (_, (prop : property)) -> collect_from_type prop.type_def) 197 p.properties ) 198 parameters ; 199 Option.iter (fun o -> Option.iter collect_from_type o.schema) output 200 | Procedure {parameters; input; output; _} -> 201 Option.iter 202 (fun p -> 203 List.iter 204 (fun (_, (prop : property)) -> collect_from_type prop.type_def) 205 p.properties ) 206 parameters ; 207 Option.iter (fun i -> Option.iter collect_from_type i.schema) input ; 208 Option.iter (fun o -> Option.iter collect_from_type o.schema) output 209 | Record {record; _} -> 210 List.iter 211 (fun (_, (prop : property)) -> collect_from_type prop.type_def) 212 record.properties 213 | _ -> 214 () 215 in 216 List.iter (fun def -> collect_from_type def.type_def) doc.defs ; 217 !nsids 218 219(** find SCCs between lexicon files, in reverse topological order *) 220let find_file_sccs (lexicons : lexicon_doc list) : lexicon_doc list list = 221 let nsids = List.map (fun doc -> doc.id) lexicons in 222 find_sccs lexicons 223 ~get_id:(fun doc -> doc.id) 224 ~get_deps:(fun doc -> 225 (* filter to only include nsids we have *) 226 get_external_nsids doc |> List.filter (fun n -> List.mem n nsids) )