1structure ThmSetData :> ThmSetData =
2struct
3
4open DB Lib HolKernel
5
6val ERR = mk_HOL_ERR "ThmSetData"
7
8type data = LoadableThyData.t
9type thname = KernelSig.kernelname
10val name_toString = KernelSig.name_toString
11datatype setdelta =
12         ADD of thname * thm
13       | REMOVE of string (* could be anything *)
14datatype raw_delta = rADD of thname | rREMOVE of string
15
16val added_thms = List.mapPartial (fn ADD (_, th) => SOME th | _ => NONE)
17
18fun splitnm nm = let
19  val comps = String.fields (equal #".") nm
20in
21  case comps of
22    [thy,nm] => (thy, nm)
23  | _ => raise Fail ("ThmSetData.splitnm applied to " ^ nm)
24end
25
26fun mk_store_name_safe s =
27   case String.fields (equal #".") s of
28       [s0] => {Thy = current_theory(), Name = s}
29     | [s1,s2] => {Thy = s1, Name = s2}
30     | _ => raise mk_HOL_ERR "ThmSetData" "mk_store_name_safe"
31                  ("Malformed name: " ^ s)
32
33fun lookup_exn {Thy,Name} = DB.fetch Thy Name
34fun mk_radd s = rADD (mk_store_name_safe s)
35fun mk_add s =
36    let val nm = mk_store_name_safe s in ADD(nm, lookup_exn nm) end
37
38
39fun lookup ty nm =
40    SOME (lookup_exn nm)
41    handle HOL_ERR _ => (
42      Feedback.HOL_WARNING
43        "ThmSetData" "lookup"
44        ("Bad theorem name: \"" ^ name_toString nm ^ "\" for set-type \"" ^
45         ty ^ "\"");
46      NONE
47    )
48
49local
50  open ThyDataSexp
51in
52val pp_sexp = pp_sexp Parse.pp_type Parse.pp_term Parse.pp_thm
53fun sexp2string sexp = PP.pp_to_string (!Globals.linewidth) pp_sexp sexp
54end;
55
56datatype read_result = OK of setdelta | BAD_ADD of thname
57
58fun raw_read1 ty sexp =
59    let
60      open ThyDataSexp
61    in
62      case sexp of
63          KName nm => SOME (rADD nm)
64        | String nm => SOME (rREMOVE nm)
65        | _ => NONE
66    end
67
68fun raw_write1 d =
69    let
70      open ThyDataSexp
71    in
72      case d of
73          rADD knm => KName knm
74        | rREMOVE s => String s
75    end
76
77fun cook (rADD nm) = ADD(nm, lookup_exn nm)
78  | cook (rREMOVE s) = REMOVE s
79
80fun read ty sexp =
81    let
82      open ThyDataSexp
83      fun decode1 sexp =
84          case sexp of
85              KName nm =>
86                (OK (ADD (nm,lookup_exn nm)) handle HOL_ERR _ => BAD_ADD nm)
87            | String nm => OK (REMOVE nm)
88            | _ => raise ERR "read" ("Bad sexp for 1 update: "^sexp2string sexp)
89    in
90      case sexp of
91          List deltas => map decode1 deltas
92        | _ => raise ERR "read" ("Bad sexp for type "^ty^": "^sexp2string sexp)
93    end
94
95fun write10 d =
96    case d of
97        ADD(knm,th) => ThyDataSexp.KName knm
98      | REMOVE s => ThyDataSexp.String s
99
100fun write_deltas ds = ThyDataSexp.List (map write10 ds)
101
102fun write1 d = write_deltas [d]
103
104
105
106(* ----------------------------------------------------------------------
107    destfn: takes polymorphic theory data and turns it into the
108            structured theorem data we want to use
109    storefn: takes a theorem name and stores the associated theorem into
110             the theory data.  Doesn't perform any associated update in
111             the run-time memory
112    exportfn: takes a string and a list of names and theorems and does
113              something with them at runtime (adds them to a simpset,
114              say). The string is expected to be the name of the
115              theory with which these theorems are associated.
116   ---------------------------------------------------------------------- *)
117
118type exportfns =
119     { add : {thy : string, named_thm : (thname * thm)} -> unit,
120       remove : {thy : string, remove : string} -> unit}
121
122type tabledata = ({thyname:string}->setdelta list) * exportfns
123
124val data_map = ref (Symtab.empty : tabledata Symtab.table)
125
126fun data_exportfns {settype = s} = Option.map #2 (Symtab.lookup (!data_map) s)
127
128fun all_set_types () = Symtab.keys (!data_map)
129
130fun theory_data {settype = key, thy} =
131    case Symtab.lookup (!data_map) key of
132      NONE => raise mk_HOL_ERR "ThmSetData" "theory_data"
133                    ("No ThmSetData with name "^Lib.quote key)
134    | SOME (sdf,_) => sdf {thyname=thy}
135
136fun current_data {settype = s} =
137    theory_data { settype = s, thy = current_theory() }
138
139fun all_data {settype = s} =
140    map (fn thy => (thy, theory_data {settype = s, thy = thy}))
141        (current_theory() :: ancestry "-")
142
143
144fun new_exporter {settype = name, efns = efns as {add, remove}} = let
145  open TheoryDelta
146  val dropBads =
147      List.mapPartial
148        (fn OK sd => SOME sd
149        | BAD_ADD s => (
150          HOL_WARNING "ThmSetData" "apply_delta"
151                      ("Bad add command, with name: "^name_toString s); NONE))
152  fun apply_deltas thyname ds =
153      let
154        fun appthis (ADD (s,th)) = add {thy = thyname, named_thm = (s, th)}
155          | appthis (REMOVE s) = remove {thy = thyname, remove =  s}
156      in
157        List.app appthis ds
158      end
159  fun loadfn {thyname, data = data_opt} =
160      case data_opt of
161          NONE => ()
162        | SOME data => apply_deltas thyname (dropBads (read name data))
163  fun uptodate_thmdelta (ADD (s,th)) = uptodate_thm th
164    | uptodate_thmdelta _ = true
165  fun neqbinding s1 (ADD ({Thy,Name},_)) =
166         s1 <> Name orelse current_theory () <> Thy
167    | neqbinding _ _ = true
168  fun toString (ADD (s,_)) = "ADD<" ^ name_toString s ^ ">"
169    | toString (REMOVE s) = "REMOVE<" ^ s ^ ">"
170  fun read_result_toString (OK sd) = toString sd
171    | read_result_toString (BAD_ADD s) = "ADD<" ^ name_toString s ^ ">"
172  fun check_result P (OK d) = P d
173    | check_result _ (BAD_ADD s) = false
174  fun revise_data P (deltas_sexp,td) =
175      let
176        val deltas = read name deltas_sexp
177        val (ok,notok) = Lib.partition (check_result P) deltas
178      in
179        case notok of
180            [] => NONE
181          | _ => (HOL_WARNING
182                      "ThmSetData" "revise_data"
183                      ("\n  Theorems in set " ^ Lib.quote name ^
184                       ":\n    " ^
185                       String.concatWith ", " (map read_result_toString notok) ^
186                       "\n  invalidated by " ^ TheoryDelta.toString td);
187                  SOME (write_deltas (dropBads ok)))
188      end
189
190  fun hook (DelConstant _) = uptodate_thmdelta
191    | hook (DelTypeOp _) = uptodate_thmdelta
192    | hook (NewConstant _) = uptodate_thmdelta
193    | hook (NewTypeOp _) = uptodate_thmdelta
194    | hook (DelBinding s) = neqbinding s
195    | hook (NewBinding(s,_)) = neqbinding s
196    | hook _ = fn _ => true
197  fun check_thydelta (arg as (sexp,td)) = revise_data (hook td) arg
198
199
200  val {export = export_deltasexp, segment_data, ...} =
201      ThyDataSexp.new {merge = ThyDataSexp.append_merge, load = loadfn,
202                       other_tds = check_thydelta, thydataty = name}
203  fun opt2list (SOME x) = x | opt2list NONE = []
204  fun segdata {thyname} =
205      segment_data {thyname=thyname}
206                   |> Option.map (dropBads o read name)
207                   |> opt2list
208  fun export p (* name * thm *) =
209      let
210      in
211        add {thy = current_theory(), named_thm = p};
212        export_deltasexp (write1 (ADD p))
213      end
214
215  fun onload thy = apply_deltas thy (segdata {thyname = thy})
216
217  fun export_nameonly s =
218      let val thnm = mk_store_name_safe s
219      in
220        export(thnm, lookup_exn thnm)
221      end
222  fun delete s = let
223    val data = write1 (REMOVE s)
224  in
225    remove {thy = current_theory(), remove = s};
226    export_deltasexp data
227  end
228
229  fun store_attrfun {attrname,name,thm} = export (mk_store_name_safe name,thm)
230  fun local_attrfun {attrname,name,thm} =
231    add {named_thm = ({Thy = current_theory(), Name = name},thm),
232         thy = current_theory()}
233in
234  data_map := Symtab.update(name,(segdata, efns)) (!data_map);
235  ThmAttribute.register_attribute (
236    name, {storedf = store_attrfun, localf = local_attrfun}
237  );
238  List.app onload (ancestry "-");
239  {export = export_nameonly, delete = delete}
240end
241
242fun lift nm = {Thy = current_theory(), Name = nm}
243
244fun mk_raw (ADD(nm,_)) = rADD nm
245  | mk_raw (REMOVE s) = rREMOVE s
246
247type 'value ops = {apply_to_global : setdelta -> 'value -> 'value,
248                   thy_finaliser : ({thyname:string} -> setdelta list ->
249                                    'value -> 'value) option,
250                   uptodate_delta : setdelta -> bool, initial_value : 'value,
251                   apply_delta : setdelta -> 'value -> 'value}
252fun raw_uptodate (rADD{Thy,Name}) = can (DB.fetch Thy) Name
253  | raw_uptodate _ = true
254
255fun export_with_ancestry
256      {settype,delta_ops:'value ops}:(setdelta,'value)AncestryData.fullresult =
257    let
258      val {apply_to_global, uptodate_delta, initial_value, apply_delta,
259           thy_finaliser} =
260          delta_ops
261      fun raw_apply_delta d = apply_delta (cook d)
262      fun raw_apply_global d = apply_to_global (cook d)
263      fun mk_raw_finaliser f thy ds = f thy (map cook ds)
264      val raw_finaliser = Option.map mk_raw_finaliser thy_finaliser
265      val adinfo = {tag = settype, initial_values = [("min", initial_value)],
266                    apply_delta = raw_apply_delta}
267      val sexps = {dec = raw_read1 settype, enc = raw_write1}
268      val globinfo = {apply_to_global = raw_apply_global,
269                      thy_finaliser = raw_finaliser,
270                      initial_value = initial_value}
271      fun uptodate rd = raw_uptodate rd andalso uptodate_delta (cook rd)
272      val fullresult =
273          AncestryData.fullmake { adinfo = adinfo,
274                                  uptodate_delta = uptodate,
275                                  sexps = sexps, globinfo = globinfo}
276      fun store_attrfun {attrname,name,thm} =
277          let val d = rADD(lift name)
278          in
279            #record_delta fullresult d;
280            #update_global_value fullresult (raw_apply_global d)
281          end
282      fun local_attrfun {attrname,name,thm} =
283          (* as this is local, the name is not going to be a valid binding,
284             and "cooking" a raw ADD delta will just throw an exception *)
285          #update_global_value fullresult(apply_to_global (ADD(lift name, thm)))
286      fun efn_add {thy,named_thm} =
287          #update_global_value fullresult(raw_apply_global(rADD (#1 named_thm)))
288      fun efn_remove {thy,remove} =
289          #update_global_value fullresult (raw_apply_global (rREMOVE remove))
290      val efns = {add = efn_add, remove = efn_remove}
291      val get_fulldeltas = map cook o #get_deltas fullresult
292    in
293      data_map := Symtab.update(settype, (get_fulldeltas, efns)) (!data_map);
294      ThmAttribute.register_attribute (
295        settype, {storedf = store_attrfun, localf = local_attrfun}
296      );
297      {merge = #merge fullresult, DB = #DB fullresult,
298       get_deltas = get_fulldeltas,
299       record_delta = #record_delta fullresult o mk_raw,
300       parents = #parents fullresult, set_parents = #set_parents fullresult,
301       get_global_value = #get_global_value fullresult,
302       update_global_value = #update_global_value fullresult
303      }
304    end
305
306
307end (* struct *)
308