1(* 2 * Copyright 2014, NICTA 3 * 4 * This software may be distributed and modified according to the terms of 5 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 6 * See "LICENSE_BSD2.txt" for details. 7 * 8 * @TAG(NICTA_BSD) 9 *) 10 11fun all_eq (a :: b :: l) = if a = b then all_eq (b :: l) else false 12 | all_eq [_] = true 13 | all_eq [] = true 14 15fun common_head (lss as (l :: _)) = 16let 17 fun common_head i = if all_eq (map (fn ls => List.nth (ls,i)) lss) handle Subscript => false then common_head (i + 1) else i 18in 19 List.take (l,common_head 0) end 20 | common_head [] = [] 21 22fun common_prefix strs = String.implode (common_head (map String.explode strs)) 23 24 25signature PROOF_GRAPH = 26sig 27 28 29type proof_entry = {name : string, file : string, lines : int * int, 30 prems : int list list, concl : int list, kind : Proof_Count.lemmaT option} 31 32val contains : proof_entry -> int list 33 34val map_contains : (int list -> int list) -> proof_entry -> proof_entry 35 36val size_of : proof_entry -> int 37 38val proper_theory_list : (string * string list) Symtab.table -> string list -> string list 39 40val get_full_spec : theory -> Spec_Graph.entry Int_Graph.T * proof_entry String_Graph.T * (string * string list) Symtab.table 41 42val write_graph_spec_of : Spec_Graph.entry Int_Graph.T * proof_entry String_Graph.T * (string * string list) Symtab.table -> string -> string -> unit 43 44val read_graph_spec_from : string -> 45 Spec_Graph.entry Int_Graph.T * proof_entry String_Graph.T * (string * string list) Symtab.table 46 47val restrict_subgraph : (String_Graph.key * 'a -> bool) -> 'a String_Graph.T -> 'a String_Graph.T 48 49val merge_multi_thms : proof_entry String_Graph.T -> proof_entry String_Graph.T 50 51val relative_path_of : string -> string 52 53 54 55end 56 57structure Proof_Graph : PROOF_GRAPH = 58struct 59 60val isabelle_home = File.full_path Path.root (Path.variable "ISABELLE_HOME") 61 62(*FIXME: Not general *) 63fun relative_path_of s = if s = "" then "" else 64 let 65 val home_base = Path.dir isabelle_home |> Path.implode 66 in 67 try (fn s => "~~/.." ^ (unprefix home_base s)) s 68 |> the_default s end 69 70type proof_entry = {name : string, file : string, lines : int * int, 71 prems : int list list, concl : int list, kind : Proof_Count.lemmaT option} 72 73fun contains ({prems, concl, ...} : proof_entry) = flat prems @ concl 74 75fun map_contains f ({name, file, lines, prems, concl, kind}) = 76let 77 val (concl' :: prems') = (burrow f (concl :: prems)) 78in 79 ({name = name, file = file, lines = lines, prems = prems', concl = concl', kind = kind} : proof_entry) 80end 81 82fun restrict_subgraph f graph = 83let 84 val restrs = String_Graph.fold (fn (id,(e,edge)) => if f (id,e) then I else cons edge) graph [] 85 |> map (fn (preds,sucs) => map_product pair (String_Graph.Keys.dest preds) (String_Graph.Keys.dest sucs)) 86in 87 fold (fold (String_Graph.add_edge)) restrs graph 88 |> String_Graph.restrict (fn id => f (id,(String_Graph.get_node graph id))) 89end 90 91fun size_of {lines, ...} = case lines of (a,b) => (b - a) + 1 92 93fun thms_of (PBody {thms,...}) = thms 94 95fun proof_body_descend' f (_,(nm,_,body)) = 96 if f nm then 97 fold (append o (proof_body_descend' f)) (thms_of (Future.join body)) [] 98 else 99 [nm] 100 101fun used_facts' f thm = fold (append o (proof_body_descend' f)) (thms_of (Thm.proof_body_of thm)) [] 102 103 104 105fun used_facts f thm = 106 let 107 val nm = Thm.get_name_hint thm 108 in 109 used_facts' (fn nm' => nm' = "" orelse nm' = nm orelse f nm) thm 110 end 111 112fun graph_proof thy = 113 let 114 115 val report = Proof_Count.get_size_report () 116 |> Proof_Count.compute_sizes 117 118 119 fun get_lines (SOME (_,(begin,done))) = 120 (((the (Position.line_of begin),the (Position.line_of done))) handle Option => (~1,~1)) 121 | get_lines NONE = (~1,~1) 122 123 fun get_transaction (SOME (t,_)) = SOME t 124 | get_transaction NONE = NONE 125 126 val all_thms = Global_Theory.all_thms_of thy true 127 128 val (graph,access) = Spec_Graph.get_graph thy 129 130 val all_defs = Int_Graph.fold (fn (_,(e,_)) => (case (#def_name e) of SOME d => Symtab.update (d,()) | NONE => I)) graph Symtab.empty 131 132 (* "interesting" defined as being a phyiscal lemma or a definitino *) 133 fun is_interesting name = 134 let 135 val lines = Symtab.lookup report name |> get_lines 136 in 137 (Symtab.defined all_defs name) orelse 138 (not (lines = (~1,~1))) 139 end 140 141 val _ = tracing ((@{make_string} (length all_thms)) ^ " total theorems found") 142 143 val interesting_thms = filter (fn (i,_) => is_interesting i) all_thms 144 |> sort_distinct (prod_ord string_ord (make_ord (K false))) 145 146 147 fun mk_entry (name,thm) = 148 let 149 (* Skip uninteresting lemmas, finding their first interesting dependant *) 150 val used = used_facts (not o is_interesting) thm 151 |> filter is_interesting 152 153 fun contains t = Term.fold_aterms (fn (Const c) => (case (access c) of SOME i => cons i | _ => I) | _ => I) t [] 154 155 val report_entry = Symtab.lookup report name 156 157 val t = prop_of thm 158 159 val e = {name = Long_Name.base_name name, 160 file = ((Symtab.lookup report name) |> the |> snd |> fst |> Position.file_of |> the |> relative_path_of) handle Option => "", 161 lines = get_lines report_entry, 162 prems = map contains (Logic.strip_imp_prems t), 163 concl = contains (Logic.strip_imp_concl t), 164 kind = get_transaction report_entry} 165 in 166 ((name,e),used) 167 end 168 169 val _ = tracing ((@{make_string} (length interesting_thms)) ^ " facts to process") 170 171 172 val raw_graph = Par_List.map (fn e => (mk_entry e)) interesting_thms 173 174 val proof_graph = String_Graph.make raw_graph 175 176 177 in (graph,proof_graph) end; 178 179 180(*Attempt to merge lemmas statements back together*) 181fun merge_entries (entries as (n,e) :: _) graph = 182let 183 val id = unsuffix "_" (common_prefix (map fst entries)) 184 185 val name = unsuffix "_" (common_prefix (map (#name o snd) entries)) 186 val prems = flat (map (#prems o snd) entries) 187 val concl = flat (map (#concl o snd) entries) 188 189 fun rep_old nm' = if exists (fn (n,_) => nm' = n) entries then id else nm' 190 191 val preds = flat (map (String_Graph.immediate_preds graph o fst) entries) |> map rep_old 192 val succs = flat (map (String_Graph.immediate_succs graph o fst) entries) |> map rep_old 193in 194 fold (fn (n,_) => String_Graph.del_node n) entries graph 195 |> String_Graph.new_node (id,{name = name,prems = prems,concl = concl, file = #file e,lines = #lines e, kind = #kind e}) 196 |> fold (fn e => String_Graph.add_edge (e,id)) preds 197 |> fold (fn e => String_Graph.add_edge (id,e)) succs 198 end handle General.Fail "unsuffix" => graph 199 200(*Merge theorems which share document position*) 201fun merge_multi_thms graph = 202let 203 val files = String_Graph.fold (fn (n,(e,_)) => Symtab.insert_list (K false) (#file e,(n,e))) graph Symtab.empty 204 fun do_partition es = 205 let 206 val parts = partition_eq (fn ((_,e),(_,e')) => #lines e = #lines e') (filter_out (fn (_,e) => #lines e = (~1,~1)) es) 207 |> filter (fn l => length l > 1) 208 |> filter_out (fn l => all_eq (map (#name o snd) l)) 209 210 in 211 fold merge_entries parts end 212in 213 Symtab.fold (fn (_,es) => do_partition es) files graph end 214 215val lemma_prefixK = "Lemma: " 216val declare_prefixK = "Declare: " 217val noneK = "NONE" 218 219fun transaction_to_str s = let 220 open Proof_Count in case s of 221 SOME (Lemma n) => lemma_prefixK ^ n 222 | SOME (Declare n) => declare_prefixK ^ n 223 | NONE => noneK 224end 225 226fun str_to_transaction s = if s = noneK then NONE else 227SOME ( 228 case (try (unprefix "Lemma: ") s) of 229 SOME x => Proof_Count.Lemma x 230 | NONE => case (try (unprefix "Declare: ") s) of 231 SOME x => Proof_Count.Declare x 232 | NONE => error ("Deserialization failure: unexpected lemma type: " ^ s)) 233 234fun to_props (e : proof_entry) = [] 235 |> Properties.put ("name", #name e) 236 |> Properties.put ("file", #file e) 237 |> Properties.put ("start", Int.toString (#lines e |> fst)) 238 |> Properties.put ("end", Int.toString (#lines e |> snd)) 239 |> Properties.put ("kind",transaction_to_str (#kind e)) 240 241 242fun from_props (prop,(prems,concl)) = 243 ({ name = Properties.get prop "name" |> the, 244 file = Properties.get prop "file" |> the, 245 lines = (Int.fromString (Properties.get prop "start" |> the) |> the, 246 Int.fromString (Properties.get prop "end" |> the) |> the), 247 kind = Properties.get prop "kind" |> the_default "Unknown" |> str_to_transaction, 248 prems = prems, 249 concl = concl} : proof_entry) 250 251fun encode_proof_graph_entry e = 252 let open XML.Encode 253 in 254 (pair properties (pair (list (list int)) (list int))) (to_props e, (#prems e,#concl e)) end 255 256val encode_proof_graph = String_Graph.encode XML.Encode.string encode_proof_graph_entry 257 258fun decode_proof_graph_entry e = 259 let open XML.Decode 260 in 261 (pair properties (pair (list (list int)) (list int))) e |> from_props end 262 263val decode_proof_graph = String_Graph.decode XML.Decode.string decode_proof_graph_entry 264 265fun get_thy_deps' thy tab = 266let 267 val ancestors = Theory.nodes_of thy 268 val nm = Context.theory_name thy 269in 270 if Symtab.defined tab nm then tab else 271 Symtab.update (nm,(Resources.master_directory thy |> Path.implode |> relative_path_of,(map Context.theory_name ancestors))) tab 272 |> fold get_thy_deps' ancestors end 273 274fun get_thy_deps thy = get_thy_deps' thy Symtab.empty 275 276fun encode_thy_deps deps = 277 let open XML.Encode in 278 (list (pair (string) (pair (string) (list string)))) (Symtab.dest deps) end 279 280fun decode_thy_deps body = 281 let open XML.Decode in 282 (list (pair (string) (pair (string) (list string)))) body 283 |> Symtab.make end 284 285fun proper_theory_list tab (bottoms : string list) = 286 let 287 fun has_bottom (_,(_,deps)) = exists (fn th => member (op =) bottoms th) deps 288 in 289 Symtab.dest tab 290 |> filter has_bottom 291 |> map fst end; 292 293fun get_full_spec thy = 294let 295 296 val thy_deps = get_thy_deps thy 297 |> Symtab.delete (Context.theory_name thy) 298 299 val (full_graph,proof_graph) = graph_proof thy 300 301in 302 (full_graph,proof_graph,thy_deps) end 303 304 305fun write_graph_spec_of (full_graph,proof_graph,thy_deps) metadata file = 306 let 307 308 val spec_xml = XML.Elem (("Spec_Graph",[]),Spec_Graph.encode_graph full_graph) 309 310 val proof_xml = XML.Elem (("Proof_Graph",[]),encode_proof_graph proof_graph) 311 312 val thy_deps_xml = XML.Elem(("Thy_Deps",[]),encode_thy_deps thy_deps) 313 314 val toplevel_xml = XML.Elem(("Toplevel",[("metadata",metadata)]),[spec_xml,proof_xml,thy_deps_xml]) 315 316 in 317 File.open_output (XML.output (toplevel_xml)) (Path.explode file) end 318 319fun read_graph_spec_from file = 320 let 321 val tree = File.read (Path.explode file) 322 |> XML.parse 323 324 325 fun deconstruct ( 326 XML.Elem (("Toplevel",_), 327 [XML.Elem(("Spec_Graph",[]),spec_body), 328 XML.Elem(("Proof_Graph",[]),proof_body), 329 XML.Elem(("Thy_Deps",[]),thy_deps)])) = (spec_body,proof_body,thy_deps) 330 | deconstruct _ = error "Not a valid spec graph" 331 332 val (spec_body,proof_body,thy_deps) = deconstruct tree 333 334 val full_graph = Spec_Graph.decode_graph spec_body 335 val proof_graph = decode_proof_graph proof_body 336 val thy_deps = decode_thy_deps thy_deps 337 338 in 339 (full_graph,proof_graph,thy_deps) end 340 341end 342