bedrock.prelude.elpi.derive.countable
(*
* Copyright (c) 2023 BlueRock Security, Inc.
* This software is distributed under the terms of the BedRock Open-Source License.
* See the LICENSE-BedRock file in the repository root for details.
*)
Require Import ssreflect.
Require Import stdpp.countable.
Require Import elpi.elpi.
Require Export bedrock.prelude.elpi.derive.common.
Require Export bedrock.prelude.elpi.derive.eq_dec.
Require Import bedrock.prelude.elpi.basis.
Elpi Accumulate derive File bedrock.basis.elpi.
(***************************************************
Countable
***************************************************)
Elpi Db derive.stdpp.countable.db lp:{{
pred countable o:gref, o:gref.
pred countable-done o:gref.
}}.
Elpi Accumulate derive.stdpp.countable.db File bedrock.typeclass.elpi.
#[superglobal] Elpi Accumulate derive.stdpp.countable.db lp:{{
:name "countable-done.typeclass"
countable-done GR :-
typeclass "derive.stdpp.countable.db"
(before "countable-done.typeclass") (countable-done GR) {{ @Countable lp:{{global GR}} _ }} Bo_.
}}.
Elpi Accumulate derive Db derive.stdpp.countable.db.
* Copyright (c) 2023 BlueRock Security, Inc.
* This software is distributed under the terms of the BedRock Open-Source License.
* See the LICENSE-BedRock file in the repository root for details.
*)
Require Import ssreflect.
Require Import stdpp.countable.
Require Import elpi.elpi.
Require Export bedrock.prelude.elpi.derive.common.
Require Export bedrock.prelude.elpi.derive.eq_dec.
Require Import bedrock.prelude.elpi.basis.
Elpi Accumulate derive File bedrock.basis.elpi.
(***************************************************
Countable
***************************************************)
Elpi Db derive.stdpp.countable.db lp:{{
pred countable o:gref, o:gref.
pred countable-done o:gref.
}}.
Elpi Accumulate derive.stdpp.countable.db File bedrock.typeclass.elpi.
#[superglobal] Elpi Accumulate derive.stdpp.countable.db lp:{{
:name "countable-done.typeclass"
countable-done GR :-
typeclass "derive.stdpp.countable.db"
(before "countable-done.typeclass") (countable-done GR) {{ @Countable lp:{{global GR}} _ }} Bo_.
}}.
Elpi Accumulate derive Db derive.stdpp.countable.db.
This Gallina function is used at code generation time (not at runtime) to produce the
positive associated to a particular value a : T, given the list of all constructors l : list T.
#[local] Fixpoint lookup_from_ctorlist `{EqDecision T} (p : positive) (l : list T) (a : T) : positive :=
match l with
| [] => p
| a' :: l' => if bool_decide (a = a') then p else lookup_from_ctorlist (p + 1)%positive l' a
end.
match l with
| [] => p
| a' :: l' => if bool_decide (a = a') then p else lookup_from_ctorlist (p + 1)%positive l' a
end.
TODO: This generic decoding function is used in generated Countable instances but isn't efficient.
It would be better to replace it by a table that's built at code generation time.
#[local] Fixpoint decode_from_encode `{EqDecision T} (encode : T -> positive) (l : list T) (x : positive) : option T :=
match l with
| [] => None
| a :: l' => if bool_decide (encode a = x) then Some a else decode_from_encode encode l' x
end.
match l with
| [] => None
| a :: l' => if bool_decide (encode a = x) then Some a else decode_from_encode encode l' x
end.
This needs to be exported for use by callers of Deriving but shouldn't be otherwise used outside this module.
Ltac derive_solve_countable := by case.
TODO: abstract
Elpi File derive.countable.elpi lp:{{
namespace derive.countable {
/*
[mk-countable CtorList Name T] assumes that type [T] has
constructors [CtorList] and generates a global instance [Name : Countable T].
*/
pred ctor-to-positive i:term, i:term, o:term.
ctor-to-positive CtorList K Pos :- std.do! [
T = {{ @lookup_from_ctorlist _ _ 1%positive lp:CtorList lp:K }},
std.assert-ok! (coq.elaborate-skeleton T _ EPos) "asdfasdf",
coq.reduction.vm.norm EPos _ Pos,
].
pred rty i:term, i:term, i:list term, i:list term, o:term.
rty RTy _ _ _ RTy.
pred to-positive-branch i:term, i:term, i:term, i:list term, i:list term, o:term.
to-positive-branch CtorList K _Kty _Vars _VarsTys PosEncoding :- std.do![
ctor-to-positive CtorList K PosEncoding
].
pred to-positive i:term, i:term, i:term, i:term, o:term.
to-positive CtorList S Ty RTy Match :-
coq.build-match S Ty (rty RTy) (to-positive-branch CtorList) Match.
pred mk-countable i:list term, i:string, i:gref, i:gref, o:constant.
mk-countable Ctors Name VariantGR OrigGR C :- std.do![
bedrock.elpi-list->list Ctors CtorList,
std.assert-ok! (coq.elaborate-skeleton CtorList _ ECtorList) "[mk-countable] failed to elaborate ctors",
VariantTy = global VariantGR,
Encode = {{ fun (x : lp:VariantTy) => lp:{{ { to-positive ECtorList {{ x }} VariantTy {{ positive }} } }} }},
Decode = {{ @decode_from_encode lp:VariantTy _ lp:Encode lp:CtorList }},
Lem = {{ forall x : lp:VariantTy, lp:Decode (lp:Encode x) = Some x }},
std.assert-ok! (coq.elaborate-skeleton Lem _ ELem) "[mk-countable] failed to elaborate lem",
std.assert-ok! (coq.typecheck {{ lp:Bo : lp:ELem }} _) "[mk-countable] failed to typecheck lem",
coq.ltac.collect-goals Bo [SealedGoal] [],
coq.ltac.open (coq.ltac.call "derive_solve_countable" []) SealedGoal [],
eqdec OrigGR GrEqdec,
EqDec = global GrEqdec,
Inst = {{ @Build_Countable lp:VariantTy lp:EqDec lp:Encode lp:Decode lp:Bo }},
std.assert-ok!
(coq.elaborate-skeleton Inst _ EInst)
"[mk-countable] failed to elaborate instance",
coq.env.add-const Name EInst {{ @Countable lp:{{global OrigGR}} lp:EqDec }} ff C,
@global! => coq.TC.declare-instance (const C) 0, %%TODO: previously level 5; was there a reason?
].
}
}}.
#[phase="both"] Elpi Accumulate derive lp:{{
dep1 "countable" "eq_dec".
}}.
#[synterp] Elpi Accumulate derive lp:{{
derivation _ _ (derive "countable" (cl\ cl = []) true).
}}.
Elpi Accumulate derive File derive.countable.elpi.
Elpi Accumulate derive lp:{{
namespace derive.countable {
pred main i:gref, i:string, o:list prop.
main TyGR Prefix Clauses :- std.do! [
bedrock.get-indt TyGR VariantI,
derive-original-gref TyGR OrigGR,
coq.env.indt VariantI _ _ _ _ Ctors _,
std.map Ctors (c\ c'\ c' = global (indc c)) CTerms,
CountableName is Prefix ^ "countable",
derive.countable.mk-countable CTerms CountableName TyGR OrigGR C,
Clauses = [countable-done TyGR, countable TyGR (const C)],
std.forall Clauses (x\
coq.elpi.accumulate _ "derive.stdpp.countable.db" (clause _ _ x)
),
].
main _ _ _ :- usage.
pred usage.
usage :- coq.error
"Usage: #[only(countable)] derive T where T is an inductive or a definition that unfolds to an inductive. Example #1: Variant T := A | B | C. #[only(countable)] derive T Example #2: #[only(countable)] derive Variant T := A | B | C. Example #3: Variant _T := A | B | C. Definition T := _T. #[only(countable)] derive T.".
}
derivation
(indt T) Prefix ff
(derive "countable"
(derive.countable.main (indt T) Prefix)
(countable-done (indt T))
).
}}.