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