Require Import Coq.Strings.String. From stdpp Require Import gmap. (* The Nix Expression Language *) (** Selected operators from Dolsta and Löh, 2008. Newer definitions at https://nixos.org/manual/nix/stable/language/operators *) Variant op : Type := | O_Eq (* = *) | O_Lt (* < *) | O_Plus (* + *) | O_Min (* - *) | O_Div (* / *) | O_Upd. (* // *) Hint Constructors op : core. Variant nonempty A := Ne_Cons (head : A) (tail : list A). Arguments Ne_Cons {A} _ _. Hint Constructors nonempty : core. Inductive expr : Type := | X_V (v : value) (* v *) | X_Id (x : string) (* x *) | X_Attrset (bs : gmap string b_rhs) (* { bᵣ* } *) | X_LetBinding (bs : gmap string b_rhs) (e : expr) (* let bᵣ* in e *) | X_Select (e : expr) (xs : nonempty string) (* e.xs *) | X_SelectOr (e : expr) (xs : nonempty string) (or : expr) (* e.xs or e *) | X_Apply (e1 e2 : expr) (* e1 e2 *) | X_Cond (e1 e2 e3 : expr) (* if e1 then e2 else e3 *) | X_Incl (e1 e2 : expr) (* with e1; e2 *) | X_Assert (e1 e2 : expr) (* assert e1; e2 *) | X_Op (op : op) (e1 e2 : expr) (* e1 e2 *) | X_HasAttr (e1 : expr) (x : string) (* e ? x *) | X_Force (e : expr) (* force e *) | X_Closed (e : expr) (* closed(e) *) | X_Placeholder (x : string) (e : expr) (* placeholderₓ(e) *) with b_rhs := | B_Rec (e : expr) (* := e *) | B_Nonrec (e : expr) (* :=ᵣ e *) with matcher := | M_Matcher (ms : gmap string m_rhs) (strict : bool) with m_rhs := | M_Mandatory | M_Optional (e : expr) (* ? e *) with value := | V_Bool (p : bool) : value (* true | false *) | V_Null : value (* null *) | V_Int (n : Z) : value (* n *) | V_Str (s : string) : value (* s *) | V_Fn (x : string) (e : expr) : value (* x: e *) | V_AttrsetFn (m : matcher) (e : expr) : value (* { m }: e *) | V_Attrset (bs : gmap string expr) : value. (* { b* } *) Hint Constructors expr : core. Instance Maybe_X_Attrset : Maybe X_Attrset := λ e, match e with | X_Attrset bs => Some bs | _ => None end. Instance Maybe_X_V : Maybe X_V := λ e, match e with | X_V v => Some v | _ => None end. Instance Maybe_B_Nonrec : Maybe B_Nonrec := λ rhs, match rhs with | B_Nonrec e => Some e | _ => None end. Instance Maybe_B_Rec : Maybe B_Rec := λ rhs, match rhs with | B_Rec e => Some e | _ => None end. Lemma maybe_b_rhs_excl b : is_Some (maybe B_Nonrec b) → is_Some (maybe B_Rec b) → False. Proof. intros [e2 Hnonrec] [e1 Hrec]. simplify_option_eq. Qed. Lemma no_b_nonrec__b_rec b : ¬ is_Some (maybe B_Nonrec b) → is_Some (maybe B_Rec b). Proof. destruct b; try by eauto. simplify_eq/=. intros contra. apply is_Some_alt. eauto. Qed. Lemma no_b_rec__b_nonrec b : ¬ is_Some (maybe B_Rec b) → is_Some (maybe B_Nonrec b). Proof. destruct b; try by eauto. simplify_eq/=. intros contra. apply is_Some_alt. eauto. Qed. Instance Maybe_V_Attrset : Maybe V_Attrset := λ e, match e with | V_Attrset bs => Some bs | _ => None end. Instance Maybe_V_Bool : Maybe V_Bool := λ e, match e with | V_Bool p => Some p | _ => None end. Global Instance B_Nonrec_inj : Inj (=) (=) B_Nonrec. Proof. intros [] [] ?; by simplify_eq. Qed. Definition matcher_keys (m : matcher) : gset string := match m with M_Matcher ms _ => dom ms end. Definition matcher_map (f : expr → expr) (m : matcher) : matcher := let map_m_rhs := λ rhs, match rhs with | M_Optional e => M_Optional (f e) | _ => rhs end in match m with | M_Matcher ms strict => M_Matcher (map_m_rhs <$> ms) strict end. Local Definition map_delete_all {K V} `{Countable K} (ks : gset K) (m : gmap K V) : gmap K V := set_fold (λ k m', map_delete k m') m ks. Local Definition b_rhs_map (f g : expr → expr) (rhs : b_rhs) : b_rhs := match rhs with | B_Rec e => B_Rec (f e) | B_Nonrec e => B_Nonrec (g e) end. (* See Dolstra (2006), §4.3.3 and fig. 4.6 (p. 75, PDF: p. 83) *) Fixpoint subst (subs : gmap string expr) (e : expr) : expr := let msubst subs' := b_rhs_map (subst subs') (subst subs) in match e with | X_V V_Null | X_V (V_Bool _) | X_V (V_Str _) | X_V (V_Int _) => e | X_Id x | X_Placeholder x _ => default e (subs !! x) | X_Attrset bs => let subs' := map_delete_all (dom bs) subs in X_Attrset (msubst subs' <$> bs) | X_V (V_Attrset bs) => X_V (V_Attrset (subst subs <$> bs)) | X_LetBinding bs e' => let subs' := map_delete_all (dom bs) subs in X_LetBinding (msubst subs' <$> bs) (subst subs' e') | X_Select e' x => X_Select (subst subs e') x | X_SelectOr e' x or => X_SelectOr (subst subs e') x (subst subs or) | X_V (V_Fn x e') => let subs' := map_delete x subs in X_V (V_Fn x (subst subs' e')) | X_V (V_AttrsetFn (M_Matcher ms _ as m) e') => let subs' := map_delete_all (matcher_keys m) subs in X_V (V_AttrsetFn (matcher_map (subst subs') m) (subst subs' e')) | X_Apply e1 e2 => X_Apply (subst subs e1) (subst subs e2) | X_Cond e1 e2 e3 => X_Cond (subst subs e1) (subst subs e2) (subst subs e3) | X_Incl e1 e2 => X_Incl (subst subs e1) (subst subs e2) | X_Op op e1 e2 => X_Op op (subst subs e1) (subst subs e2) | X_HasAttr e x => X_HasAttr (subst subs e) x | X_Assert e1 e2 => X_Assert (subst subs e1) (subst subs e2) | X_Closed e => X_Closed e | X_Force e => X_Force (subst subs e) end. Definition closed (bs : gmap string expr) : gmap string expr := X_Closed <$> bs. Definition placeholders (bs : gmap string expr) : gmap string expr := map_imap (λ (x : string) (e : expr), Some (X_Placeholder x e)) bs. Definition nonempty_singleton {A} (a : A): nonempty A := Ne_Cons a nil. Definition nonempty_cons {A} (a : A) (l : nonempty A) : nonempty A := match l with Ne_Cons head tail => Ne_Cons a (head :: tail) end. Definition indirect (bs : gmap string b_rhs) : gmap string expr := map_imap (λ (x : string) (rhs : b_rhs), Some (X_Select (X_Attrset bs) (nonempty_singleton x))) bs. Definition rec_subst (bs : gmap string b_rhs) : gmap string expr := let subs := indirect bs in (λ b, match b with | B_Rec e => subst (closed subs) e | B_Nonrec e => e end) <$> bs. Inductive strong_value : Type := | SV_Bool (b : bool) | SV_Null | SV_Int (n : Z) | SV_Str (s : string) | SV_Fn (x : string) (e : expr) | SV_AttrsetFn (m : matcher) (e : expr) | SV_Attrset (bs : gmap string strong_value). Fixpoint value_from_strong_value (sv : strong_value) : value := match sv with | SV_Null => V_Null | SV_Bool b => V_Bool b | SV_Int n => V_Int n | SV_Str s => V_Str s | SV_Fn x e => V_Fn x e | SV_AttrsetFn m e => V_AttrsetFn m e | SV_Attrset bs => V_Attrset (X_V ∘ value_from_strong_value <$> bs) end. Global Instance X_V_inj : Inj (=) (=) X_V. Proof. intros [] [] ?; by simplify_eq. Qed. Definition expr_from_strong_value : strong_value → expr := X_V ∘ value_from_strong_value. (* Based on the fin_maps test from stdpp *) Fixpoint sv_size (sv : strong_value) : nat := match sv with | SV_Null | SV_Bool _ | SV_Int _ | SV_Str _ | SV_Fn _ _ | SV_AttrsetFn _ _ => 1 | SV_Attrset bs => S (map_fold (λ _ sv', plus (sv_size sv')) 0 bs) end. (* Based on the fin_maps test from stdpp *) Lemma strong_value_ind' : ∀ P : strong_value → Prop, P SV_Null → (∀ b : bool, P (SV_Bool b)) → (∀ n : Z, P (SV_Int n)) → (∀ s : string, P (SV_Str s)) → (∀ (x : string) (e : expr), P (SV_Fn x e)) → (∀ (m : matcher) (e : expr), P (SV_AttrsetFn m e)) → (∀ bs : gmap string strong_value, map_Forall (λ i, P) bs → P (SV_Attrset bs)) → ∀ s : strong_value, P s. Proof. intros P PNull PBool PInt PStr PFn PAttrsetFn PAttrset sv. remember (sv_size sv) as n eqn:Hn. revert sv Hn. induction (lt_wf n) as [n _ IH]; intros [| | | | | | bs] ->; simpl in *; try done. apply PAttrset. revert bs IH. apply (map_fold_ind (λ r bs, (∀ n', n' < S r → _) → map_Forall (λ _, P) bs)). - intros IH. apply map_Forall_empty. - intros k sv bs r ? IHbs IHsv. apply map_Forall_insert; [done|]. split. + eapply IHsv; [|done]; lia. + eapply IHbs. intros; eapply IHsv; [|done]; lia. Qed. Coercion X_Id : string >-> expr. Coercion V_Int : Z >-> value. (* Leaving this out for now: would conflict with X_Id and therefore cause ambiguity *) (* Coercion X_StrVal : string >-> expr. *) Coercion V_Bool : bool >-> value. Coercion X_V : value >-> expr. Coercion value_from_strong_value : strong_value >-> value.