1(* ========================================================================= *) 2(* FILE : Theory.sml *) 3(* DESCRIPTION : Management of logical theories. *) 4(* *) 5(* AUTHOR : Konrad Slind, University of Calgary *) 6(* (also T.U. Munich and Cambridge) *) 7(* DATE : September 11, 1991 *) 8(* REVISION : August 7, 1997 *) 9(* : March 9, 1998 *) 10(* : August 2000 *) 11(* *) 12(* ========================================================================= *) 13 14(* ---------------------------------------------------------------------- 15 Updated November/December 2002 - by Michael Norrish 16 ---------------------------------------------------------------------- *) 17 18(*--------------------------------------------------------------------------- 19 20 Notes on the design. 21 22 We provide a single current theory segment, which can be thought of as 23 a scratchpad for building the segment that eventually gets exported. 24 The following are the important components of a segment: 25 26 - the types and terms declared in the current segment (stored in 27 the "symbol tables" maintained in modules Term and Type). 28 29 - the unique id for the theory, along with its parents, which 30 should be already-loaded theory segments. 31 32 - the theory graph, used to enforce a prohibition on circular 33 dependencies among segments. 34 35 - the axioms, definitions, and theorems stored in the segment so far. 36 37 The parents of the segment are held in the theory graph. 38 39 When a segment is exported, we dump everything in it to a text file 40 representing an ML structure. 41 42 Elements in the current segment can be deleted or overwritten, which 43 makes consistency maintenance an issue. Before a theory is exported, 44 everything that is about to be exported gets checked for up-to-date-ness. 45 46 ---------------------------------------------------------------------------*) 47 48 49structure Theory :> Theory = 50struct 51 52open Feedback Lib Type Term Thm ; 53 54open TheoryPP 55 56 57structure PP = HOLPP 58type num = Arbnum.num 59 60val ERR = mk_HOL_ERR "Theory"; 61val WARN = HOL_WARNING "Theory"; 62 63type thy_addon = {sig_ps : (unit -> PP.pretty) option, 64 struct_ps : (unit -> PP.pretty) option} 65 66local 67 val hooks = 68 (* hooks are stored in the order they are registered, with later 69 hooks earlier in the list. 70 The set component is the list of the disabled hooks. 71 *) 72 ref (HOLset.empty String.compare, 73 [] : (string * (TheoryDelta.t -> unit)) list) 74in 75fun call_hooks td = let 76 val (disabled, hooks) = !hooks 77 val hooks_rev = List.rev hooks 78 fun protect nm (f:TheoryDelta.t -> unit) td = let 79 fun error_pfx() = 80 "Hook "^nm^" failed on event " ^ TheoryDelta.toString td 81 in 82 f td 83 handle e as HOL_ERR {origin_function,origin_structure,message} => 84 Feedback.HOL_WARNING 85 "Theory" 86 "callhooks" 87 (error_pfx() ^ " with problem " ^ 88 Feedback.exn_to_string e) 89 | Match => 90 Feedback.HOL_WARNING 91 "Theory" 92 "callhooks" 93 (error_pfx() ^ " with a Match exception") 94 end 95 fun recurse l = 96 case l of 97 [] => () 98 | (nm, f) :: rest => let 99 in 100 if HOLset.member(disabled,nm) then () 101 else protect nm f td; 102 recurse rest 103 end 104in 105 recurse hooks_rev 106end 107 108fun register_hook (nm, f) = let 109 val (disabled, hooks0) = !hooks 110 val hooks0 = List.filter (fn (nm',f) => nm' <> nm) hooks0 111in 112 hooks := (disabled, (nm,f) :: hooks0) 113end 114 115fun delete_hook nm = let 116 val (disabled, hookfns) = !hooks 117 val (deleting, remaining) = Lib.partition (fn (nm', _) => nm' = nm) hookfns 118in 119 case deleting of 120 [] => HOL_WARNING "Theory" "delete_hook" ("No hook with name: "^nm) 121 | _ => (); 122 hooks := (HOLset.delete(disabled,nm), remaining) 123end 124 125fun get_hooks () = #2 (!hooks) 126 127fun hook_modify act f x = 128 let 129 val (disabled0, fns) = !hooks 130 fun finish() = hooks := (disabled0, fns) 131 val _ = hooks := (act disabled0, fns) 132 val result = f x handle e => (finish(); raise e) 133 in 134 finish(); 135 result 136 end 137 138fun disable_hook nm f x = 139 hook_modify (fn s => HOLset.add(s,nm)) f x 140 141fun safedel_fromset nm s = 142 HOLset.delete(s, nm) handle HOLset.NotFound => s 143fun enable_hook nm f x = 144 hook_modify (safedel_fromset nm) f x 145 146 147end (* local block enclosing declaration of hooks variable *) 148 149(* This reference is set in course of loading the parsing library *) 150 151val pp_thm = ref (fn _:thm => PP.add_string "<thm>") 152 153(*---------------------------------------------------------------------------* 154 * Unique identifiers, for securely linking a theory to its parents when * 155 * loading from disk. * 156 *---------------------------------------------------------------------------*) 157local 158 open Arbnum 159in 160abstype thyid = UID of {name:string, sec: num, usec : num} 161with 162 fun thyid_eq x (y:thyid) = (x=y); 163 fun new_thyid s = 164 let val {sec,usec} = Portable.dest_time (Portable.timestamp()) 165 in 166 UID{name=s, sec = sec, usec = usec} 167 end 168 169 fun dest_thyid (UID{name, sec, usec}) = (name,sec,usec) 170 171 val thyid_name = #1 o dest_thyid; 172 173 fun make_thyid(s,i1,i2) = UID{name=s, sec=i1,usec=i2} 174 175 fun thyid_to_string (UID{name,sec,usec}) = 176 String.concat["(",Lib.quote name,",",toString sec, ",", 177 toString usec, ")"] 178 179 val min_thyid = 180 UID{name="min", sec = fromInt 0, usec = fromInt 0} (* Ur-theory *) 181 182end; 183end (* local *) 184 185fun thyid_assoc x [] = raise ERR "thyid_assoc" "not found" 186 | thyid_assoc x ((a,b)::t) = if thyid_eq x a then b else thyid_assoc x t; 187 188fun thyname_assoc x [] = raise ERR "thyname_assoc" "not found" 189 | thyname_assoc x ((a,b)::t) = if x = thyid_name a then b 190 else thyname_assoc x t; 191 192 193(*--------------------------------------------------------------------------- 194 The theory graph is quite basic: just a list of pairs (thyid,parents). 195 The "min" theory is already installed; it has no parents. 196 ---------------------------------------------------------------------------*) 197 198structure Graph = struct type graph = (thyid * thyid list) list 199local val theGraph = ref [(min_thyid,[])] 200in 201 fun add p = theGraph := (p :: !theGraph) 202 fun add_parent (n,newp) = 203 let fun same (node,_) = thyid_eq node n 204 fun addp(node,parents) = (node, op_union thyid_eq [newp] parents) 205 fun ins (a::rst) = if same a then addp a::rst else a::ins rst 206 | ins [] = raise ERR "Graph.add_parent.ins" "not found" 207 in theGraph := ins (!theGraph) 208 end 209 fun isin n = Lib.can (thyid_assoc n) (!theGraph); 210 fun parents_of n = thyid_assoc n (!theGraph); 211 fun ancestryl L = 212 let fun Anc P Q = rev_itlist 213 (fn nde => fn A => if op_mem thyid_eq nde A then A 214 else Anc (parents_of nde handle HOL_ERR _ => []) (nde::A)) P Q 215 in Anc L [] 216 end; 217 fun fringe () = 218 let val all_parents = List.map #2 (!theGraph) 219 fun is_parent y = Lib.exists (Lib.op_mem thyid_eq y) all_parents 220 in List.filter (not o is_parent) (List.map #1 (!theGraph)) 221 end; 222 fun first P = Lib.first P (!theGraph) 223end 224end; (* structure Graph *) 225 226 227(*---------------------------------------------------------------------------* 228 * A type for distinguishing the different kinds of theorems that may be * 229 * stored in a theory. * 230 *---------------------------------------------------------------------------*) 231 232datatype thmkind = Thm of thm | Axiom of string Nonce.t * thm | Defn of thm 233 234fun is_axiom (Axiom _) = true | is_axiom _ = false; 235fun is_theorem (Thm _) = true | is_theorem _ = false; 236fun is_defn (Defn _) = true | is_defn _ = false; 237 238fun drop_thmkind (Axiom(_,th)) = th 239 | drop_thmkind (Thm th) = th 240 | drop_thmkind (Defn th) = th; 241 242fun drop_pthmkind (s,th) = (s,drop_thmkind th); 243 244fun drop_Axkind (Axiom rth) = rth 245 | drop_Axkind _ = raise ERR "drop_Axkind" ""; 246 247 248(*---------------------------------------------------------------------------* 249 * The type of HOL theory segments. Lacks fields for the type and term * 250 * signatures, which are held locally in the Type and Term structures. * 251 * Also lacks a field for the theory graph, which is held in Graph. * 252 *---------------------------------------------------------------------------*) 253 254datatype thydata = Loaded of UniversalType.t 255 | Pending of (string * (string -> term)) list 256type ThyDataMap = (string,thydata)Binarymap.dict 257 (* map from string identifying the "type" of the data, 258 e.g., "simp", "mono", "cong", "grammar_update", 259 "LaTeX map", to the data itself. *) 260val empty_datamap : ThyDataMap = Binarymap.mkDict String.compare 261 262type segment = {thid : thyid, (* unique id *) 263 facts : (string * thmkind) list, (* stored ax,def,and thm *) 264 thydata : ThyDataMap, (* extra theory data *) 265 adjoin : thy_addon list, (* extras for export *) 266 mldeps : string HOLset.set} 267local 268 open FunctionalRecordUpdate 269 fun seg_mkUp z = makeUpdate5 z 270in 271 fun update_seg z = let 272 fun from adjoin facts mldeps thid thydata = 273 {adjoin=adjoin, facts=facts, mldeps=mldeps, thid=thid, thydata=thydata} 274 (* fields in reverse order to above *) 275 fun from' thydata thid mldeps facts adjoin = 276 {adjoin=adjoin, facts=facts, mldeps=mldeps, thid=thid, thydata=thydata} 277 fun to f {adjoin, facts, mldeps, thid, thydata} = 278 f adjoin facts mldeps thid thydata 279 in 280 seg_mkUp (from, from', to) 281 end z 282 val U = U 283 val $$ = $$ 284end (* local *) 285 286 287 288(*---------------------------------------------------------------------------* 289 * CREATE THE INITIAL THEORY SEGMENT. * 290 * * 291 * The timestamp for a segment is its creation date. "con_wrt_disk" is * 292 * set to false because when a segment is created no corresponding file * 293 * gets created (the file is only created on export). * 294 *---------------------------------------------------------------------------*) 295 296fun fresh_segment s :segment = {thid=new_thyid s, facts=[], adjoin=[], 297 thydata = empty_datamap, 298 mldeps = HOLset.empty String.compare}; 299 300 301local val CT = ref (fresh_segment "scratch") 302in 303 fun theCT() = !CT 304 fun makeCT seg = CT := seg 305end; 306 307val CTname = thyid_name o #thid o theCT; 308val current_theory = CTname; 309 310 311(*---------------------------------------------------------------------------* 312 * READING FROM THE SEGMENT * 313 *---------------------------------------------------------------------------*) 314 315fun thy_types thyname = Type.thy_types thyname 316fun thy_constants thyname = Term.thy_consts thyname 317fun thy_parents thyname = snd (Graph.first 318 (equal thyname o thyid_name o fst)) 319fun thy_axioms (th:segment) = filter (is_axiom o #2) (#facts th) 320fun thy_theorems (th:segment) = filter (is_theorem o #2) (#facts th) 321fun thy_defns (th:segment) = filter (is_defn o #2) (#facts th) 322fun thy_addons (th:segment) = #adjoin th 323 324fun stamp thyname = 325 let val (_,sec,usec) = dest_thyid (fst (Graph.first 326 (equal thyname o thyid_name o fst))) 327 in 328 Portable.mk_time {sec = sec, usec = usec} 329 end; 330 331local fun norm_name "-" = CTname() 332 | norm_name s = s 333 fun grab_item style name alist = 334 case Lib.assoc1 name alist 335 of SOME (_,th) => th 336 | NONE => raise ERR style 337 ("couldn't find "^style^" named "^Lib.quote name) 338in 339 val types = thy_types o norm_name 340 val constants = thy_constants o norm_name 341 fun get_parents s = if norm_name s = CTname() 342 then Graph.fringe() else thy_parents s 343 val parents = map thyid_name o get_parents 344 val ancestry = map thyid_name o Graph.ancestryl o get_parents 345 fun current_axioms() = map drop_pthmkind (thy_axioms (theCT())) 346 fun current_theorems() = map drop_pthmkind (thy_theorems (theCT())) 347 fun current_definitions() = map drop_pthmkind (thy_defns (theCT())) 348 fun current_ML_deps() = HOLset.listItems (#mldeps (theCT())) 349end; 350 351(*---------------------------------------------------------------------------* 352 * Is a segment empty? * 353 *---------------------------------------------------------------------------*) 354 355fun empty_segment ({thid,facts, ...}:segment) = 356 let val thyname = thyid_name thid 357 in null (thy_types thyname) andalso 358 null (thy_constants thyname) andalso 359 null facts 360 end; 361 362(*---------------------------------------------------------------------------* 363 * ADDING TO THE SEGMENT * 364 *---------------------------------------------------------------------------*) 365 366fun add_type {name,theory,arity} thy = 367 (Type.prim_new_type {Thy = theory, Tyop = name} arity; thy) 368 369fun add_term {name,theory,htype} thy = 370 (Term.prim_new_const {Thy = theory, Name = name} htype; thy) 371 372local fun pluck1 x L = 373 let fun get [] A = NONE 374 | get ((p as (x',_))::rst) A = 375 if x=x' then SOME (p,rst@A) else get rst (p::A) 376 in get L [] 377 end 378 fun overwrite (p as (s,f)) l = 379 case pluck1 s l of 380 NONE => p::l 381 | SOME ((_,f'),l') => p::l' 382in 383fun add_fact (th as (s,_)) (seg : segment) = 384 update_seg seg (U #facts (overwrite th (#facts seg))) $$ 385 before 386 call_hooks (TheoryDelta.NewBinding s) 387end; 388 389fun new_addon a (s as {adjoin, ...} : segment) = 390 update_seg s (U #adjoin (a::adjoin)) $$ 391 392fun add_ML_dep s (seg as {mldeps, ...} : segment) = 393 update_seg seg (U #mldeps (HOLset.add(mldeps, s))) $$ 394 395local fun plucky x L = 396 let fun get [] A = NONE 397 | get ((p as (x',_))::rst) A = 398 if x=x' then SOME (rev A, p, rst) else get rst (p::A) 399 in get L [] 400 end 401in 402fun set_MLbind (s1,s2) (seg as {facts, ...} : segment) = 403 case plucky s1 facts of 404 NONE => (WARN "set_MLbind" (Lib.quote s1^" not found in current theory"); 405 seg) 406 | SOME (X,(_,b),Y) => 407 update_seg seg (U #facts (X@((s2,b)::Y))) $$ 408end; 409 410(*--------------------------------------------------------------------------- 411 Deleting from the segment 412 ---------------------------------------------------------------------------*) 413 414fun del_type (name,thyname) thy = 415 (Type.prim_delete_type {Thy = thyname, Tyop = name}; thy) 416 417(*--------------------------------------------------------------------------- 418 Remove a constant from the signature. 419 ---------------------------------------------------------------------------*) 420 421fun del_const (name,thyname) thy = 422 (Term.prim_delete_const {Thy = thyname, Name = name} ; thy) 423 424fun del_binding name (s as {facts,...} : segment) = 425 update_seg s (U #facts (filter (fn (s, _) => not(s=name)) facts)) $$; 426 427(*--------------------------------------------------------------------------- 428 Clean out the segment. Note: this clears out the segment, and the 429 signatures, but does not alter the theory graph. The segment will 430 still be there, with its parents. 431 ---------------------------------------------------------------------------*) 432 433fun zap_segment s (thy : segment) = 434 (Type.del_segment s; Term.del_segment s; 435 {adjoin=[], facts=[],thid= #thid thy, thydata = empty_datamap, 436 mldeps = HOLset.empty String.compare}) 437 438(*--------------------------------------------------------------------------- 439 Wrappers for functions that alter the segment. 440 ---------------------------------------------------------------------------*) 441 442local fun inCT f arg = makeCT(f arg (theCT())) 443 open TheoryDelta 444in 445 val add_typeCT = inCT add_type 446 val add_termCT = inCT add_term 447 fun add_axiomCT(r,ax) = inCT add_fact (Nonce.dest r, Axiom(r,ax)) 448 fun add_defnCT(s,def) = inCT add_fact (s, Defn def) 449 fun add_thmCT(s,th) = inCT add_fact (s, Thm th) 450 val add_ML_dependency = inCT add_ML_dep 451 452 fun delete_type n = (inCT del_type (n,CTname()); 453 call_hooks 454 (DelTypeOp{Name = n, Thy = CTname()})) 455 fun delete_const n = (inCT del_const (n,CTname()); 456 call_hooks 457 (DelConstant{Name = n, Thy = CTname()})) 458 459 fun delete_binding s = (inCT del_binding s; call_hooks (DelBinding s)) 460 461 fun set_MLname s1 s2 = inCT set_MLbind (s1,s2) 462 val adjoin_to_theory = inCT new_addon 463 val zapCT = inCT zap_segment 464 465end; 466 467local 468 structure PP = HOLPP 469 fun pp_lines l = 470 PP.block PP.CONSISTENT 0 471 (List.concat (map (fn s => [PP.add_string s, PP.NL]) l)) 472 val is_empty = 473 fn [] => true 474 | [s] => s = "none" orelse List.all Char.isSpace (String.explode s) 475 | _ => false 476 fun pp l = if is_empty l then NONE else SOME (fn _ => pp_lines l) 477 val qpp = pp o Portable.quote_to_string_list 478in 479 fun quote_adjoin_to_theory q1 q2 = 480 adjoin_to_theory {sig_ps = qpp q1, struct_ps = qpp q2} 481end 482 483(*---------------------------------------------------------------------------* 484 * INSTALLING CONSTANTS IN THE CURRENT SEGMENT * 485 *---------------------------------------------------------------------------*) 486 487fun new_type (Name,Arity) = 488 (if Lexis.allowed_type_constant Name orelse 489 not (!Globals.checking_type_names) 490 then () 491 else WARN "new_type" (Lib.quote Name^" is not a standard type name") 492 ; add_typeCT {name=Name, arity=Arity, theory = CTname()} 493 ; call_hooks (TheoryDelta.NewTypeOp {Name = Name, Thy = CTname()})); 494 495fun new_constant (Name,Ty) = 496 (if Lexis.allowed_term_constant Name orelse 497 not (!Globals.checking_const_names) 498 then () 499 else WARN "new_constant" (Lib.quote Name^" is not a standard constant name") 500 ; add_termCT {name=Name, theory=CTname(), htype=Ty} 501 ; call_hooks (TheoryDelta.NewConstant {Name = Name, Thy = CTname()})) 502 503(*--------------------------------------------------------------------------- 504 Install constants in the current theory, as part of loading a 505 previously built theory from disk. 506 ---------------------------------------------------------------------------*) 507 508fun install_type(s,a,thy) = add_typeCT {name=s, arity=a, theory=thy}; 509fun install_const(s,ty,thy) = add_termCT {name=s, htype=ty, theory=thy} 510 511 512(*--------------------------------------------------------------------------- 513 * Is an object wellformed (current) wrt the symtab, i.e., have none of its 514 * constants been re-declared after it was built? A constant is 515 * up-to-date if either 1) it was not declared in the current theory (hence 516 * it was declared in an ancestor theory and is thus frozen); or 2) it was 517 * declared in the current theory and its witness is up-to-date. 518 * 519 * When a new entry is made in the theory, it is checked to see if it is 520 * uptodate (or if its witnesses are). The "overwritten" bit of a segment 521 * tells whether any element of the theory has been overwritten. If 522 * overwritten is false, then the theory is uptodate. If we want to add 523 * something to an uptodate theory, then no processing need be done. 524 * Otherwise, we have to examine the item, and recursively any item it 525 * depends on, to see if any constant or type constant occurring in it, 526 * or any theorem it depends on, is outofdate. If so, then the item 527 * will not be added to the theory. 528 * 529 * To clean up a theory with outofdate elements, use "scrub". 530 * 531 * To tell if an object is uptodate, we can't just look at it; we have 532 * to recursively examine its witness(es). We can't just accept a witness 533 * that seems to be uptodate, since its constants may be flagged as uptodate, 534 * but some may depend on outofdate witnesses. The solution taken 535 * here is to first set all constants in the segment signature to be 536 * outofdate. Then a bottom-up pass is made. The "utd" flag in each 537 * signature entry is used to cut off repeated recursive traversal, as in 538 * dynamic programming. It holds the value "true" when the witness is 539 * uptodate. 540 *---------------------------------------------------------------------------*) 541 542fun uptodate_thm thm = 543 Lib.all uptodate_term (Thm.concl thm::Thm.hyp thm) 544 andalso 545 uptodate_axioms (Tag.axioms_of (Thm.tag thm)) 546and uptodate_axioms [] = true 547 | uptodate_axioms rlist = let 548 val axs = map (drop_Axkind o snd) (thy_axioms(theCT())) 549 in 550 (* tempting to call uptodate_thm here, but this would put us into a loop 551 because axioms have themselves as tags, also unnecessary because 552 axioms never have hypotheses (check type of new_axiom) *) 553 Lib.all (uptodate_term o Thm.concl o Lib.C Lib.assoc axs) rlist 554 end handle HOL_ERR _ => false 555 556fun scrub_ax (s as {facts,...} : segment) = 557 let fun check (_, Thm _ ) = true 558 | check (_, Defn _) = true 559 | check (_, Axiom(_,th)) = uptodate_term (Thm.concl th) 560 in 561 update_seg s (U #facts (List.filter check facts)) $$ 562 end 563 564fun scrub_thms (s as {facts,...}: segment) = 565 let fun check (_, Axiom _) = true 566 | check (_, Thm th ) = uptodate_thm th 567 | check (_, Defn th) = uptodate_thm th 568 in 569 update_seg s (U #facts (List.filter check facts)) $$ 570 end 571 572fun scrub () = makeCT (scrub_thms (scrub_ax (theCT()))) 573 574fun scrubCT() = (scrub(); theCT()); 575 576 577(*---------------------------------------------------------------------------* 578 * WRITING AXIOMS, DEFINITIONS, AND THEOREMS INTO THE CURRENT SEGMENT * 579 *---------------------------------------------------------------------------*) 580 581local 582 fun check_name tempok (fname,s) = 583 if Lexis.ok_sml_identifier s andalso 584 not (Lib.mem s ["ref", "true", "false", "::", "nil", "="]) orelse 585 tempok andalso is_temp_binding s 586 then () 587 else raise ERR fname ("Can't use name " ^ Lib.mlquote s ^ 588 " as a theory-binding") 589 fun DATED_ERR f bindname = ERR f (Lib.quote bindname^" is out-of-date!") 590 val save_thm_reporting = ref 1 591 val _ = Feedback.register_trace ("Theory.save_thm_reporting", 592 save_thm_reporting, 2) 593 fun mesg_str th = 594 let 595 val tags = Lib.set_diff (fst (Tag.dest_tag (Thm.tag th))) ["DISK_THM"] 596 in 597 if List.null tags 598 then "theorem" 599 else if Lib.null_intersection tags ["fast_proof", "cheat"] 600 then "ORACLE thm" 601 else "CHEAT" 602 end 603 fun save_mesg s name = 604 if !save_thm_reporting = 0 orelse 605 !Globals.interactive andalso !save_thm_reporting < 2 606 then () 607 else let 608 val s = if !Globals.interactive then s 609 else StringCvt.padRight #"_" 13 (s ^ " ") 610 in 611 with_flag (MESG_to_string, Lib.I) HOL_MESG 612 ("Saved " ^ s ^ " " ^ Lib.quote name ^ "\n") 613 end 614in 615 fun save_thm (name, th) = 616 let 617 val th' = save_dep (CTname ()) th 618 in 619 check_name true ("save_thm", name) 620 ; if uptodate_thm th' then add_thmCT (name, th') 621 else raise DATED_ERR "save_thm" name 622 ; save_mesg (mesg_str th') name 623 ; th' 624 end 625 626 fun new_axiom (name,tm) = 627 let 628 val rname = Nonce.mk name 629 val axiom = Thm.mk_axiom_thm (rname, tm) 630 val axiom' = save_dep (CTname()) axiom 631 in 632 check_name false ("new_axiom",name) 633 ; if uptodate_term tm then add_axiomCT (rname, axiom') 634 else raise DATED_ERR "new_axiom" name 635 ; axiom' 636 end 637 638 fun store_definition (name, def) = 639 let 640 val def' = save_dep (CTname ()) def 641 in 642 check_name true ("store_definition", name) 643 ; uptodate_thm def' orelse raise DATED_ERR "store_definition" name 644 ; add_defnCT (name, def') 645 ; def' 646 end 647end; 648 649(*---------------------------------------------------------------------------* 650 * Adding a new theory into the current theory graph. * 651 *---------------------------------------------------------------------------*) 652 653fun set_diff a b = filter (fn x => not (Lib.op_mem thyid_eq x b)) a; 654fun node_set_eq S1 S2 = null(set_diff S1 S2) andalso null(set_diff S2 S1); 655 656fun link_parents thy plist = 657 let val node = make_thyid thy 658 val parents = map make_thyid plist 659 in 660 if Lib.all Graph.isin parents 661 then if Graph.isin node 662 then if node_set_eq parents (Graph.parents_of node) then () 663 else (HOL_MESG 664 "link_parents: the theory has two unequal sets of parents"; 665 raise ERR "link_parents" "") 666 else Graph.add (node,parents) 667 else let val baddies = Lib.filter (not o Graph.isin) parents 668 val names = map thyid_to_string baddies 669 in HOL_MESG (String.concat 670 ["link_parents: the following parents of ", 671 Lib.quote (thyid_name node), 672 "\n should already be in the theory graph (but aren't): ", 673 String.concat (commafy names)]); 674 raise ERR "link_parents" "" 675 end 676 end; 677 678fun incorporate_types thy tys = 679 let fun itype (s,a) = (install_type(s,a,thy);()) 680 in List.app itype tys 681 end; 682 683fun incorporate_consts thy tyvector consts = 684 let fun iconst(s,i) = (install_const(s,Vector.sub(tyvector,i),thy);()) 685 in List.app iconst consts 686 end; 687 688(* ---------------------------------------------------------------------- 689 Theory data functions 690 691 In addition to the data in the current segment, we want to track the data 692 associated with all previous segments. We do this with another reference 693 variable (yuck). 694 ---------------------------------------------------------------------- *) 695 696structure LoadableThyData = 697struct 698 699 type t = UniversalType.t 700 type DataOps = {merge : t * t -> t, 701 read : (string -> term) -> string -> t option, 702 write : (term -> string) -> t -> string, 703 terms : t -> term list} 704 val allthydata = ref (Binarymap.mkDict String.compare : 705 (string, ThyDataMap) Binarymap.dict) 706 val dataops = ref (Binarymap.mkDict String.compare : 707 (string, DataOps) Binarymap.dict) 708 709 fun segment_data {thy,thydataty} = let 710 val {thydata,thid,...} = theCT() 711 fun check_map m = 712 case Binarymap.peek(m, thydataty) of 713 NONE => NONE 714 | SOME (Loaded value) => SOME value 715 | SOME (Pending _) => raise ERR "segment_data" 716 "Can't interpret pending loads" 717 in 718 if thyid_name thid = thy then check_map thydata 719 else 720 case Binarymap.peek(!allthydata, thy) of 721 NONE => NONE 722 | SOME dmap => check_map dmap 723 end 724 725 fun write_data_update {thydataty,data} = 726 case Binarymap.peek(!dataops, thydataty) of 727 NONE => raise ERR "write_data_update" 728 ("No operations defined for "^thydataty) 729 | SOME {merge,read,write,terms} => let 730 val (s as {thydata,...}) = theCT() 731 open Binarymap 732 fun updatemap inmap = let 733 val newdata = 734 case peek(inmap, thydataty) of 735 NONE => Loaded data 736 | SOME (Loaded t) => Loaded (merge(t, data)) 737 | SOME (Pending ds) => 738 raise Fail "write_data_update invariant failure" 739 in 740 insert(inmap,thydataty,newdata) 741 end 742 in 743 makeCT (update_seg s (U #thydata (updatemap thydata)) $$) 744 end 745 746 fun set_theory_data {thydataty,data} = 747 case Binarymap.peek(!dataops, thydataty) of 748 NONE => raise ERR "set_theory_data" 749 ("No operations defined for "^thydataty) 750 | SOME{read,write,...} => let 751 val (s as {thydata,...}) = theCT() 752 open Binarymap 753 in 754 makeCT 755 (update_seg s 756 (U #thydata (insert(thydata, thydataty, Loaded data))) 757 $$) 758 end 759 760 fun temp_encoded_update {thy, thydataty, data, read = tmread} = let 761 val (s as {thydata, thid, ...}) = theCT() 762 open Binarymap 763 fun updatemap inmap = let 764 val baddecode = ERR "temp_encoded_update" 765 ("Bad decode for "^thydataty^" ("^data^")") 766 val newdata = 767 case (peek(inmap, thydataty), peek(!dataops,thydataty)) of 768 (NONE, NONE) => Pending [(data,tmread)] 769 | (NONE, SOME {read,...}) => 770 Loaded (valOf (read tmread data) handle Option => raise baddecode) 771 | (SOME (Loaded t), NONE) => 772 raise Fail "temp_encoded_update invariant failure 1" 773 | (SOME (Loaded t), SOME {merge,read,...}) => 774 Loaded (merge(t, valOf (read tmread data) 775 handle Option => raise baddecode)) 776 | (SOME (Pending ds), NONE) => Pending ((data,tmread)::ds) 777 | (SOME (Pending _), SOME _) => 778 raise Fail "temp_encoded_update invariant failure 2" 779 in 780 insert(inmap, thydataty, newdata) 781 end 782 in 783 if thy = thyid_name thid then 784 makeCT (update_seg s (U #thydata (updatemap thydata)) $$) 785 else let 786 val newsubmap = 787 case peek (!allthydata, thy) of 788 NONE => updatemap empty_datamap 789 | SOME dm => updatemap dm 790 in 791 allthydata := insert(!allthydata, thy, newsubmap) 792 end 793 end 794 795fun update_pending (m,r) thydataty = let 796 open Binarymap 797 fun update1 inmap = 798 case peek(inmap, thydataty) of 799 NONE => inmap 800 | SOME (Loaded t) => 801 raise ERR "LoadableThyData.new" 802 ("Theory data name " ^ Lib.quote thydataty ^ 803 " already in use.") 804 | SOME (Pending []) => raise Fail "update_pending invariant failure 2" 805 | SOME (Pending (ds as (_ :: _))) => let 806 fun foldthis ((d,tmrd),acc) = m(acc, valOf (r tmrd d)) 807 val ds' = List.rev ds 808 val (d1,tmrd1) = hd ds' 809 in 810 insert(inmap,thydataty, 811 Loaded (List.foldl foldthis (valOf (r tmrd1 d1)) (tl ds'))) 812 end 813 fun foldthis (k,v,acc) = insert(acc,k,update1 v) 814 val _ = allthydata := Binarymap.foldl foldthis 815 (Binarymap.mkDict String.compare) 816 (!allthydata) 817 val (seg as {thydata,...}) = theCT() 818in 819 makeCT (update_seg seg (U #thydata (update1 thydata)) $$) 820end 821 822fun 'a new {thydataty, merge, read, write, terms} = let 823 val (mk : 'a -> t, dest) = UniversalType.embed () 824 fun vdest t = valOf (dest t) 825 fun merge' (t1, t2) = mk(merge(vdest t1, vdest t2)) 826 fun read' tmread s = Option.map mk (read tmread s) 827 fun write' tmwrite t = write tmwrite (vdest t) 828 fun terms' t = terms (vdest t) 829in 830 update_pending (merge',read') thydataty; 831 dataops := Binarymap.insert(!dataops, thydataty, 832 {merge=merge', read=read', write=write', 833 terms=terms'}); 834 (mk,dest) 835end 836 837 838end (* struct *) 839 840 841 842(*---------------------------------------------------------------------------* 843 * PRINTING THEORIES OUT AS ML STRUCTURES AND SIGNATURES. * 844 *---------------------------------------------------------------------------*) 845 846fun theory_out p ostrm = 847 let 848 in 849 PP.prettyPrint ((fn s => TextIO.output(ostrm,s)), 75) p 850 handle e => (Portable.close_out ostrm; raise e); 851 TextIO.closeOut ostrm 852 end; 853 854fun unkind facts = 855 List.foldl (fn ((s,Axiom (_,th)),(A,D,T)) => ((s,th)::A,D,T) 856 | ((s,Defn th),(A,D,T)) => (A,(s,th)::D,T) 857 | ((s,Thm th),(A,D,T)) => (A,D,(s,th)::T)) ([],[],[]) facts; 858 859(* automatically reverses the list, which is what is needed. *) 860 861fun unadjzip [] A = A 862 | unadjzip ({sig_ps,struct_ps}::t) (l1,l2) = 863 unadjzip t (sig_ps::l1, struct_ps::l2) 864 865 866(*--------------------------------------------------------------------------- 867 We always export the theory, except if it is the initial theory (named 868 "scratch") and the initial theory is empty. If the initial theory is 869 *not* empty, i.e., the user made some definitions, or stored some 870 theorems or whatnot, then the initial theory will be exported. 871 ----------------------------------------------------------------------------*) 872 873fun total_cpu {usr,sys} = Time.+(usr,sys) 874val new_theory_time = ref (total_cpu (Timer.checkCPUTimer Globals.hol_clock)) 875 876val report_times = ref true 877val _ = Feedback.register_btrace ("report_thy_times", report_times) 878 879local 880 val mesg = Lib.with_flag(Feedback.MESG_to_string, Lib.I) HOL_MESG 881 fun maybe_log_time_to_disk thyname timestr = let 882 open FileSys 883 fun concatl pl = List.foldl (fn (p2,p1) => Path.concat(p1,p2)) 884 (hd pl) (tl pl) 885 val filename_opt = let 886 val build = Systeml.build_log_file 887 val currentdir = Systeml.make_log_file 888 in 889 List.find (fn s => access(s, [A_WRITE])) [build, currentdir] 890 end 891 in 892 case filename_opt of 893 SOME s => let 894 val fs = TextIO.openAppend s 895 in 896 TextIO.output(fs, thyname ^ " " ^ timestr ^ "\n"); 897 TextIO.closeOut fs 898 end 899 | NONE => () 900 end 901in 902fun export_theory () = let 903 val _ = call_hooks (TheoryDelta.ExportTheory (current_theory())) 904 val {thid,facts,adjoin,thydata,mldeps,...} = scrubCT() 905 val concat = String.concat 906 val thyname = thyid_name thid 907 val name = thyname^"Theory" 908 val (A,D,T) = unkind facts 909 val (sig_ps, struct_ps) = unadjzip adjoin ([],[]) 910 val sigthry = {name = thyname, 911 parents = map thyid_name (Graph.fringe()), 912 axioms = A, 913 definitions = D, 914 theorems = T, 915 sig_ps = sig_ps} 916 fun mungethydata dmap = let 917 fun foldthis (k,v,acc as (tmlist,dict)) = 918 case v of 919 Loaded t => 920 let 921 val {write,terms,...} = Binarymap.find(!LoadableThyData.dataops, k) 922 handle NotFound => raise ERR "export_theory" 923 ("Couldn't find thydata ops for "^k) 924 925 in 926 (terms t @ tmlist, 927 Binarymap.insert(dict,k,(fn wrtm => write wrtm t))) 928 end 929 | _ => acc 930 in 931 Binarymap.foldl foldthis ([], Binarymap.mkDict String.compare) dmap 932 end 933 val structthry = 934 {theory = dest_thyid thid, 935 parents = map dest_thyid (Graph.fringe()), 936 types = thy_types thyname, 937 constants = Lib.mapfilter Term.dest_const (thy_constants thyname), 938 axioms = A, 939 definitions = D, 940 theorems = T, 941 struct_ps = struct_ps, 942 thydata = mungethydata thydata, 943 mldeps = HOLset.listItems mldeps} 944 fun filtP s = not (Lexis.ok_sml_identifier s) andalso 945 not (is_temp_binding s) 946 in 947 case filter filtP (map fst (A@D@T)) of 948 [] => 949 (let val ostrm1 = Portable.open_out(concat["./",name,".sig"]) 950 val ostrm2 = Portable.open_out(concat["./",name,".sml"]) 951 val ostrm3 = Portable.open_out(concat["./",name,".dat"]) 952 val time_now = total_cpu (Timer.checkCPUTimer Globals.hol_clock) 953 val time_since = Time.-(time_now, !new_theory_time) 954 val tstr = Lib.time_to_string time_since 955 in 956 mesg ("Exporting theory "^Lib.quote thyname^" ... "); 957 theory_out (TheoryPP.pp_sig (!pp_thm) sigthry) ostrm1; 958 theory_out (TheoryPP.pp_struct structthry) ostrm2; 959 theory_out (TheoryPP.pp_thydata structthry) ostrm3; 960 mesg "done.\n"; 961 if !report_times then 962 (mesg ("Theory "^Lib.quote thyname^" took "^ tstr ^ " to build\n"); 963 maybe_log_time_to_disk thyname (Time.toString time_since)) 964 else () 965 end 966 handle e => (Lib.say "\nFailure while writing theory!\n"; raise e)) 967 968 | badnames => 969 (HOL_MESG 970 (String.concat 971 ["\nThe following ML binding names in the theory to be exported:\n", 972 String.concat (Lib.commafy (map Lib.quote badnames)), 973 "\n are not acceptable ML identifiers.\n", 974 " Use `set_MLname <bad> <good>' to change each name."]); 975 raise ERR "export_theory" "bad binding names") 976end 977end; 978 979 980(* ---------------------------------------------------------------------- 981 "on load" stuff 982 ---------------------------------------------------------------------- *) 983 984fun load_complete thyname = 985 call_hooks (TheoryDelta.TheoryLoaded thyname) 986 987 988(* ---------------------------------------------------------------------- 989 Allocate a new theory segment over an existing one. After 990 that, initialize any registered packages. A package registers 991 with a call to "register_hook". 992 ---------------------------------------------------------------------- *) 993 994fun new_theory str = 995 if not(Lexis.ok_identifier str) then 996 raise ERR "new_theory" 997 ("proposed theory name "^Lib.quote str^" is not an identifier") 998 else let 999 val thy as {thid, ...} = theCT() 1000 val thyname = thyid_name thid 1001 val tdelta = TheoryDelta.NewTheory{oldseg=thyname,newseg=str} 1002 fun mk_thy () = (HOL_MESG ("Created theory "^Lib.quote str); 1003 makeCT(fresh_segment str); 1004 call_hooks tdelta) 1005 val _ = 1006 new_theory_time := total_cpu (Timer.checkCPUTimer Globals.hol_clock) 1007 in 1008 if str=thyname then 1009 (HOL_MESG("Restarting theory "^Lib.quote str); 1010 zapCT str; 1011 call_hooks tdelta) 1012 else if mem str (ancestry thyname) then 1013 raise ERR"new_theory" ("theory: "^Lib.quote str^" already exists.") 1014 else if thyname="scratch" andalso empty_segment thy then 1015 mk_thy() 1016 else 1017 (export_theory (); 1018 Graph.add (thid, Graph.fringe()); mk_thy ()) 1019 end 1020 1021 1022(* ---------------------------------------------------------------------- 1023 Function f tries to extend current theory. If that fails then 1024 revert to previous state. 1025 1026 We do not (yet) track changes to the state used by adjoin_to_theory or 1027 any hooks, though the hooks should see the changes adding and 1028 removing things from the "signature". 1029 ---------------------------------------------------------------------- *) 1030 1031fun try_theory_extension f x = 1032 let open Term 1033 val tnames1 = map fst (types"-") 1034 val cnames1 = map (fst o dest_const) (constants"-") 1035 fun revert _ = 1036 let val tnames2 = map fst (types"-") 1037 val cnames2 = map (fst o dest_const) (constants"-") 1038 val new_tnames = Lib.set_diff tnames2 tnames1 1039 val new_cnames = Lib.set_diff cnames2 cnames1 1040 in map delete_type new_tnames; 1041 map delete_const new_cnames; 1042 scrub() 1043 end 1044 in 1045 f x handle e => (revert(); raise e) 1046 end; 1047 1048structure Definition = 1049struct 1050(* ---------------------------------------------------------------------- 1051 DESCRIPTION : Principles of type definition, constant specification 1052 and constant definition. Almost the same as in hol88, 1053 except that parsing status is not dealt with by the 1054 functions in this module (at this stage, the parser 1055 hasn't been compiled yet). A further difference is 1056 the principle of constant definition is not derived 1057 from constant specification, as in hol88. The 1058 principle of definition has also been changed to be 1059 simpler than that of hol88. 1060 1061 AUTHOR : (c) Mike Gordon, University of Cambridge 1062 TRANSLATOR : Konrad Slind, University of Calgary 1063 DATE : September 11, 1991 -- translated 1064 DATE : October 1, 2000 -- union of previous 3 modules 1065 ---------------------------------------------------------------------- *) 1066 1067val ERR = mk_HOL_ERR "Definition"; 1068val TYDEF_ERR = ERR "new_type_definition" 1069val DEF_ERR = ERR "new_definition" 1070val SPEC_ERR = ERR "new_specification"; 1071 1072val TYDEF_FORM_ERR = TYDEF_ERR "expected a theorem of the form \"?x. P x\""; 1073val DEF_FORM_ERR = DEF_ERR "expected a term of the form \"v = M\""; 1074 1075(*--------------------------------------------------------------------------- 1076 Misc. utilities. There are some local definitions of syntax 1077 operations, since we delay defining all the basic formula 1078 operations until after boolTheory is built. 1079 ---------------------------------------------------------------------------*) 1080 1081fun mk_exists (absrec as (Bvar,_)) = 1082 mk_comb(mk_thy_const{Name="?",Thy="bool", Ty= (type_of Bvar-->bool)-->bool}, 1083 mk_abs absrec) 1084 1085fun dest_exists M = 1086 let val (Rator,Rand) = with_exn dest_comb M (TYDEF_ERR"dest_exists") 1087 in case total dest_thy_const Rator 1088 of SOME{Name="?",Thy="bool",...} => dest_abs Rand 1089 | otherwise => raise TYDEF_ERR"dest_exists" 1090 end 1091 1092fun nstrip_exists 0 t = ([],t) 1093 | nstrip_exists n t = 1094 let val (Bvar, Body) = dest_exists t 1095 val (l,t'') = nstrip_exists (n-1) Body 1096 in (Bvar::l, t'') 1097 end; 1098 1099fun mk_eq (lhs,rhs) = 1100 let val ty = type_of lhs 1101 val eq = mk_thy_const{Name="=",Thy="min",Ty=ty-->ty-->bool} 1102 in list_mk_comb(eq,[lhs,rhs]) 1103 end; 1104 1105fun dest_eq M = 1106 let val (Rator,r) = dest_comb M 1107 val (eq,l) = dest_comb Rator 1108 in case dest_thy_const eq 1109 of {Name="=",Thy="min",...} => (l,r) 1110 | _ => raise ERR "dest_eq" "not an equality" 1111 end; 1112 1113fun check_null_hyp th f = 1114 if null(Thm.hyp th) then () 1115 else raise f "theorem must have no assumptions"; 1116 1117fun check_free_vars tm f = 1118 case free_vars tm 1119 of [] => () 1120 | V => raise f (String.concat 1121 ("Free variables in rhs of definition: " 1122 :: commafy (map (Lib.quote o fst o dest_var) V))); 1123 1124fun check_tyvars body_tyvars ty f = 1125 case Lib.set_diff body_tyvars (type_vars ty) 1126 of [] => () 1127 | extras => 1128 raise f (String.concat 1129 ("Unbound type variable(s) in definition: " 1130 :: commafy (map (Lib.quote o dest_vartype) extras))); 1131 1132val new_definition_hook = ref 1133 ((fn tm => ([], tm)), 1134 (fn (V,th) => 1135 if null V then th 1136 else raise ERR "new_definition" "bare post-processing phase")); 1137 1138fun okChar c = Char.isGraph c andalso c <> #"(" andalso c <> #")" 1139 1140fun check_name princ_name name = 1141 if CharVector.all okChar name then true 1142 else raise ERR 1143 princ_name 1144 ("Entity name >"^name^"< includes non-printable/bad character") 1145 1146(*---------------------------------------------------------------------------*) 1147(* DEFINITION PRINCIPLES *) 1148(*---------------------------------------------------------------------------*) 1149 1150fun new_type_definition (name,thm) = let 1151 val Thy = current_theory() 1152 val _ = is_temp_binding name orelse check_name "new_type_definition" name 1153 val tydef = Thm.prim_type_definition({Thy = Thy, Tyop = name}, thm) 1154 in 1155 store_definition (name^"_TY_DEF", tydef) before 1156 call_hooks (TheoryDelta.NewTypeOp{Name = name, Thy = Thy}) 1157 end 1158 handle e => raise (wrap_exn "Theory.Definition" "new_type_definition" e); 1159 1160fun gen_new_specification(name, th) = let 1161 val thy = current_theory() 1162 val (cnames,def) = Thm.gen_prim_specification thy th 1163 in 1164 store_definition (name, def) before 1165 List.app (fn s => call_hooks (TheoryDelta.NewConstant{Name=s, Thy=thy})) 1166 cnames 1167 end 1168 handle e => raise (wrap_exn "Definition" "gen_new_specification" e); 1169 1170fun new_definition(name,M) = 1171 let val (dest,post) = !new_definition_hook 1172 val (V,eq) = dest M 1173 val (nm, _) = eq |> dest_eq |> #1 |> dest_var 1174 handle HOL_ERR _ => 1175 raise ERR "Definition.new_definition" 1176 "Definition not an equality" 1177 val _ = is_temp_binding name orelse 1178 check_name "new_definition" nm 1179 val Thy = current_theory() 1180 val (cn,def_th) = Thm.gen_prim_specification Thy (Thm.ASSUME eq) 1181 val Name = case cn of [Name] => Name | _ => raise Match 1182 in 1183 store_definition (name, post(V,def_th)) before 1184 call_hooks (TheoryDelta.NewConstant{Name=Name, Thy=Thy}) 1185 end 1186 handle e => raise (wrap_exn "Definition" "new_definition" e); 1187 1188fun new_specification (name, cnames, th) = let 1189 val thy = current_theory() 1190 val _ = is_temp_binding name orelse 1191 List.all (check_name "new_specification") cnames 1192 val def = Thm.prim_specification thy cnames th 1193 val final = store_definition (name, def) 1194 in 1195 List.app (fn s => call_hooks (TheoryDelta.NewConstant{Name=s, Thy = thy})) 1196 cnames 1197 ; final 1198 end 1199 handle e => raise (wrap_exn "Definition" "new_specification" e); 1200 1201end (* Definition struct *) 1202 1203end (* Theory *) 1204