summaryrefslogtreecommitdiff
path: root/theory.ml
diff options
context:
space:
mode:
Diffstat (limited to 'theory.ml')
-rw-r--r--theory.ml1097
1 files changed, 1097 insertions, 0 deletions
diff --git a/theory.ml b/theory.ml
new file mode 100644
index 0000000..3fa35bf
--- /dev/null
+++ b/theory.ml
@@ -0,0 +1,1097 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** Constr from the theory of this particular development
+
+ The inner-working of this module is highly correlated with the
+ particular structure of {b AAC.v}, therefore, it should
+ be of little interest to most readers.
+*)
+open Term
+
+module Control = struct
+ let printing = true
+ let debug = false
+ let time = false
+end
+
+module Debug = Helper.Debug (Control)
+open Debug
+
+(** {1 HMap : Specialized Hashtables based on constr} *)
+
+
+module Hashed_constr =
+struct
+ type t = constr
+ let equal = (=)
+ let hash = Hashtbl.hash
+end
+
+ (* TODO module HMap = Hashtbl, du coup ? *)
+module HMap = Hashtbl.Make(Hashed_constr)
+
+let ac_theory_path = ["AAC_tactics"; "AAC"]
+
+module Stubs = struct
+ let path = ac_theory_path@["Internal"]
+
+ (** The constants from the inductive type *)
+ let _Tty = lazy (Coq.init_constant path "T")
+ let vTty = lazy (Coq.init_constant path "vT")
+
+ let rsum = lazy (Coq.init_constant path "sum")
+ let rprd = lazy (Coq.init_constant path "prd")
+ let rsym = lazy (Coq.init_constant path "sym")
+ let runit = lazy (Coq.init_constant path "unit")
+
+ let vnil = lazy (Coq.init_constant path "vnil")
+ let vcons = lazy (Coq.init_constant path "vcons")
+ let eval = lazy (Coq.init_constant path "eval")
+
+
+ let decide_thm = lazy (Coq.init_constant path "decide")
+ let lift_normalise_thm = lazy (Coq.init_constant path "lift_normalise")
+
+ let lift =
+ lazy (Coq.init_constant ac_theory_path "AAC_lift")
+ let lift_proj_equivalence=
+ lazy (Coq.init_constant ac_theory_path "aac_lift_equivalence")
+ let lift_transitivity_left =
+ lazy(Coq.init_constant ac_theory_path "lift_transitivity_left")
+ let lift_transitivity_right =
+ lazy(Coq.init_constant ac_theory_path "lift_transitivity_right")
+ let lift_reflexivity =
+ lazy(Coq.init_constant ac_theory_path "lift_reflexivity")
+end
+
+module Classes = struct
+ module Associative = struct
+ let path = ac_theory_path
+ let typ = lazy (Coq.init_constant path "Associative")
+ let ty (rlt : Coq.Relation.t) (value : Term.constr) =
+ mkApp (Lazy.force typ, [| rlt.Coq.Relation.carrier;
+ rlt.Coq.Relation.r;
+ value
+ |] )
+ let infer goal rlt value =
+ let ty = ty rlt value in
+ Coq.resolve_one_typeclass goal ty
+ end
+
+ module Commutative = struct
+ let path = ac_theory_path
+ let typ = lazy (Coq.init_constant path "Commutative")
+ let ty (rlt : Coq.Relation.t) (value : Term.constr) =
+ mkApp (Lazy.force typ, [| rlt.Coq.Relation.carrier;
+ rlt.Coq.Relation.r;
+ value
+ |] )
+
+ end
+
+ module Proper = struct
+ let path = ac_theory_path @ ["Internal";"Sym"]
+ let typeof = lazy (Coq.init_constant path "type_of")
+ let relof = lazy (Coq.init_constant path "rel_of")
+ let mk_typeof : Coq.Relation.t -> int -> constr = fun rlt n ->
+ let x = rlt.Coq.Relation.carrier in
+ mkApp (Lazy.force typeof, [| x ; Coq.Nat.of_int n |])
+ let mk_relof : Coq.Relation.t -> int -> constr = fun rlt n ->
+ let (x,r) = Coq.Relation.split rlt in
+ mkApp (Lazy.force relof, [| x;r ; Coq.Nat.of_int n |])
+
+ let ty rlt op ar =
+ let typeof = mk_typeof rlt ar in
+ let relof = mk_relof rlt ar in
+ Coq.Classes.mk_morphism typeof relof op
+ let infer goal rlt op ar =
+ let ty = ty rlt op ar in
+ Coq.resolve_one_typeclass goal ty
+ end
+
+ module Unit = struct
+ let path = ac_theory_path
+ let typ = lazy (Coq.init_constant path "Unit")
+ let ty (rlt : Coq.Relation.t) (value : Term.constr) (unit : Term.constr)=
+ mkApp (Lazy.force typ, [| rlt.Coq.Relation.carrier;
+ rlt.Coq.Relation.r;
+ value;
+ unit
+ |] )
+ end
+
+end
+
+(* Non empty lists *)
+module NEList = struct
+ let path = ac_theory_path @ ["Internal"]
+ let typ = lazy (Coq.init_constant path "list")
+ let nil = lazy (Coq.init_constant path "nil")
+ let cons = lazy (Coq.init_constant path "cons")
+ let cons ty h t =
+ mkApp (Lazy.force cons, [| ty; h ; t |])
+ let nil ty x =
+ (mkApp (Lazy.force nil, [| ty ; x|]))
+ let rec of_list ty = function
+ | [] -> invalid_arg "NELIST"
+ | [x] -> nil ty x
+ | t::q -> cons ty t (of_list ty q)
+
+ let type_of_list ty =
+ mkApp (Lazy.force typ, [|ty|])
+end
+
+(** a [mset] is a ('a * pos) list *)
+let mk_mset ty (l : (constr * int) list) =
+ let pos = Lazy.force Coq.Pos.typ in
+ let pair (x : constr) (ar : int) =
+ Coq.Pair.of_pair ty pos (x, Coq.Pos.of_int ar)
+ in
+ let pair_ty = Coq.lapp Coq.Pair.typ [| ty ; pos|] in
+ let rec aux = function
+ | [ ] -> assert false
+ | [x,ar] -> NEList.nil pair_ty (pair x ar)
+ | (t,ar)::q -> NEList.cons pair_ty (pair t ar) (aux q)
+ in
+ aux l
+
+module Sigma = struct
+ let sigma = lazy (Coq.init_constant ac_theory_path "sigma")
+ let sigma_empty = lazy (Coq.init_constant ac_theory_path "sigma_empty")
+ let sigma_add = lazy (Coq.init_constant ac_theory_path "sigma_add")
+ let sigma_get = lazy (Coq.init_constant ac_theory_path "sigma_get")
+
+ let add ty n x map =
+ mkApp (Lazy.force sigma_add,[|ty; n; x ; map|])
+ let empty ty =
+ mkApp (Lazy.force sigma_empty,[|ty |])
+ let typ ty =
+ mkApp (Lazy.force sigma, [|ty|])
+
+ let to_fun ty null map =
+ mkApp (Lazy.force sigma_get, [|ty;null;map|])
+
+ let of_list ty null l =
+ match l with
+ | (_,t)::q ->
+ let map =
+ List.fold_left
+ (fun acc (i,t) ->
+ assert (i > 0);
+ add ty (Coq.Pos.of_int i) ( t) acc)
+ (empty ty)
+ q
+ in to_fun ty (t) map
+ | [] -> debug "of_list empty" ; to_fun ty (null) (empty ty)
+
+
+end
+
+
+module Sym = struct
+ type pack = {ar: Term.constr; value: Term.constr ; morph: Term.constr}
+ let path = ac_theory_path @ ["Internal";"Sym"]
+ let typ = lazy (Coq.init_constant path "pack")
+ let mkPack = lazy (Coq.init_constant path "mkPack")
+ let value = lazy (Coq.init_constant path "value")
+ let null = lazy (Coq.init_constant path "null")
+ let mk_pack (rlt: Coq.Relation.t) s =
+ let (x,r) = Coq.Relation.split rlt in
+ mkApp (Lazy.force mkPack, [|x;r; s.ar;s.value;s.morph|])
+ let null rlt =
+ let x = rlt.Coq.Relation.carrier in
+ let r = rlt.Coq.Relation.r in
+ mkApp (Lazy.force null, [| x;r;|])
+
+ let mk_ty : Coq.Relation.t -> constr = fun rlt ->
+ let (x,r) = Coq.Relation.split rlt in
+ mkApp (Lazy.force typ, [| x; r|] )
+end
+
+module Bin =struct
+ type pack = {value : Term.constr;
+ compat : Term.constr;
+ assoc : Term.constr;
+ comm : Term.constr option;
+ }
+
+ let path = ac_theory_path @ ["Internal";"Bin"]
+ let typ = lazy (Coq.init_constant path "pack")
+ let mkPack = lazy (Coq.init_constant path "mk_pack")
+
+ let mk_pack: Coq.Relation.t -> pack -> Term.constr = fun (rlt) s ->
+ let (x,r) = Coq.Relation.split rlt in
+ let comm_ty = Classes.Commutative.ty rlt s.value in
+ mkApp (Lazy.force mkPack , [| x ; r;
+ s.value;
+ s.compat ;
+ s.assoc;
+ Coq.Option.of_option comm_ty s.comm
+ |])
+ let mk_ty : Coq.Relation.t -> constr = fun rlt ->
+ let (x,r) = Coq.Relation.split rlt in
+ mkApp (Lazy.force typ, [| x; r|] )
+end
+
+module Unit = struct
+ let path = ac_theory_path @ ["Internal"]
+ let unit_of_ty = lazy (Coq.init_constant path "unit_of")
+ let unit_pack_ty = lazy (Coq.init_constant path "unit_pack")
+ let mk_unit_of = lazy (Coq.init_constant path "mk_unit_for")
+ let mk_unit_pack = lazy (Coq.init_constant path "mk_unit_pack")
+
+ type unit_of =
+ {
+ uf_u : Term.constr; (* u *)
+ uf_idx : Term.constr;
+ uf_desc : Term.constr; (* Unit R (e_bin uf_idx) u *)
+ }
+
+ type pack = {
+ u_value : Term.constr; (* X *)
+ u_desc : (unit_of) list (* unit_of u_value *)
+ }
+
+ let ty_unit_of rlt e_bin u =
+ let (x,r) = Coq.Relation.split rlt in
+ mkApp ( Lazy.force unit_of_ty, [| x; r; e_bin; u |])
+
+ let ty_unit_pack rlt e_bin =
+ let (x,r) = Coq.Relation.split rlt in
+ mkApp (Lazy.force unit_pack_ty, [| x; r; e_bin |])
+
+ let mk_unit_of rlt e_bin u unit_of =
+ let (x,r) = Coq.Relation.split rlt in
+ mkApp (Lazy.force mk_unit_of , [| x;
+ r;
+ e_bin ;
+ u;
+ unit_of.uf_idx;
+ unit_of.uf_desc
+ |])
+
+ let mk_pack rlt e_bin pack : Term.constr =
+ let (x,r) = Coq.Relation.split rlt in
+ let ty = ty_unit_of rlt e_bin pack.u_value in
+ let mk_unit_of = mk_unit_of rlt e_bin pack.u_value in
+ let u_desc =Coq.List.of_list ( ty ) (List.map mk_unit_of pack.u_desc) in
+ mkApp (Lazy.force mk_unit_pack, [|x;r;
+ e_bin ;
+ pack.u_value;
+ u_desc
+ |])
+
+ let default x : pack =
+ { u_value = x;
+ u_desc = []
+ }
+
+end
+
+let anomaly msg =
+ Util.anomaly ("aac_tactics: " ^ msg)
+
+let user_error msg =
+ Util.error ("aac_tactics: " ^ msg)
+
+module Trans = struct
+
+ (** {1 From Coq to Abstract Syntax Trees (AST)}
+
+ This module provides facilities to interpret a Coq term with
+ arbitrary operators as an abstract syntax tree. Considering an
+ application, we try to infer instances of our classes.
+
+ We consider that [A] operators are coarser than [AC] operators,
+ which in turn are coarser than raw morphisms. That means that
+ [List.append], of type [(A : Type) -> list A -> list A -> list
+ A] can be understood as an [A] operator.
+
+ During this reification, we gather some informations that will
+ be used to rebuild Coq terms from AST ( type {!envs})
+
+ We use a main hash-table from [constr] to [pack], in order to
+ discriminate the various constructors. All these are mixed in
+ order to improve on the number of comparisons in the tables *)
+
+ (* 'a * (unit, op_unit) option *)
+ type 'a with_unit = 'a * (Unit.unit_of) option
+ type pack =
+ (* used to infer the AC/A structure in the first pass {!Gather} *)
+ | Bin of Bin.pack with_unit
+ (* will only be used in the second pass : {!Parse}*)
+ | Sym of Sym.pack
+ | Unit of constr (* to avoid confusion in bloom *)
+
+ (** discr is a map from {!Term.constr} to {!pack}.
+
+ [None] means that it is not possible to instantiate this partial
+ application to an interesting class.
+
+ [Some x] means that we found something in the past. This means
+ in particular that a single [constr] cannot be two things at the
+ same time.
+
+ The field [bloom] allows to give a unique number to each class we
+ found. *)
+ type envs =
+ {
+ discr : (pack option) HMap.t ;
+ bloom : (pack, int ) Hashtbl.t;
+ bloom_back : (int, pack ) Hashtbl.t;
+ bloom_next : int ref;
+ }
+
+ let empty_envs () =
+ {
+ discr = HMap.create 17;
+ bloom = Hashtbl.create 17;
+ bloom_back = Hashtbl.create 17;
+ bloom_next = ref 1;
+ }
+
+
+
+ let add_bloom envs pack =
+ if Hashtbl.mem envs.bloom pack
+ then ()
+ else
+ let x = ! (envs.bloom_next) in
+ Hashtbl.add envs.bloom pack x;
+ Hashtbl.add envs.bloom_back x pack;
+ incr (envs.bloom_next)
+
+ let find_bloom envs pack =
+ try Hashtbl.find envs.bloom pack
+ with Not_found -> assert false
+
+ (********************************************)
+ (* (\* Gather the occuring AC/A symbols *\) *)
+ (********************************************)
+
+ (** This modules exhibit a function that memoize in the environment
+ all the AC/A operators as well as the morphism that occur. This
+ staging process allows us to prefer AC/A operators over raw
+ morphisms. Moreover, for each AC/A operators, we need to try to
+ infer units. Otherwise, we do not have [x * y * x <= a * a] since 1
+ does not occur.
+
+ But, do we also need to check whether constants are
+ units. Otherwise, we do not have the ability to rewrite [0 = a +
+ a] in [a = ...]*)
+ module Gather : sig
+ val gather : Coq.goal_sigma -> Coq.Relation.t -> envs -> Term.constr -> Coq.goal_sigma
+ end
+ = struct
+
+ let memoize envs t pack : unit =
+ begin
+ HMap.add envs.discr t (Some pack);
+ add_bloom envs pack;
+ match pack with
+ | Bin (_, None) | Sym _ -> ()
+ | Bin (_, Some (unit_of)) ->
+ let unit = unit_of.Unit.uf_u in
+ HMap.add envs.discr unit (Some (Unit unit));
+ add_bloom envs (Unit unit);
+ | Unit _ -> assert false
+ end
+
+
+ let get_unit (rlt : Coq.Relation.t) op goal :
+ (Coq.goal_sigma * constr * constr ) option=
+ let x = (rlt.Coq.Relation.carrier) in
+ let unit, goal = Coq.evar_unit goal x in
+ let ty =Classes.Unit.ty rlt op unit in
+ let result =
+ try
+ let t,goal = Coq.resolve_one_typeclass goal ty in
+ Some (goal,t,unit)
+ with Not_found -> None
+ in
+ match result with
+ | None -> None
+ | Some (goal,s,unit) ->
+ let unit = Coq.nf_evar goal unit in
+ Some (goal, unit, s)
+
+
+
+ (** gives back the class and the operator *)
+ let is_bin (rlt: Coq.Relation.t) (op: constr) ( goal: Coq.goal_sigma)
+ : (Coq.goal_sigma * Bin.pack) option =
+ let assoc_ty = Classes.Associative.ty rlt op in
+ let comm_ty = Classes.Commutative.ty rlt op in
+ let proper_ty = Classes.Proper.ty rlt op 2 in
+ try
+ let proper , goal = Coq.resolve_one_typeclass goal proper_ty in
+ let assoc, goal = Coq.resolve_one_typeclass goal assoc_ty in
+ let comm , goal =
+ try
+ let comm, goal = Coq.resolve_one_typeclass goal comm_ty in
+ Some comm, goal
+ with Not_found ->
+ None, goal
+ in
+ let bin =
+ {Bin.value = op;
+ Bin.compat = proper;
+ Bin.assoc = assoc;
+ Bin.comm = comm }
+ in
+ Some (goal,bin)
+ with |Not_found -> None
+
+ let is_bin (rlt : Coq.Relation.t) (op : constr) (goal : Coq.goal_sigma)=
+ match is_bin rlt op goal with
+ | None -> None
+ | Some (goal, bin_pack) ->
+ match get_unit rlt op goal with
+ | None -> Some (goal, Bin (bin_pack, None))
+ | Some (gl, unit,s) ->
+ let unit_of =
+ {
+ Unit.uf_u = unit;
+ (* TRICK : this term is not well-typed by itself,
+ but it is okay the way we use it in the other
+ functions *)
+ Unit.uf_idx = op;
+ Unit.uf_desc = s;
+ }
+ in Some (gl,Bin (bin_pack, Some (unit_of)))
+
+
+ (** {is_morphism} try to infer the kind of operator we are
+ dealing with *)
+ let is_morphism goal (rlt : Coq.Relation.t) (papp : Term.constr) (ar : int) : (Coq.goal_sigma * pack ) option =
+ let typeof = Classes.Proper.mk_typeof rlt ar in
+ let relof = Classes.Proper.mk_relof rlt ar in
+ let m = Coq.Classes.mk_morphism typeof relof papp in
+ try
+ let m,goal = Coq.resolve_one_typeclass goal m in
+ let pack = {Sym.ar = (Coq.Nat.of_int ar); Sym.value= papp; Sym.morph= m} in
+ Some (goal, Sym pack)
+ with
+ | Not_found -> None
+
+
+ (* [crop_app f [| a_0 ; ... ; a_n |]]
+ returns Some (f a_0 ... a_(n-2), [|a_(n-1); a_n |] )
+ *)
+ let crop_app t ca : (constr * constr array) option=
+ let n = Array.length ca in
+ if n <= 1
+ then None
+ else
+ let papp = Term.mkApp (t, Array.sub ca 0 (n-2) ) in
+ let args = Array.sub ca (n-2) 2 in
+ Some (papp, args )
+
+ let fold goal (rlt : Coq.Relation.t) envs t ca cont =
+ let fold_morphism t ca =
+ let nb_params = Array.length ca in
+ let rec aux n =
+ assert (n < nb_params && 0 < nb_params );
+ let papp = Term.mkApp (t, Array.sub ca 0 n) in
+ match is_morphism goal rlt papp (nb_params - n) with
+ | None ->
+ (* here we have to memoize the failures *)
+ HMap.add envs.discr papp None;
+ if n < nb_params - 1 then aux (n+1) else goal
+ | Some (goal, pack) ->
+ let args = Array.sub ca (n) (nb_params -n)in
+ let goal = Array.fold_left cont goal args in
+ memoize envs papp pack;
+ goal
+ in
+ if nb_params = 0 then goal else aux 0
+ in
+ let is_aac t = is_bin rlt t in
+ match crop_app t ca with
+ | None ->
+ fold_morphism t ca
+ | Some (papp, args) ->
+ begin match is_aac papp goal with
+ | None -> fold_morphism t ca
+ | Some (goal, pack) ->
+ memoize envs papp pack;
+ Array.fold_left cont goal args
+ end
+
+ (* update in place the envs of known stuff, using memoization. We
+ have to memoize failures, here. *)
+ let gather goal (rlt : Coq.Relation.t ) envs t : Coq.goal_sigma =
+ let rec aux goal x =
+ match Coq.decomp_term x with
+ | App (t,ca) ->
+ fold goal rlt envs t ca (aux )
+ | _ -> goal
+ in
+ aux goal t
+ end
+
+ (** We build a term out of a constr, updating in place the
+ environment if needed (the only kind of such updates are the
+ constants). *)
+ module Parse :
+ sig
+ val t_of_constr : Coq.goal_sigma -> Coq.Relation.t -> envs -> constr -> Matcher.Terms.t * Coq.goal_sigma
+ end
+ = struct
+
+ (** [discriminates goal envs rlt t ca] infer :
+
+ - its {! pack } (is it an AC operator, or an A operator, or a
+ Symbol ?),
+
+ - the relevant partial application,
+
+ - the vector of the remaining arguments
+
+ We use an expansion to handle the special case of units,
+ before going back to the general discrimination
+ procedure. That means that a unit is more coarse than a raw
+ morphism
+
+ This functions is prevented to go through [ar < 0] by the fact
+ that a constant is a morphism. But not an eva. *)
+
+ let is_morphism goal (rlt : Coq.Relation.t) (papp : Term.constr) (ar : int) : (Coq.goal_sigma * pack ) option =
+ let typeof = Classes.Proper.mk_typeof rlt ar in
+ let relof = Classes.Proper.mk_relof rlt ar in
+ let m = Coq.Classes.mk_morphism typeof relof papp in
+ try
+ let m,goal = Coq.resolve_one_typeclass goal m in
+ let pack = {Sym.ar = (Coq.Nat.of_int ar); Sym.value= papp; Sym.morph= m} in
+ Some (goal, Sym pack)
+ with
+ | e -> None
+
+ exception NotReflexive
+ let discriminate goal envs (rlt : Coq.Relation.t) t ca : Coq.goal_sigma * pack * constr * constr array =
+ let nb_params = Array.length ca in
+ let rec fold goal ar :Coq.goal_sigma * pack * constr * constr array =
+ begin
+ assert (0 <= ar && ar <= nb_params);
+ let p_app = mkApp (t, Array.sub ca 0 (nb_params - ar)) in
+ begin
+ try
+ begin match HMap.find envs.discr p_app with
+ | None ->
+ fold goal (ar-1)
+ | Some pack ->
+ (goal, pack, p_app, Array.sub ca (nb_params -ar ) ar)
+ end
+ with
+ Not_found -> (* Could not find this constr *)
+ memoize (is_morphism goal rlt p_app ar) p_app ar
+ end
+ end
+ and memoize (x) p_app ar =
+ assert (0 <= ar && ar <= nb_params);
+ match x with
+ | Some (goal, pack) ->
+ HMap.add envs.discr p_app (Some pack);
+ add_bloom envs pack;
+ (goal, pack, p_app, Array.sub ca (nb_params-ar) ar)
+ | None ->
+
+ if ar = 0 then raise NotReflexive;
+ begin
+ (* to memoise the failures *)
+ HMap.add envs.discr p_app None;
+ (* will terminate, since [const] is capped, and it is
+ easy to find an instance of a constant *)
+ fold goal (ar-1)
+ end
+ in
+ try match HMap.find envs.discr (mkApp (t,ca))with
+ | None -> fold goal (nb_params)
+ | Some pack -> goal, pack, (mkApp (t,ca)), [| |]
+ with Not_found -> fold goal (nb_params)
+
+ let discriminate goal envs rlt x =
+ try
+ match Coq.decomp_term x with
+ | App (t,ca) ->
+ discriminate goal envs rlt t ca
+ | _ -> discriminate goal envs rlt x [| |]
+ with
+ | NotReflexive -> user_error "The relation to which the goal was lifted is not Reflexive"
+ (* TODO: is it the only source of invalid use that fall
+ into this catch_all ? *)
+ | e ->
+ user_error "Cannot handle this kind of hypotheses (variables occuring under a function symbol which is not a proper morphism)."
+
+ (** [t_of_constr goal rlt envs cstr] builds the abstract syntax tree
+ of the term [cstr]. Doing so, it modifies the environment of
+ known stuff [envs], and eventually creates fresh
+ evars. Therefore, we give back the goal updated accordingly *)
+ let t_of_constr goal (rlt: Coq.Relation.t ) envs : constr -> Matcher.Terms.t * Coq.goal_sigma =
+ let r_goal = ref (goal) in
+ let rec aux x =
+ match Coq.decomp_term x with
+ | Rel i -> Matcher.Terms.Var i
+ | _ ->
+ let goal, pack , p_app, ca = discriminate (!r_goal) envs rlt x in
+ r_goal := goal;
+ let k = find_bloom envs pack
+ in
+ match pack with
+ | Bin (pack,_) ->
+ begin match pack.Bin.comm with
+ | Some _ ->
+ assert (Array.length ca = 2);
+ Matcher.Terms.Plus ( k, aux ca.(0), aux ca.(1))
+ | None ->
+ assert (Array.length ca = 2);
+ Matcher.Terms.Dot ( k, aux ca.(0), aux ca.(1))
+ end
+ | Unit _ ->
+ assert (Array.length ca = 0);
+ Matcher.Terms.Unit ( k)
+ | Sym _ ->
+ Matcher.Terms.Sym ( k, Array.map aux ca)
+ in
+ (
+ fun x -> let r = aux x in r, !r_goal
+ )
+
+ end (* Parse *)
+
+ let add_symbol goal rlt envs l =
+ let goal = Gather.gather goal rlt envs (Term.mkApp (l, [| Term.mkRel 0;Term.mkRel 0|])) in
+ goal
+
+ (* [t_of_constr] buils a the abstract syntax tree of a constr,
+ updating in place the environment. Doing so, we infer all the
+ morphisms and the AC/A operators. It is mandatory to do so both
+ for the pattern and the term, since AC symbols can occur in one
+ and not the other *)
+ let t_of_constr goal rlt envs (l,r) =
+ let goal = Gather.gather goal rlt envs l in
+ let goal = Gather.gather goal rlt envs r in
+ let l,goal = Parse.t_of_constr goal rlt envs l in
+ let r, goal = Parse.t_of_constr goal rlt envs r in
+ l, r, goal
+
+ (* An intermediate representation of the environment, with association lists for AC/A operators, and also the raw [packer] information *)
+
+ type ir =
+ {
+ packer : (int, pack) Hashtbl.t; (* = bloom_back *)
+ bin : (int * Bin.pack) list ;
+ units : (int * Unit.pack) list;
+ sym : (int * Term.constr) list ;
+ matcher_units : Matcher.ext_units
+ }
+
+ let ir_to_units ir = ir.matcher_units
+
+ let ir_of_envs goal (rlt : Coq.Relation.t) envs =
+ let add x y l = (x,y)::l in
+ let nil = [] in
+ let sym ,
+ (bin : (int * Bin.pack with_unit) list),
+ (units : (int * constr) list) =
+ Hashtbl.fold
+ (fun int pack (sym,bin,units) ->
+ match pack with
+ | Bin s ->
+ sym, add (int) s bin, units
+ | Sym s ->
+ add (int) s sym, bin, units
+ | Unit s ->
+ sym, bin, add int s units
+ )
+ envs.bloom_back
+ (nil,nil,nil)
+ in
+ let matcher_units =
+ let unit_for , is_ac =
+ List.fold_left
+ (fun ((unit_for,is_ac) as acc) (n,(bp,wu)) ->
+ match wu with
+ | None -> acc
+ | Some (unit_of) ->
+ let unit_n = Hashtbl.find envs.bloom
+ (Unit unit_of.Unit.uf_u)
+ in
+ ( n, unit_n)::unit_for,
+ (n, bp.Bin.comm <> None )::is_ac
+
+ )
+ ([],[]) bin
+ in
+ {Matcher.unit_for = unit_for; Matcher.is_ac = is_ac}
+
+ in
+ let units : (int * Unit.pack) list =
+ List.fold_left
+ (fun acc (n,u) ->
+ (* first, gather all bins with this unit *)
+ let all_bin =
+ List.fold_left
+ ( fun acc (nop,(bp,wu)) ->
+ match wu with
+ | None -> acc
+ | Some unit_of ->
+ if unit_of.Unit.uf_u = u
+ then
+ {unit_of with
+ Unit.uf_idx = (Coq.Pos.of_int nop)} :: acc
+ else
+ acc
+ )
+ [] bin
+ in
+ (n,{
+ Unit.u_value = u;
+ Unit.u_desc = all_bin
+ })::acc
+ )
+ [] units
+
+ in
+ goal, {
+ packer = envs.bloom_back;
+ bin = (List.map (fun (n,(s,_)) -> n, s) bin);
+ units = units;
+ sym = (List.map (fun (n,s) -> n,(Sym.mk_pack rlt s)) sym);
+ matcher_units = matcher_units
+ }
+
+
+
+ (** {1 From AST back to Coq }
+
+ The next functions allow one to map OCaml abstract syntax trees
+ to Coq terms *)
+
+ (** {2 Building raw, natural, terms} *)
+
+ (** [raw_constr_of_t_debruijn] rebuilds a term in the raw
+ representation, without products on top, and maybe escaping free
+ debruijn indices (in the case of a pattern for example). *)
+ let raw_constr_of_t_debruijn ir (t : Matcher.Terms.t) : Term.constr * int list =
+ let add_set,get =
+ let r = ref [] in
+ let rec add x = function
+ [ ] -> [x]
+ | t::q when t = x -> t::q
+ | t::q -> t:: (add x q)
+ in
+ (fun x -> r := add x !r),(fun () -> !r)
+ in
+ (* Here, we rely on the invariant that the maps are well formed:
+ it is meanigless to fail to find a symbol in the maps, or to
+ find the wrong kind of pack in the maps *)
+ let rec aux t =
+ match t with
+ | Matcher.Terms.Plus (s,l,r) ->
+ begin match Hashtbl.find ir.packer s with
+ | Bin (s,_) ->
+ mkApp (s.Bin.value , [|(aux l);(aux r)|])
+ | _ -> Printf.printf "erreur:%i\n%!"s;
+ assert false
+ end
+ | Matcher.Terms.Dot (s,l,r) ->
+ begin match Hashtbl.find ir.packer s with
+ | Bin (s,_) ->
+ mkApp (s.Bin.value, [|(aux l);(aux r)|])
+ | _ -> assert false
+ end
+ | Matcher.Terms.Sym (s,t) ->
+ begin match Hashtbl.find ir.packer s with
+ | Sym s ->
+ mkApp (s.Sym.value, Array.map aux t)
+ | _ -> assert false
+ end
+ | Matcher.Terms.Unit x ->
+ begin match Hashtbl.find ir.packer x with
+ | Unit s -> s
+ | _ -> assert false
+ end
+ | Matcher.Terms.Var i -> add_set i;
+ mkRel (i)
+ in
+ let t = aux t in
+ t , get ( )
+
+ (** [raw_constr_of_t] rebuilds a term in the raw representation *)
+ let raw_constr_of_t ir rlt (context:rel_context) t =
+ (** cap rebuilds the products in front of the constr *)
+ let rec cap c = function [] -> c
+ | t::q ->
+ let i = Term.lookup_rel t context in
+ cap (mkProd_or_LetIn i c) q
+ in
+ let t,indices = raw_constr_of_t_debruijn ir t in
+ cap t (List.sort (Pervasives.compare) indices)
+
+
+ (** {2 Building reified terms} *)
+
+ (* Some informations to be able to build the maps *)
+ type reif_params =
+ {
+ bin_null : Bin.pack; (* the default A operator *)
+ sym_null : constr;
+ unit_null : Unit.pack;
+ sym_ty : constr; (* the type, as it appears in e_sym *)
+ bin_ty : constr
+ }
+
+
+ (** A record containing the environments that will be needed by the
+ decision procedure, as a Coq constr. Contains the functions
+ from the symbols (as ints) to indexes (as constr) *)
+
+ type sigmas = {
+ env_sym : Term.constr;
+ env_bin : Term.constr;
+ env_units : Term.constr; (* the [idx -> X:constr] *)
+ }
+
+
+ type sigma_maps = {
+ sym_to_pos : int -> Term.constr;
+ bin_to_pos : int -> Term.constr;
+ units_to_pos : int -> Term.constr;
+ }
+
+
+ (** infers some stuff that will be required when we will build
+ environments (our environments need a default case, so we need
+ an Op_AC, an Op_A, and a symbol) *)
+ (* Note : this function can fail if the user is using the wrong
+ relation, like proving a = b, while the classes are defined with
+ another relation (==) *)
+ let build_reif_params goal (rlt : Coq.Relation.t) (zero) :
+ reif_params * Coq.goal_sigma =
+ let carrier = rlt.Coq.Relation.carrier in
+ let bin_null =
+ try
+ let op,goal = Coq.evar_binary goal carrier in
+ let assoc,goal = Classes.Associative.infer goal rlt op in
+ let compat,goal = Classes.Proper.infer goal rlt op 2 in
+ let op = Coq.nf_evar goal op in
+ {
+ Bin.value = op;
+ Bin.compat = compat;
+ Bin.assoc = assoc;
+ Bin.comm = None
+ }
+ with Not_found -> user_error "Cannot infer a default A operator (required at least to be Proper and Associative)"
+ in
+ let zero, goal =
+ try
+ let evar_op,goal = Coq.evar_binary goal carrier in
+ let evar_unit, goal = Coq.evar_unit goal carrier in
+ let query = Classes.Unit.ty rlt evar_op evar_unit in
+ let _, goal = Coq.resolve_one_typeclass goal query in
+ Coq.nf_evar goal evar_unit, goal
+ with _ -> zero, goal in
+ let sym_null = Sym.null rlt in
+ let unit_null = Unit.default zero in
+ let record =
+ {
+ bin_null = bin_null;
+ sym_null = sym_null;
+ unit_null = unit_null;
+ sym_ty = Sym.mk_ty rlt ;
+ bin_ty = Bin.mk_ty rlt
+ }
+ in
+ record, goal
+
+ (* We want to lift down the indexes of symbols. *)
+ let renumber (l: (int * 'a) list ) =
+ let _, l = List.fold_left (fun (next,acc) (glob,x) ->
+ (next+1, (next,glob,x)::acc)
+ ) (1,[]) l
+ in
+ let rec to_global loc = function
+ | [] -> assert false
+ | (local, global,x)::q when local = loc -> global
+ | _::q -> to_global loc q
+ in
+ let rec to_local glob = function
+ | [] -> assert false
+ | (local, global,x)::q when global = glob -> local
+ | _::q -> to_local glob q
+ in
+ let locals = List.map (fun (local,global,x) -> local,x) l in
+ locals, (fun x -> to_local x l) , (fun x -> to_global x l)
+
+ (** [build_sigma_maps] given a envs and some reif_params, we are
+ able to build the sigmas *)
+ let build_sigma_maps (rlt : Coq.Relation.t) zero ir (k : sigmas * sigma_maps -> Proof_type.tactic ) : Proof_type.tactic = fun goal ->
+ let rp,goal = build_reif_params goal rlt zero in
+ let renumbered_sym, to_local, to_global = renumber ir.sym in
+ let env_sym = Sigma.of_list
+ rp.sym_ty
+ (rp.sym_null)
+ renumbered_sym
+ in
+ Coq.cps_mk_letin "env_sym" env_sym
+ (fun env_sym ->
+ let bin = (List.map ( fun (n,s) -> n, Bin.mk_pack rlt s) ir.bin) in
+ let env_bin =
+ Sigma.of_list
+ rp.bin_ty
+ (Bin.mk_pack rlt rp.bin_null)
+ bin
+ in
+ Coq.cps_mk_letin "env_bin" env_bin
+ (fun env_bin ->
+ let units = (List.map (fun (n,s) -> n, Unit.mk_pack rlt env_bin s)ir.units) in
+ let env_units =
+ Sigma.of_list
+ (Unit.ty_unit_pack rlt env_bin)
+ (Unit.mk_pack rlt env_bin rp.unit_null )
+ units
+ in
+
+ Coq.cps_mk_letin "env_units" env_units
+ (fun env_units ->
+ let sigmas =
+ {
+ env_sym = env_sym ;
+ env_bin = env_bin ;
+ env_units = env_units;
+ } in
+ let f = List.map (fun (x,_) -> (x,Coq.Pos.of_int x)) in
+ let sigma_maps =
+ {
+ sym_to_pos = (let sym = f renumbered_sym in fun x -> (List.assoc (to_local x) sym));
+ bin_to_pos = (let bin = f bin in fun x -> (List.assoc x bin));
+ units_to_pos = (let units = f units in fun x -> (List.assoc x units));
+ }
+ in
+ k (sigmas, sigma_maps )
+ )
+ )
+ ) goal
+
+ (** builders for the reification *)
+ type reif_builders =
+ {
+ rsum: constr -> constr -> constr -> constr;
+ rprd: constr -> constr -> constr -> constr;
+ rsym: constr -> constr array -> constr;
+ runit : constr -> constr
+ }
+
+ (* donne moi une tactique, je rajoute ma part. Potentiellement, il
+ est possible d'utiliser la notation 'do' a la Haskell:
+ http://www.cas.mcmaster.ca/~carette/pa_monad/ *)
+ let (>>) : 'a * Proof_type.tactic -> ('a -> 'b * Proof_type.tactic) -> 'b * Proof_type.tactic =
+ fun (x,t) f ->
+ let b,t' = f x in
+ b, Tacticals.tclTHEN t t'
+
+ let return x = x, Tacticals.tclIDTAC
+
+ let mk_vect vnil vcons v =
+ let ar = Array.length v in
+ let rec aux = function
+ | 0 -> vnil
+ | n -> let n = n-1 in
+ mkApp( vcons,
+ [|
+ (Coq.Nat.of_int n);
+ v.(ar - 1 - n);
+ (aux (n))
+ |]
+ )
+ in aux ar
+
+ (* TODO: use a do notation *)
+ let mk_reif_builders (rlt: Coq.Relation.t) (env_sym:constr) (k: reif_builders -> Proof_type.tactic) =
+ let x = (rlt.Coq.Relation.carrier) in
+ let r = (rlt.Coq.Relation.r) in
+
+ let x_r_env = [|x;r;env_sym|] in
+ let tty = mkApp (Lazy.force Stubs._Tty, x_r_env) in
+ let rsum = mkApp (Lazy.force Stubs.rsum, x_r_env) in
+ let rprd = mkApp (Lazy.force Stubs.rprd, x_r_env) in
+ let rsym = mkApp (Lazy.force Stubs.rsym, x_r_env) in
+ let vnil = mkApp (Lazy.force Stubs.vnil, x_r_env) in
+ let vcons = mkApp (Lazy.force Stubs.vcons, x_r_env) in
+ Coq.cps_mk_letin "tty" tty
+ (fun tty ->
+ Coq.cps_mk_letin "rsum" rsum
+ (fun rsum ->
+ Coq.cps_mk_letin "rprd" rprd
+ (fun rprd ->
+ Coq.cps_mk_letin "rsym" rsym
+ (fun rsym ->
+ Coq.cps_mk_letin "vnil" vnil
+ (fun vnil ->
+ Coq.cps_mk_letin "vcons" vcons
+ (fun vcons ->
+ let r ={
+ rsum =
+ begin fun idx l r ->
+ mkApp (rsum, [| idx ; mk_mset tty [l,1 ; r,1]|])
+ end;
+ rprd =
+ begin fun idx l r ->
+ let lst = NEList.of_list tty [l;r] in
+ mkApp (rprd, [| idx; lst|])
+ end;
+ rsym =
+ begin fun idx v ->
+ let vect = mk_vect vnil vcons v in
+ mkApp (rsym, [| idx; vect|])
+ end;
+ runit = fun idx -> (* could benefit of a letin *)
+ mkApp (Lazy.force Stubs.runit , [|x;r;env_sym;idx; |])
+ }
+ in k r
+ ))))))
+
+
+
+ type reifier = sigma_maps * reif_builders
+
+
+ let mk_reifier rlt zero envs (k : sigmas *reifier -> Proof_type.tactic) =
+ build_sigma_maps rlt zero envs
+ (fun (s,sm) ->
+ mk_reif_builders rlt s.env_sym
+ (fun rb ->k (s,(sm,rb)) )
+
+ )
+
+ (** [reif_constr_of_t reifier t] rebuilds the term [t] in the
+ reified form. We use the [reifier] to minimise the size of the
+ terms (we make as much lets as possible)*)
+ let reif_constr_of_t (sm,rb) (t:Matcher.Terms.t) : constr =
+ let rec aux = function
+ | Matcher.Terms.Plus (s,l,r) ->
+ let idx = sm.bin_to_pos s in
+ rb.rsum idx (aux l) (aux r)
+ | Matcher.Terms.Dot (s,l,r) ->
+ let idx = sm.bin_to_pos s in
+ rb.rprd idx (aux l) (aux r)
+ | Matcher.Terms.Sym (s,t) ->
+ let idx = sm.sym_to_pos s in
+ rb.rsym idx (Array.map aux t)
+ | Matcher.Terms.Unit s ->
+ let idx = sm.units_to_pos s in
+ rb.runit idx
+ | Matcher.Terms.Var i ->
+ anomaly "call to reif_constr_of_t on a term with variables."
+ in aux t
+end
+
+
+