(* Title: Tools/nbe.ML Authors: Klaus Aehlig, LMU Muenchen; Tobias Nipkow, Florian Haftmann, TU Muenchen
Normalization by evaluation, based on generic code generator.
*)
signature NBE = sig val dynamic_conv: Proof.context -> conv val dynamic_value: Proof.context -> term -> term val static_conv: { ctxt: Proof.context, consts: stringlist }
-> Proof.context -> conv val static_value: { ctxt: Proof.context, consts: stringlist }
-> Proof.context -> term -> term
datatypeType = Typeofstring * Typelist
| TParam ofstring datatype Univ = Constof (int * Typelist) * Univ list(*named (uninterpreted) constants*)
| DFree ofstring * int (*free (uninterpreted) dictionary parameters*)
| BVar of int * Univ list(*bound variables, named*)
| Abs of (int * (Univ list -> Univ)) * Univ list val apps: Univ -> Univ list -> Univ (*explicit applications*) val abss: int -> (Univ list -> Univ) -> Univ (*abstractions as closures*) val same: Univ * Univ -> bool
val put_result: (unit -> (Typelist -> Univ) list -> (Typelist -> Univ) list)
-> Proof.context -> Proof.context val trace: bool Config.T
val add_const_alias: thm -> theory -> theory end;
structure Nbe: NBE = struct
(* generic non-sense *)
val trace = Attrib.setup_config_bool \<^binding>\<open>nbe_trace\<close> (K false); fun traced ctxt f x = if Config.get ctxt trace then (tracing (f x); x) else x;
(** certificates and oracle for "trivial type classes" **)
structure Triv_Class_Data = Theory_Data
( type T = (class * thm) list; val empty = []; fun merge data : T = AList.merge (op =) (K true) data;
);
fun add_const_alias thm thy = let val (ofclass, eqn) = casetry Logic.dest_equals (Thm.prop_of thm) of SOME ofclass_eq => ofclass_eq
| _ => error ("Bad certificate: " ^ Thm.string_of_thm_global thy thm); val (T, class) = casetry Logic.dest_of_class ofclass of SOME T_class => T_class
| _ => error ("Bad certificate: " ^ Thm.string_of_thm_global thy thm); val tvar = casetry Term.dest_TVar T of SOME (tvar as (_, sort)) => if null (filter (can (Axclass.get_info thy)) sort) then tvar else error ("Bad sort: " ^ Thm.string_of_thm_global thy thm)
| _ => error ("Bad type: " ^ Thm.string_of_thm_global thy thm); val _ = if Term.add_tvars eqn [] = [tvar] then () else error ("Inconsistent type: " ^ Thm.string_of_thm_global thy thm); val lhs_rhs = casetry Logic.dest_equals eqn of SOME lhs_rhs => lhs_rhs
| _ => error ("Not an equation: " ^ Syntax.string_of_term_global thy eqn); val c_c' = case try (apply2 (Axclass.unoverload_const thy o dest_Const)) lhs_rhs of SOME c_c' => c_c'
| _ => error ("Not an equation with two constants: "
^ Syntax.string_of_term_global thy eqn); val _ = if the_list (Axclass.class_of_param thy (snd c_c')) = [class] then () else error ("Inconsistent class: " ^ Thm.string_of_thm_global thy thm); in Triv_Class_Data.map (AList.update (op =) (class, Thm.trim_context thm)) thy end;
local
val get_triv_classes = map fst o Triv_Class_Data.get;
fun lift_triv_classes_conv orig_ctxt conv ct = let val thy = Proof_Context.theory_of orig_ctxt; val ctxt = Proof_Context.init_global thy; (*FIXME quasi-global context*) val algebra = Sign.classes_of thy; val triv_classes = get_triv_classes thy; fun additional_classes sort = filter_out (fn class => Sorts.sort_le algebra (sort, [class])) triv_classes; fun mk_entry (v, sort) = let val T = TFree (v, sort); val cT = Thm.ctyp_of ctxt T; val triv_sort = additional_classes sort; in
(v, (Sorts.inter_sort algebra (sort, triv_sort),
(cT, AList.make (fn class => Thm.of_class (cT, class)) sort
@ AList.make (fn class => triv_of_class (thy, T, class)) triv_sort))) end; val vs_tab = map mk_entry (Term.add_tfrees (Thm.term_of ct) []); fun instantiate thm = let val tvars =
Term.add_tvars (#1 (Logic.dest_equals (Logic.strip_imp_concl (Thm.prop_of thm)))) []; val instT = map2 (fn v => fn (_, (_, (cT, _))) => (v, cT)) tvars vs_tab; in Thm.instantiate (TVars.make instT, Vars.empty) thm end; fun of_class (TFree (v, _), class) =
the (AList.lookup (op =) ((snd o snd o the o AList.lookup (op =) vs_tab) v) class)
| of_class (T, _) = error ("Bad type " ^ Syntax.string_of_typ ctxt T); fun strip_of_class thm = let val prems_of_class = Thm.prop_of thm
|> Logic.strip_imp_prems
|> map (Logic.dest_of_class #> of_class); in fold Thm.elim_implies prems_of_class thm end; in
ct
|> Thm.term_of
|> (map_types o map_type_tfree)
(fn (v, _) => TFree (v, (fst o the o AList.lookup (op =) vs_tab) v))
|> Thm.cterm_of ctxt
|> conv ctxt
|> Thm.strip_shyps
|> Thm.varifyT_global
|> Thm.unconstrainT
|> instantiate
|> strip_of_class end;
fun lift_triv_classes_rew ctxt rew t = let val thy = Proof_Context.theory_of ctxt; val algebra = Sign.classes_of thy; val triv_classes = get_triv_classes thy; val vs = Term.add_tfrees t []; in
t
|> (map_types o map_type_tfree)
(fn (v, sort) => TFree (v, Sorts.inter_sort algebra (sort, triv_classes)))
|> rew
|> (map_types o map_type_tfree)
(fn (v, sort) => TFree (v, the_default sort (AList.lookup (op =) vs v))) end;
end;
(** the semantic universe **)
(* Functions are given by their semantic function value. To avoid trouble with the ML-type system, these functions have the most generic type, that is "Univ list -> Univ". The calling convention is that the arguments come as a list, the last argument first. In other words, a function call that usually would look like
f x_1 x_2 ... x_n or f(x_1,x_2, ..., x_n)
would be in our convention called as
f [x_n,..,x_2,x_1]
Moreover, to handle functions that are still waiting for some arguments we have additionally a list of arguments collected to far and the number of arguments we're still waiting for.
*)
fun same (Const ((k1, typargs1), us1), Const ((k2, typargs2), us2)) =
(k1 = k2) andalso eq_list same_type (typargs1, typargs2) andalso eq_list same (us1, us2)
| same (DFree (n1, i1), DFree (n2, i2)) = (n1 = n2) andalso (i1 = i2)
| same (BVar (i1, us1), BVar (i2, us2)) = (i1 = i2) andalso eq_list same (us1, us2)
| same _ = false;
(** assembling and compiling ML code from terms **)
(* abstract ML syntax *)
infix 9 `$` `$$`;
infix 8 `*`;
fun e1 `$` e2 = enclose "("")" (e1 ^ " " ^ e2); fun e `$$` [] = e
| e `$$` es = enclose "("")" (e ^ " " ^ implode_space es); fun ml_abs v e = enclose "("")" ("fn " ^ v ^ " => " ^ e);
fun ml_cases e cs = enclose "("")"
("case " ^ e ^ " of " ^ space_implode " | " (map (fn (p, e) => p ^ " => " ^ e) cs)); fun ml_let d e = "\n let\n" ^ prefix_lines " " d ^ "\n in " ^ e ^ " end";
fun ml_and [] = "true"
| ml_and [e] = e
| ml_and es = enclose "("")" (space_implode " andalso " es); fun ml_if b e1 e2 = enclose "("")" (implode_space ["if", b, "then", e1, "else", e2]);
fun e1 `*` e2 = enclose "("")" (e1 ^ ", " ^ e2); fun ml_list es = enclose "[""]" (commas es);
fun ml_exc s = enclose "("")" ("raise Fail " ^ quote s);
fun ml_fundefs ([(name, [([], e)])]) = "val " ^ name ^ " = " ^ e ^ ""
| ml_fundefs (eqs :: eqss) = let fun fundef (name, eqs) = let fun eqn (es, e) = name ^ " " ^ implode_space es ^ " = " ^ e in space_implode "\n | " (map eqn eqs) end; in
(prefix "fun " o fundef) eqs :: map (prefix "and " o fundef) eqss
|> cat_lines end;
(* nbe specific syntax and sandbox communication *)
structure Univs = Proof_Data
( type T = unit -> (Typelist -> Univ) list -> (Typelist -> Univ) list; val empty: T = fn () => raise Fail "Univs"; fun init _ = empty;
); val get_result = Univs.get; val put_result = Univs.put;
local val prefix = "Nbe."; val name_put = prefix ^ "put_result"; val name_const = prefix ^ "Const"; val name_type = prefix ^ "Type"; val name_abss = prefix ^ "abss"; val name_apps = prefix ^ "apps"; val name_same = prefix ^ "same"; in
val univs_cookie = (get_result, put_result, name_put);
(* Convention: parameters representing ("assembled") string representations of logical entities are prefixed with an "a_" -- unless they are an unqualified name ready to become part of an ML identifier.
*)
fun nbe_tparam v = "t_" ^ v; fun nbe_dict v n = "d_" ^ v ^ "_" ^ string_of_int n; fun nbe_global_param v = "w_" ^ v; fun nbe_bound v = "v_" ^ v; fun nbe_bound_optional NONE = "_"
| nbe_bound_optional (SOME v) = nbe_bound v; fun nbe_type a_sym a_tys = name_type `$` (quote a_sym `*` ml_list a_tys); fun nbe_fun a_sym a_typargs = a_sym `$` ml_list a_typargs;
(*note: these are the "turning spots" where proper argument order is established!*) fun nbe_apps a_u [] = a_u
| nbe_apps a_u a_us = name_apps `$$` [a_u, ml_list (rev a_us)]; fun nbe_apps_fun a_sym a_typargs a_us = nbe_fun a_sym a_typargs `$` ml_list (rev a_us); fun nbe_apps_fallback_fun a_sym a_us = a_sym `$$` a_us; fun nbe_apps_const a_sym a_typargs a_us = name_const `$` ((a_sym `*` ml_list a_typargs) `*` ml_list (rev a_us)); fun nbe_apps_constpat a_sym a_us = name_const `$` ((a_sym `*` "_") `*` ml_list (rev a_us));
fun nbe_abss 0 f = f `$` ml_list []
| nbe_abss n f = name_abss `$$` [string_of_int n, f];
fun retype_term ctxt t T = let val ctxt' =
ctxt
|> Variable.declare_typ T
|> Config.put Type_Infer.object_logic false
|> Config.put Type_Infer_Context.const_sorts false in
singleton (Variable.export_terms ctxt' ctxt') (Syntax.check_term ctxt' (Type.constraint T t)) end;
fun normalize_term (nbe_program, sym_tab) raw_ctxt t_original vs_ty_t deps = let val T = fastype_of t_original; val tfrees = Term.add_tfrees t_original []; val ctxt = Syntax.init_pretty_global (Proof_Context.theory_of raw_ctxt); val string_of_term =
Syntax.string_of_term
(ctxt
|> Config.put show_types true
|> Config.put show_sorts true); fun retype t' = retype_term ctxt t' T; fun check_tvars t' = if null (Term.add_tvars t' []) then t' else error ("Illegal schematic type variables in normalized term: " ^ string_of_term t'); in
Code_Preproc.timed "computing NBE expression" #ctxt compile_and_reconstruct_term
{ ctxt = ctxt, nbe_program = nbe_program, sym_tab = sym_tab, deps = deps,
tfrees = tfrees, vs_ty_t = vs_ty_t }
|> traced ctxt (fn t => "Normalized:\n" ^ string_of_term t)
|> retype
|> traced ctxt (fn t => "Types inferred:\n" ^ string_of_term t)
|> check_tvars
|> traced ctxt (fn _ => "---\n") end;
(* function store *)
structure Nbe_Functions = Code_Data
( type T = ((Typelist -> Univ) option * int) Code_Symbol.Graph.T * (int * Code_Symbol.T Inttab.table); val empty = (Code_Symbol.Graph.empty, (0, Inttab.empty));
);
fun compile ignore_cache ctxt program = let val (nbe_program, (_, sym_tab)) =
Nbe_Functions.change (if ignore_cache then NONE else SOME (Proof_Context.theory_of ctxt))
(Code_Preproc.timed "compiling NBE program" #ctxt
compile_program { ctxt = ctxt, program = program }); in (nbe_program, sym_tab) end;
(* evaluation oracle *)
fun mk_equals ctxt lhs raw_rhs = let val ty = Thm.typ_of_cterm lhs; val eq = Thm.cterm_of ctxt \<^Const>\<open>Pure.eq ty\<close>; val rhs = Thm.cterm_of ctxt raw_rhs; in Thm.mk_binop eq lhs rhs end;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.