(* ========================================================================= *) (* Proof-objects for HOL-light *) (* *) (* Steven Obua, TU München, December 2004 *) (* *) (* based on Sebastian Skalberg's HOL4 proof-objects *) (* ========================================================================= *) #load "unix.cma";; module type Proofobject_primitives = sig type proof val proof_REFL : term -> proof val proof_TRANS : proof * proof -> proof val proof_MK_COMB : proof * proof -> proof val proof_ASSUME : term -> proof val proof_EQ_MP : proof -> proof -> proof val proof_IMPAS : proof -> proof -> proof val proof_DISCH : proof -> term -> proof val proof_DEDUCT_ANTISYM_RULE : proof * term -> proof * term -> proof val proof_BETA : term -> proof val proof_ABS : term -> proof -> proof val proof_INST_TYPE : (hol_type * hol_type) list -> proof -> proof val proof_INST : (term * term) list -> proof -> proof val proof_new_definition : string -> hol_type -> term -> proof val proof_CONJ : proof -> proof -> proof val proof_CONJUNCT1 : proof -> proof val proof_CONJUNCT2 : proof -> proof val proof_new_basic_type_definition : string -> string * string -> term * term -> proof -> proof val proof_SPEC : term -> proof -> proof val proof_SYM : proof -> proof val proof_GEN : proof -> term -> proof val proof_DISJ1 : proof -> term -> proof val proof_DISJ2 : proof -> term -> proof val proof_NOTI : proof -> proof val proof_NOTE : proof -> proof val proof_CONTR : proof -> term -> proof val proof_DISJCASES : proof -> proof -> proof -> proof val proof_CHOOSE : term -> proof -> proof -> proof val proof_EXISTS : term -> term -> proof -> proof val new_axiom_name : string -> string val proof_new_axiom : string -> term -> proof val save_proof : string -> proof -> (term option) -> unit val proof_database : unit -> ((string * proof * (term option)) list) val export_proofs : string option -> (string * proof * (term option)) list -> unit val export_saved_proofs : string option -> unit end;; module Proofobjects : Proofobject_primitives = struct let writeln s p = p;; (* let q = s^"\n" in (output stdout q 0 (String.length q); p);;*) type tag = string type proof_info_rec = {disk_info: (string * string) option ref; status: int ref; references: int ref; queued: bool ref} type proof_info = Info of proof_info_rec type ('a, 'b) libsubst_rec = {redex:'a; residue:'b} type ('a, 'b) libsubst = (('a,'b) libsubst_rec) list let pair2libsubstrec = fun (a,b) -> {redex=b;residue=a} (* note: not all of the proof_content constructors are actually used, some are just legacy from the HOL4 proof objects *) type proof = Proof of (proof_info * proof_content * (unit -> unit)) and proof_content = Prefl of term | Pinstt of proof * ((hol_type,hol_type) libsubst) | Psubst of proof list * term * proof | Pabs of proof * term | Pdisch of proof * term | Pmp of proof * proof | Phyp of term | Paxm of string * term | Pdef of string * string * term | Ptmspec of string * string list * proof | Ptydef of string * string * proof | Ptyintro of string * string * string * string * term * term * proof | Poracle of tag * term list * term | Pdisk | Pspec of proof * term | Pinst of proof * (term,term) libsubst | Pgen of proof * term | Pgenabs of proof * term option * term list | Psym of proof | Ptrans of proof * proof | Pcomb of proof * proof | Peqmp of proof * proof | Peqimp of proof | Pexists of proof * term * term | Pchoose of term * proof * proof | Pconj of proof * proof | Pconjunct1 of proof | Pconjunct2 of proof | Pdisj1 of proof * term | Pdisj2 of proof * term | Pdisjcases of proof * proof * proof | Pnoti of proof | Pnote of proof | Pcontr of proof * term | Pimpas of proof * proof let THEORY_NAME = "hollight" let content_of (Proof (_,p,_)) = p let inc_references (Proof(Info{references=r},_,_) as p) = ( let old = !r in r := old + 1; p) let concat = String.concat "" let dummy_fun () = () let mk_proof p = Proof(Info {disk_info = ref None; status = ref 0; references = ref 0; queued = ref false},p, dummy_fun) let global_ax_counter = let counter = ref 1 in let f = fun () -> (let x = !counter in counter := !counter+1; x) in f let new_axiom_name n = concat["ax_"; n; "_"; string_of_int(global_ax_counter())] let proof_REFL t = writeln "REFL" (mk_proof (Prefl t)) let proof_TRANS (p,q) = writeln "TRANS" ( match (content_of p, content_of q) with (Prefl _,_) -> q | (_, Prefl _) -> p | _ -> mk_proof (Ptrans (inc_references p, inc_references q))) let proof_MK_COMB (p1,p2) = writeln "MK_COMB" ( (match (content_of p1, content_of p2) with (Prefl tm1, Prefl tm2) -> proof_REFL (mk_comb (tm1, tm2)) | _ -> mk_proof (Pcomb (inc_references p1, inc_references p2)))) let proof_ASSUME t = writeln "ASSUME "(mk_proof (Phyp t)) let proof_EQ_MP p q = writeln "EQ_MP" ( (match content_of p with Prefl _ -> q | _ -> mk_proof (Peqmp(inc_references p, inc_references q)))) let proof_IMPAS p1 p2 = writeln "IMPAS" ( mk_proof (Pimpas (inc_references p1, inc_references p2))) let proof_DISCH p t = writeln "DISCH" (mk_proof (Pdisch(inc_references p,t))) let proof_DEDUCT_ANTISYM_RULE (p1,t1) (p2,t2) = writeln "DEDUCT_ANTISYM_RULE" ( proof_IMPAS (proof_DISCH p1 t2) (proof_DISCH p2 t1)) let proof_BETA t = writeln "BETA" (mk_proof (Prefl t)) let proof_ABS x p = writeln "ABS" ( (match (content_of p) with Prefl tm -> proof_REFL (mk_abs(x,tm)) | _ -> mk_proof (Pabs(inc_references p,x)))) let proof_INST_TYPE s p = writeln "INST_TYPE" (mk_proof (Pinstt(inc_references p, map pair2libsubstrec s))) let proof_INST s p = writeln "INST" (mk_proof (Pinst(inc_references p, map pair2libsubstrec s))) let proof_new_definition cname _ t = writeln "new_definition" (mk_proof (Pdef (THEORY_NAME, cname, t))) let proof_new_axiom axname t = writeln "new_axiom" (mk_proof (Paxm (axname, t))) let proof_CONJ p1 p2 = writeln "CONJ" (mk_proof (Pconj (inc_references p1, inc_references p2))) let proof_CONJUNCT1 p = writeln "CONJUNCT1" (mk_proof (Pconjunct1 (inc_references p))) let proof_CONJUNCT2 p = writeln "CONJUNCT2" (mk_proof (Pconjunct2 (inc_references p))) let proof_new_basic_type_definition tyname (absname, repname) (pt,tt) p = writeln "new_basic_type_definition" ( mk_proof(Ptyintro (THEORY_NAME, tyname, absname, repname, pt, tt,inc_references p))) (* ---- used only in substitute_proof calls ---- *) let proof_SPEC s p = writeln "SPEC" (mk_proof (Pspec(inc_references p, s))) let proof_SYM p = writeln "SYM" (mk_proof (Psym(inc_references p))) let proof_GEN p a = writeln "GEN" (mk_proof (Pgen(inc_references p, a))) let proof_DISJ1 p a = writeln "DISJ1" (mk_proof (Pdisj1 (inc_references p, a))) let proof_DISJ2 p a = writeln "DISJ2" (mk_proof (Pdisj2 (inc_references p, a))) let proof_NOTI p = writeln "NOTI" (mk_proof (Pnoti (inc_references p))) let proof_NOTE p = writeln "NOTE" (mk_proof (Pnote (inc_references p))) let proof_CONTR p a = writeln "CONTR" (mk_proof (Pcontr (inc_references p, a))) let proof_DISJCASES p q r = writeln "DISJCASES" (mk_proof (Pdisjcases (inc_references p, inc_references q, inc_references r))) let proof_CHOOSE a p q = writeln "CHOOSE" (mk_proof (Pchoose (a, inc_references p, inc_references q))) let proof_EXISTS x y p = writeln "EXISTS" (mk_proof (Pexists (inc_references p, x, y))) (* ---- formerly known as proofio.ml ---- *) let ensure_export_directory thyname = let dir = Sys.getenv "HOLPROOFEXPORTDIR" in let dirsub = Filename.concat dir "hollight" in let dirsubsub = Filename.concat dirsub thyname in let mk d = if Sys.file_exists d then () else Unix.mkdir d 509 in (mk dir; mk dirsub; mk dirsubsub; dirsubsub);; (* ---- Useful functions on terms ---- *) let rec types_of tm = if is_var tm then [type_of tm] else if is_const tm then [type_of tm] else if is_comb tm then let (f,a) = dest_comb tm in union (types_of f) (types_of a) else let (x,a) = dest_abs tm in insert (type_of x) (types_of a);; let beta_conv tm = try let (f,arg) = dest_comb tm in let (v,bod) = dest_abs f in vsubst [(arg,v)] bod with Failure _ -> failwith "beta_conv: Not a beta-redex";; let eta_conv tm = try (let (v, bod) = dest_abs tm in let (f, arg) = dest_comb bod in if (arg = v && (not(vfree_in v f))) then f else failwith "") with Failure _ -> failwith "eta_conv: Not an eta-redex";; let rec be_contract tm = let rec bec tm = try try Some (beta_conv tm) with Failure _ -> Some (eta_conv tm) with Failure _ -> if is_comb tm then (let (f,x) = dest_comb tm in match bec f with Some f' -> Some (mk_comb(f',x)) | None -> (match bec x with Some x' -> Some (mk_comb(f,x')) | None -> None)) else if is_abs tm then (let (x,body) = dest_abs tm in (match bec body with Some body' -> Some (mk_abs(x,body')) | None -> None)) else None in (match bec tm with Some tm' -> be_contract tm' | None -> tm);; let rec polymorphic x = if is_vartype x then true else exists polymorphic (snd (dest_type x)) (* ---- From Lib etc. ---- *) let rec append = fun xlist l -> (match xlist with [] -> l | (x::xs) -> x::(append xs l));; let assoc1 item = let rec assc = (function (((key,_) as e)::rst) -> if item=key then Some e else assc rst | [] -> None) in assc;; let rec listconcat = function [] -> [] | (l::ls) -> append l (listconcat ls);; let listnull = function [] -> true | _ -> false;; (* ---- exported ---- *) let encodeXMLEntities m = m;;let encodeXMLEntities s = let len = String.length s in let encodeChar = function '<' -> "<" | '>' -> ">" | '&' -> "&" | '\'' -> "'" | '"' -> """ | c -> String.make 1 c in let rec encodeStr i = if (i out (encodeXMLEntities x);; let content_of (Proof (_,x,_)) = x;; let rec explode_subst = function [] -> [] | ({redex=x;residue=y}::rest) -> x::y::(explode_subst rest);; let rec app f = function [] -> () | (x::l) -> (f x; app f l);; let disk_info_of (Proof(Info {disk_info=di},_,_)) = !di;; let set_disk_info_of (Proof(Info {disk_info=di},_,_)) thyname thmname = di := Some (thyname,thmname);; let references (Proof (Info info,_,_)) = !(info.references);; let wrap b e s = b^s^e;; let xml_empty_tag = wrap "<" "/>";; let xml_start_tag = wrap "<" ">";; let xml_end_tag = wrap "";; let xml_attr attr = itlist (function (tag,v) -> function s -> concat[" ";tag;"=\"";v;"\"";s] ) attr "";; let xml_element tag attr children = let header = tag ^ (xml_attr attr) in (if listnull children then xml_empty_tag header else wrap (xml_start_tag header) (xml_end_tag tag) (concat children));; let id_to_atts curthy id = [("n", encodeXMLEntities id)];; (* There is only one theory in Hol-Light, therefore id_to_atts is superfluous *) let glob_counter = ref 1;; let get_counter () = let res = !glob_counter in glob_counter := res + 1; res;; let get_iname = string_of_int o get_counter;; let next_counter () = !glob_counter;; let trivial p = match (content_of p) with Prefl _ -> true | Paxm _ -> true | Pdisk -> true | Phyp _ -> true | Poracle _ -> true | _ -> false;; let do_share p = references p > 1 & not (trivial p);; exception Err of string*string;; (* ---- The General List Formerly Known As Net ---- *) type 'a exprnet = (('a list) ref) * ('a -> ('a list)) let empty_net f () = (ref [], f);; let rec lookup'_net net x = match net with [] -> raise Not_found | (a::l) -> if (a = x) then 0 else 1+(lookup'_net l x);; let lookup_net (net,f) x = lookup'_net (!net) x;; let insert'_net (net,f) x = try lookup'_net !net x; () with Not_found -> ((net := (!net)@[x]);());; let rec insert_net ((net,f) as n) x = (app (insert_net n) (f x); insert'_net n x);; let to_list_net (net,f) = !net;; (* ---- The Type Net (it's not a net any more!) ---- *) type yy_net = hol_type exprnet;; let yy_empty = empty_net (function x -> if is_type x then snd (dest_type x) else []);; let yy_lookup = lookup_net;; let yy_output_types out thyname net = let all_types = to_list_net net in let rec xml_index ty = xml_element "tyi" [("i",string_of_int (yy_lookup net ty))] [] and xml_const id = xml_element "tyc" (id_to_atts thyname id) [] and out_type ty = if is_vartype ty then out (xml_element "tyv" [("n",encodeXMLEntities (dest_vartype ty))] []) else ( match dest_type ty with (id, []) -> out (xml_const id) | (id, tl) -> out (xml_element "tya" [] ((xml_const id)::(map xml_index tl))) ) in out ""; app out_type all_types; out "";; let yy_insert = insert_net;; (* ---- The Term Net (it's not a net anymore!) ---- *) type mm_net = term exprnet;; let mm_empty = empty_net ( function tm -> if is_abs tm then (let (x,b) = dest_abs tm in [x; b]) else if is_comb tm then (let (s,t) = dest_comb tm in [s; t]) else []) let mm_lookup net x = lookup_net net (be_contract x);; let mm_insert net x = insert_net net (be_contract x);; let mm_output_terms out thyname types net = let all_terms = to_list_net net in let xml_type ty = xml_element "tyi" [("i",string_of_int (yy_lookup types ty))] [] in let xml_index tm = xml_element "tmi" [("i",string_of_int (mm_lookup net tm))] [] in let out_term tm = if is_var tm then let (name,ty) = dest_var tm in out (xml_element "tmv" [("n",encodeXMLEntities name);("t", string_of_int (yy_lookup types ty))] []) else if is_const tm then let (name, ty) = (dest_const tm) in let general_ty = get_const_type name in let atts = [("n",encodeXMLEntities name)] in if polymorphic general_ty then out (xml_element "tmc" (atts@[("t",string_of_int (yy_lookup types ty))]) []) else out (xml_element "tmc" atts []) else if is_comb tm then let (f,a) = dest_comb tm in out (xml_element "tma" [("f", string_of_int (mm_lookup net f));("a",string_of_int (mm_lookup net a))] []) else let (x,a) = dest_abs tm in out (xml_element "tml" [("x", string_of_int (mm_lookup net x));("a",string_of_int (mm_lookup net a))] []) in out ""; app out_term all_terms; out "";; (* ---- collect_types_terms ---- *) let collect_types_terms thyname out prf c_opt = let will_be_shared prf = ( match disk_info_of prf with Some _ -> true | None -> do_share prf) in let types = yy_empty () in let terms = mm_empty () in let insert_type ty = yy_insert types ty in let insert_term tm = (mm_insert terms tm; app (yy_insert types) (types_of tm)) in let rec ct' prf = (match content_of prf with Pinstt(prf,tsubst) -> (app (function {redex=x;residue=u}->(insert_type x; insert_type u)) tsubst; ct prf) | Psubst(prfs,tm,prf) -> (insert_term tm; ct prf; app ct prfs) | Pabs(prf,tm) -> (insert_term tm; ct prf) | Pdisch(prf,tm) -> (insert_term tm; ct prf) | Pmp(prf1,prf2) -> (ct prf1; ct prf2) | Poracle(_,tms,tm) -> (insert_term tm; app insert_term tms) | Pdef(_,_,tm) -> insert_term tm | Ptmspec(_,_,prf) -> ct prf | Ptydef(_,_,prf) -> ct prf | Ptyintro(_,_,_,_,pt,tt,prf) -> (insert_term pt; insert_term tt;ct prf) | Pspec(prf,tm) -> (insert_term tm; ct prf) | Pinst(prf,subst) -> (app (fun{redex=x;residue=u}->(insert_term x; insert_term u)) subst; ct prf) | Pgen(prf,tm) -> (insert_term tm; ct prf) | Pgenabs(prf,tm_opt,tms) -> (match tm_opt with Some tm -> insert_term tm | None -> (); app insert_term tms; ct prf) | Psym prf -> ct prf | Ptrans(prf1,prf2) -> (ct prf1; ct prf2) | Pcomb(prf1,prf2) -> (ct prf1; ct prf2) | Peqmp(prf1,prf2) -> (ct prf1; ct prf2) | Peqimp prf -> ct prf | Pexists(prf,ex,w) -> (insert_term ex; insert_term w; ct prf) | Pchoose(v,prf1,prf2) -> (insert_term v; ct prf1; ct prf2) | Pconj(prf1,prf2) -> (ct prf1; ct prf2) | Pconjunct1 prf -> ct prf | Pconjunct2 prf -> ct prf | Pdisj1(prf,tm) -> (insert_term tm; ct prf) | Pdisj2(prf,tm) -> (insert_term tm; ct prf) | Pdisjcases(prf1,prf2,prf3) -> (ct prf1; ct prf2; ct prf3) | Pnoti prf -> ct prf | Pnote prf -> ct prf | Pcontr(prf,tm) -> (insert_term tm; ct prf) | Prefl tm -> insert_term tm | Phyp tm -> insert_term tm | Pdisk -> () | Paxm (_,tm) -> insert_term tm | Pimpas (prf1,prf2) -> (ct prf1; ct prf2)) and ct prf = if will_be_shared prf then () else ct' prf in let _ = ct' prf in let _ = (match c_opt with Some c -> insert_term c | None -> ()) in let _ = yy_output_types out thyname types in let _ = mm_output_terms out thyname types terms in (types,terms);; let rec export_proof path thyname thmname p c_opt il = let outchannel = open_out (Filename.concat path (thmname^".prf")) in let out = output_string outchannel in let nout = encodeXMLEntitiesOut out in let _ = out "" in let (types,terms) = collect_types_terms thyname out p c_opt in let wti att tm = (out " "; out att; out "=\""; out (string_of_int (mm_lookup terms tm)); out "\"") in let wt tm = try (out "") with Not_found -> raise (Err("export_proof","Term not found!")) in let wty ty = try (out "") with Not_found -> raise (Err("export_proof","Type not found!")) in let wdi thy thm = (out "") in let write_proof p il = (let rec share_info_of p il = (match (disk_info_of p) with Some (thyname,thmname) -> Some(thyname,thmname,il) | None -> if do_share p then let name = get_iname() in set_disk_info_of p thyname name; Some(thyname,name,(name,p,None)::il) else None ) and dump str il prfs = (let _ = out (xml_start_tag str) in let res = rev_itlist (function p -> function il -> wp il p) prfs il in let _ = out (xml_end_tag str) in res) and wp' il = (function (Prefl tm) -> (out ""; il) | (Pinstt(p,lambda)) -> (let _ = out "" in let res = wp il p in let _ = app wty (explode_subst lambda) in let _ = out "" in res) | (Psubst(ps,t,p)) -> (let _ = (out "") in let il' = wp il p in let res = rev_itlist (function p -> function il -> wp il p) ps il' in let _ = out "" in res) | (Pabs(p,t)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | (Pdisch(p,tm)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | (Pmp(p1,p2)) -> dump "pmp" il [p1;p2] | (Phyp tm) -> (out ""; il) | (Paxm(name,tm)) -> (out ""; il) | (Pdef(seg,name,tm)) -> (out ""; il) | (Ptmspec(seg,names,p)) -> (let _ = (out "") in let res = wp il p in let _ = app (function s -> (out "")) names in let _ = out "" in res) | (Ptydef(seg,name,p)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | (Ptyintro(seg,name,abs,rep,pt,tt,p)) -> (let _ = (out "") in let _ = wt pt in let _ = wt tt in let res = wp il p in let _ = out "" in res) | (Poracle(tg,asl,c)) -> raise (Err("export_proof", "sorry, oracle export is not implemented!")) (* (out ""; app (function s -> (out "")) (Tag.oracles_of tg); wt c; app wt asl; out ""; il)*) | (Pspec(p,t)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | (Pinst(p,theta)) -> (let _ = out "" in let res = wp il p in let _ = app wt (explode_subst theta) in let _ = out "" in res) | (Pgen(p,x)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | (Pgenabs(p,opt,vl)) -> (let _ = out " wti "i" c | None -> ()) in let _ = out ">" in let res = wp il p in let _ = app wt vl in let _ = out "" in res) | (Psym p) -> dump "psym" il [p] | (Ptrans(p1,p2)) -> dump "ptrans" il [p1;p2] | (Pcomb(p1,p2)) -> dump "pcomb" il [p1;p2] | (Peqmp(p1,p2)) -> dump "peqmp" il [p1;p2] | (Peqimp p) -> dump "peqimp" il [p] | (Pexists(p,ex,w)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | (Pchoose(v,p1,p2)) -> (let _ = (out "") in let il' = wp il p1 in let res = wp il' p2 in let _ = out "" in res) | (Pconj(p1,p2)) -> dump "pconj" il [p1;p2] | (Pimpas(p1,p2)) -> dump "pimpas" il [p1;p2] | (Pconjunct1 p) -> dump "pconjunct1" il [p] | (Pconjunct2 p) -> dump "pconjunct2" il [p] | (Pdisj1(p,tm)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | (Pdisj2(p,tm)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | (Pdisjcases(p1,p2,p3)) -> dump "pdisjcases" il [p1;p2;p3] | (Pnoti p) -> dump "pnoti" il [p] | (Pnote p) -> dump "pnote" il [p] | (Pcontr(p,tm)) -> (let _ = (out "") in let res = wp il p in let _ = out "" in res) | Pdisk -> raise (Err("wp'","shouldn't try to write pdisk")) ) and wp il p = (let res = match (share_info_of p il) with Some(thyname',thmname,il') -> (wdi thyname' thmname; il') | None -> wp' il (content_of p) in res) in let res = (match disk_info_of p with Some(thyname',thmname') -> if thyname' = thyname & thmname' = thmname then wp' il (content_of p) else (wdi thyname' thmname'; il) | None -> wp' il (content_of p)) in res) in let il = write_proof p il in let _ = (match c_opt with Some c -> wt c | None -> ()) in let _ = (out "\n";(close_out outchannel)) in let _ = set_disk_info_of p thyname thmname in match il with [] -> () (* everything has been written *) | ((thmname',prf,c_opt)::rest) -> export_proof path thyname thmname' prf c_opt rest;; let export_proofs theory_name l' = let theory_name = match theory_name with None -> THEORY_NAME | Some n -> n in let path = ensure_export_directory theory_name in let ostrm = open_out (Filename.concat path "facts.lst") in let out = output_string ostrm in let _ = app (function (s,_,_) -> out (s^"\n")) l' in let _ = flush ostrm in let _ = (match l' with [] -> () | ((thmname,p,c_opt)::rest) -> export_proof path theory_name thmname p c_opt rest) in let num_int_thms = next_counter() - 1 in let _ = out ((string_of_int num_int_thms)^"\n");(close_out ostrm) in ();; let the_proof_database = ref ([]:(string*proof*(term option)) list);; exception Duplicate_proof_name;; let rec search_proof_name n db = match db with [] -> () | ((m, a, b)::db') -> if n=m then (raise Duplicate_proof_name) else search_proof_name n db' let save_proof name p c_opt = let _ = search_proof_name name (!the_proof_database) in (the_proof_database := (name, p, c_opt)::(!the_proof_database));; let proof_database () = !the_proof_database;; (* this is a little bit dangerous, because the function is not injective, but I guess one can live with that *) let make_filesystem_compatible s = let modify = function | "/" -> "_slash_" | "\\" -> "_backslash_" | "=" -> "_equal_" | ">" -> "_greaterthan_" | "<" -> "_lessthan_" | "?" -> "_questionmark_" | "!" -> "_exclamationmark_" | "*" -> "_star_" | s -> s in implode (map modify (explode s));; let export_saved_proofs thy = let context = rev (proof_database ()) in export_proofs thy (map (function (s,p,c) -> (make_filesystem_compatible s,p,c)) context);; end;; include Proofobjects;;