sidekick/src/quip/Proof.ml
2022-07-14 22:09:13 -04:00

231 lines
6.8 KiB
OCaml

type id = int
(** A reference to a previously defined object in the proof *)
(** Representation of types *)
module Ty = struct
type t =
| Bool
| Arrow of t array * t
| App of string * t array
| Ref of string
let equal : t -> t -> bool = ( = )
let hash : t -> int = CCHash.poly
let[@inline] view (self : t) : t = self
let rec pp out (self : t) =
match self with
| Bool -> Fmt.string out "Bool"
| Arrow (args, ret) ->
Fmt.fprintf out "(@[->@ %a@ %a@])" (Util.pp_array pp) args pp ret
| App (c, [||]) -> Fmt.string out c
| App (c, args) -> Fmt.fprintf out "(@[%s@ %a@])" c (Util.pp_array pp) args
| Ref name -> Fmt.fprintf out "(@@ %s)" name
end
module Fun = struct
type t = string
let pp out (self : t) = Fmt.string out self
let equal : t -> t -> bool = ( = )
let hash : t -> int = CCHash.poly
end
module Cstor = Fun
(** Representation of terms, with explicit sharing *)
module T = struct
type t =
| Bool of bool
| App_fun of Fun.t * t array
| App_ho of t * t
| Is_a of Fun.t * t
| Ite of t * t * t
| Not of t
| Eq of t * t
| Ref of string
let[@inline] view (self : t) : t = self
let equal : t -> t -> bool = ( = )
let hash : t -> int = CCHash.poly
let true_ : t = Bool true
let false_ : t = Bool false
let bool b = Bool b
let not_ = function
| Not x -> x
| x -> Not x
let eq a b : t = Eq (a, b)
let ref name : t = Ref name
let app_fun f args : t = App_fun (f, args)
let const c = app_fun c [||]
let app_ho a b : t = App_ho (a, b)
let ite a b c : t = Ite (a, b, c)
let is_a f t : t = Is_a (f, t)
let rec pp out = function
| Bool b -> Fmt.bool out b
| Ite (a, b, c) -> Fmt.fprintf out "(@[if@ %a@ %a@ %a@])" pp a pp b pp c
| App_fun (f, [||]) -> Fmt.string out f
| App_fun (f, args) ->
Fmt.fprintf out "(@[%a@ %a@])" Fun.pp f (Util.pp_array pp) args
| App_ho (f, a) -> Fmt.fprintf out "(@[%a@ %a@])" pp f pp a
| Not a -> Fmt.fprintf out "(@[not@ %a@])" pp a
| Eq (a, b) -> Fmt.fprintf out "(@[=@ %a@ %a@])" pp a pp b
| Ref name -> Fmt.fprintf out "(@@ %s)" name
| Is_a (c, t) -> Fmt.fprintf out "(@[(_ is %a)@ %a@])" Fun.pp c pp t
end
type term = T.t
type ty = Ty.t
module Lit = struct
type t = L_eq of term * term | L_a of bool * term
let pp_with ~pp_t out = function
| L_eq (t, u) -> Fmt.fprintf out "(@[=@ %a@ %a@])" pp_t t pp_t u
| L_a (false, t) -> Fmt.fprintf out "(@[not@ %a@])" pp_t t
| L_a (true, t) -> pp_t out t
let pp = pp_with ~pp_t:T.pp
let eq t u = L_eq (t, u)
let mk b t = L_a (b, t)
end
type clause = Lit.t list
type t =
| Unspecified
| Sorry (* NOTE: v. bad as we don't even specify the return *)
| Sorry_c of clause
| Named of string (* refers to previously defined clause *)
| Refl of term
| CC_lemma_imply of t list * term * term
| CC_lemma of clause
| Assertion of term
| Assertion_c of clause
| Hres of t * hres_step list
| Res of term * t * t
| Res1 of t * t
| Paramod1 of t * t
| Rup of clause * t list
| Clause_rw of {
res: clause;
c0: t;
using: t list; (** the rewriting equations/atoms *)
}
| DT_isa_split of ty * term list
| DT_isa_disj of ty * term * term
| DT_cstor_inj of Cstor.t * int * term list * term list (* [c t…=c u… |- t_i=u_i] *)
| Bool_true_is_true
| Bool_true_neq_false
| Bool_eq of term * term (* equal by pure boolean reasoning *)
| Bool_c of bool_c_name * term list (* boolean tautology *)
| Ite_true of term (* given [if a b c] returns [a=T |- if a b c=b] *)
| Ite_false of term
| LRA of clause
| Composite of {
(* some named (atomic) assumptions *)
assumptions: (string * Lit.t) list;
steps: composite_step array; (* last proof_rule is the proof *)
}
and bool_c_name = string
and composite_step =
| S_step_c of {
name: string; (* name *)
res: clause; (* result of [proof] *)
proof: t; (* sub-proof *)
}
(** A named step in {!Composite}, with the expected result.
This decouples the checking of the sub-proof, from its use in the rest
of the proof, as we can use [res] even if checking [proof] failed. *)
| S_step_anon of { name: string; (* name of step *) proof: t (* proof *) }
(** A named intermediate proof, to be reused in subsequent proofs.
Unlike {!S_step_c} we do not specify the expected result
so if this fails, any proof downstream will also fail. *)
| S_define_t of term * term (* [const := t] *)
| S_define_t_name of string * term
(* [const := t] *)
and hres_step =
| R of { pivot: term; p: t }
| R1 of t
| P of { lhs: term; rhs: term; p: t }
| P1 of t
let r p ~pivot : hres_step = R { pivot; p }
let r1 p = R1 p
let p p ~lhs ~rhs : hres_step = P { p; lhs; rhs }
let p1 p = P1 p
let stepc ~name res proof : composite_step = S_step_c { proof; name; res }
let step_anon ~name proof : composite_step = S_step_anon { name; proof }
let deft c rhs : composite_step = S_define_t (c, rhs)
let deft_name c rhs : composite_step = S_define_t_name (c, rhs)
let is_trivial_refl = function
| Refl _ -> true
| _ -> false
let default = Unspecified
let sorry_c c = Sorry_c (Iter.to_rev_list c)
let sorry_c_l c = Sorry_c c
let sorry = Sorry
let refl t : t = Refl t
let ref_by_name name : t = Named name
let cc_lemma c : t = CC_lemma c
let cc_imply_l l t u : t =
let l = List.filter (fun p -> not (is_trivial_refl p)) l in
match l with
| [] -> refl t (* only possible way [t=u] *)
| l -> CC_lemma_imply (l, t, u)
let cc_imply2 h1 h2 t u : t = CC_lemma_imply ([ h1; h2 ], t, u)
let assertion t = Assertion t
let assertion_c c = Assertion_c (Iter.to_rev_list c)
let assertion_c_l c = Assertion_c c
let rup c hyps : t = Rup (c, hyps)
let clause_rw c0 ~res ~using : t = Clause_rw { res; c0; using }
let composite_a ?(assms = []) steps : t =
Composite { assumptions = assms; steps }
let composite_l ?(assms = []) steps : t =
Composite { assumptions = assms; steps = Array.of_list steps }
let composite_iter ?(assms = []) steps : t =
let steps = Iter.to_array steps in
Composite { assumptions = assms; steps }
let isa_split ty i = DT_isa_split (ty, Iter.to_rev_list i)
let isa_disj ty t u = DT_isa_disj (ty, t, u)
let cstor_inj c i t u = DT_cstor_inj (c, i, t, u)
let ite_true t = Ite_true t
let ite_false t = Ite_false t
let true_is_true : t = Bool_true_is_true
let true_neq_false : t = Bool_true_neq_false
let bool_eq a b : t = Bool_eq (a, b)
let bool_c name c : t = Bool_c (name, c)
let hres_l p l : t =
let l =
List.filter
(function
| P1 (Refl _) -> false
| _ -> true)
l
in
if l = [] then
p
else
Hres (p, l)
let hres_iter c i : t = hres_l c (Iter.to_list i)
let res ~pivot p1 p2 : t = Res (pivot, p1, p2)
let res1 p1 p2 : t = Res1 (p1, p2)
let paramod1 p1 p2 : t = Paramod1 (p1, p2)
let lra_l c : t = LRA c
let lra c = LRA (Iter.to_rev_list c)