aboutsummaryrefslogtreecommitdiffstats
path: root/theories/nix/interp.v
diff options
context:
space:
mode:
Diffstat (limited to 'theories/nix/interp.v')
-rw-r--r--theories/nix/interp.v351
1 files changed, 351 insertions, 0 deletions
diff --git a/theories/nix/interp.v b/theories/nix/interp.v
new file mode 100644
index 0000000..bb4e815
--- /dev/null
+++ b/theories/nix/interp.v
@@ -0,0 +1,351 @@
1From Coq Require Import Ascii.
2From mininix Require Export res nix.operational_props.
3From stdpp Require Import options.
4
5Section val.
6 Local Unset Elimination Schemes.
7 Inductive val :=
8 | VLit (bl : base_lit) (Hbl : base_lit_ok bl)
9 | VClo (x : string) (E : gmap string (kind * thunk)) (e : expr)
10 | VCloMatch (E : gmap string (kind * thunk))
11 (ms : gmap string (option expr))
12 (strict : bool) (e : expr)
13 | VList (ts : list thunk)
14 | VAttr (ts : gmap string thunk)
15 with thunk :=
16 | Forced (v : val) : thunk
17 | Thunk (E : gmap string (kind * thunk)) (e : expr) : thunk
18 | Indirect (x : string)
19 (E : gmap string (kind * thunk))
20 (tαs : gmap string (expr + thunk)).
21End val.
22
23Notation VLitI bl := (VLit bl I).
24
25Notation tattr := (expr + thunk)%type.
26Notation env := (gmap string (kind * thunk)).
27
28Definition maybe_VLit (v : val) : option base_lit :=
29 if v is VLit bl _ then Some bl else None.
30Global Instance maybe_VList : Maybe VList := λ v,
31 if v is VList ts then Some ts else None.
32Global Instance maybe_VAttr : Maybe VAttr := λ v,
33 if v is VAttr ts then Some ts else None.
34
35Fixpoint interp_eq_list_body (i : nat) (ts1 ts2 : list thunk) : expr :=
36 match ts1, ts2 with
37 | [], [] => ELit (LitBool true)
38 | _ :: ts1, _ :: ts2 =>
39 EIf (EBinOp EqOp (EId' ("1" +:+ pretty i)) (EId' ("2" +:+ pretty i)))
40 (interp_eq_list_body (S i) ts1 ts2) (ELit (LitBool false))
41 | _, _ => ELit (LitBool false)
42 end.
43
44Definition interp_eq_list (ts1 ts2 : list thunk) : thunk :=
45 Thunk (kmap (λ n : nat, String "1" (pretty n)) ((ABS,.) <$> map_seq 0 ts1) ∪
46 kmap (λ n : nat, String "2" (pretty n)) ((ABS,.) <$> map_seq 0 ts2)) $
47 interp_eq_list_body 0 ts1 ts2.
48
49Fixpoint interp_lt_list_body (i : nat) (ts1 ts2 : list thunk) : expr :=
50 match ts1, ts2 with
51 | [], _ => ELit (LitBool true)
52 | _ :: ts1, _ :: ts2 =>
53 EIf (EBinOp LtOp (EId' ("1" +:+ pretty i)) (EId' ("2" +:+ pretty i)))
54 (ELit (LitBool true))
55 (EIf (EBinOp EqOp (EId' ("1" +:+ pretty i)) (EId' ("2" +:+ pretty i)))
56 (interp_lt_list_body (S i) ts1 ts2) (ELit (LitBool false)))
57 | _ :: _, [] => ELit (LitBool false)
58 end.
59
60Definition interp_lt_list (ts1 ts2 : list thunk) : thunk :=
61 Thunk (kmap (λ n : nat, String "1" (pretty n)) ((ABS,.) <$> map_seq 0 ts1) ∪
62 kmap (λ n : nat, String "2" (pretty n)) ((ABS,.) <$> map_seq 0 ts2)) $
63 interp_lt_list_body 0 ts1 ts2.
64
65Definition interp_eq_attr (ts1 ts2 : gmap string thunk) : thunk :=
66 Thunk (kmap (String "1") ((ABS,.) <$> ts1) ∪
67 kmap (String "2") ((ABS,.) <$> ts2)) $
68 sem_and_attr $ map_imap (λ x _,
69 Some (EBinOp EqOp (EId' ("1" +:+ x)) (EId' ("2" +:+ x)))) ts1.
70
71Definition interp_eq (v1 v2 : val) : option thunk :=
72 match v1, v2 with
73 | VLit bl1 _, VLit bl2 _ =>
74 Some $ Forced $ VLitI (LitBool $ sem_eq_base_lit bl1 bl2)
75 | VClo _ _ _, VClo _ _ _ => None
76 | VList ts1, VList ts2 => Some $
77 if decide (length ts1 = length ts2) then interp_eq_list ts1 ts2
78 else Forced $ VLitI (LitBool false)
79 | VAttr ts1, VAttr ts2 => Some $
80 if decide (dom ts1 = dom ts2) then interp_eq_attr ts1 ts2
81 else Forced $ VLitI (LitBool false)
82 | _, _ => Some $ Forced $ VLitI (LitBool false)
83 end.
84
85Definition type_of_val (v : val) : string :=
86 match v with
87 | VLit bl _ => type_of_base_lit bl
88 | VClo _ _ _ | VCloMatch _ _ _ _ => "lambda"
89 | VList _ => "list"
90 | VAttr _ => "set"
91 end.
92
93Global Instance val_inhabited : Inhabited val := populate (VLitI inhabitant).
94Global Instance thunk_inhabited : Inhabited thunk := populate (Forced inhabitant).
95
96Definition interp_bin_op (op : bin_op) (v1 : val) : option (val → option thunk) :=
97 if decide (op = EqOp) then
98 Some (interp_eq v1)
99 else if decide (op = TypeOfOp) then
100 Some $ λ v2,
101 guard (maybe_VLit v2 = Some LitNull);;
102 Some $ Forced $ VLitI (LitString $ type_of_val v1)
103 else
104 match v1 with
105 | VLit (LitNum n1) Hn1 =>
106 if maybe RoundOp op is Some m then
107 Some $ λ v2,
108 guard (maybe_VLit v2 = Some LitNull);;
109 Some $ Forced $ VLit
110 (LitNum $ NInt $ float_to_bounded_Z $ Float.round m $ num_to_float n1)
111 (float_to_bounded_Z_ok _)
112 else
113 '(f ↾ Hf) ← option_to_eq_Some (sem_bin_op_num op n1);
114 Some $ λ v2,
115 if v2 is VLit (LitNum n2) Hn2 then
116 '(bl ↾ Hbl) ← option_to_eq_Some (f n2);
117 Some $ Forced $ VLit bl (sem_bin_op_num_ok Hn1 Hn2 Hf Hbl)
118 else None
119 | VLit (LitString s1) _ =>
120 match op with
121 | SingletonAttrOp => Some $ λ v2,
122 guard (maybe_VLit v2 = Some LitNull);;
123 Some $ Forced $ VClo "t" ∅ (EAttr {[ s1 := AttrN (EId' "t") ]})
124 | MatchStringOp => Some $ λ v2,
125 guard (maybe_VLit v2 = Some LitNull);;
126 match s1 with
127 | EmptyString => Some $ Forced $ VAttr {[
128 "empty" := Forced (VLitI (LitBool true));
129 "head" := Forced (VLitI LitNull);
130 "tail" := Forced (VLitI LitNull) ]}
131 | String a s1 => Some $ Forced $ VAttr {[
132 "empty" := Forced (VLitI (LitBool false));
133 "head" := Forced (VLitI (LitString (String a EmptyString)));
134 "tail" := Forced (VLitI (LitString s1)) ]}
135 end
136 | _ =>
137 '(f ↾ Hf) ← option_to_eq_Some (sem_bin_op_string op);
138 Some $ λ v2,
139 bl2 ← maybe_VLit v2;
140 s2 ← maybe LitString bl2;
141 Some $ Forced $ VLit (f s1 s2) (sem_bin_op_string_ok Hf)
142 end
143 | VClo _ _ _ =>
144 match op with
145 | FunctionArgsOp => Some $ λ v2,
146 guard (maybe_VLit v2 = Some LitNull);;
147 Some (Forced (VAttr ∅))
148 | _ => None
149 end
150 | VCloMatch _ ms _ _ =>
151 match op with
152 | FunctionArgsOp => Some $ λ v2,
153 guard (maybe_VLit v2 = Some LitNull);;
154 Some $ Forced $ VAttr $
155 (λ m, Forced $ VLitI (LitBool (from_option (λ _, true) false m))) <$> ms
156 | _ => None
157 end
158 | VList ts1 =>
159 match op with
160 | LtOp => Some $ λ v2,
161 ts2 ← maybe VList v2;
162 Some (interp_lt_list ts1 ts2)
163 | MatchListOp => Some $ λ v2,
164 guard (maybe_VLit v2 = Some LitNull);;
165 match ts1 with
166 | [] => Some $ Forced $ VAttr {[
167 "empty" := Forced (VLitI (LitBool true));
168 "head" := Forced (VLitI LitNull);
169 "tail" := Forced (VLitI LitNull) ]}
170 | t :: ts1 => Some $ Forced $ VAttr {[
171 "empty" := Forced (VLitI (LitBool false));
172 "head" := t;
173 "tail" := Forced (VList ts1) ]}
174 end
175 | AppendListOp => Some $ λ v2,
176 ts2 ← maybe VList v2;
177 Some (Forced (VList (ts1 ++ ts2)))
178 | _ => None
179 end
180 | VAttr ts1 =>
181 match op with
182 | SelectAttrOp => Some $ λ v2,
183 bl ← maybe_VLit v2;
184 x ← maybe LitString bl;
185 ts1 !! x
186 | UpdateAttrOp => Some $ λ v2,
187 ts2 ← maybe VAttr v2;
188 Some $ Forced $ VAttr $ ts2 ∪ ts1
189 | HasAttrOp => Some $ λ v2,
190 bl ← maybe_VLit v2;
191 x ← maybe LitString bl;
192 Some $ Forced $ VLitI (LitBool $ bool_decide (is_Some (ts1 !! x)))
193 | DeleteAttrOp => Some $ λ v2,
194 bl ← maybe_VLit v2;
195 x ← maybe LitString bl;
196 Some $ Forced $ VAttr $ delete x ts1
197 | MatchAttrOp => Some $ λ v2,
198 guard (maybe_VLit v2 = Some LitNull);;
199 match map_minimal_key attr_le ts1 with
200 | None => Some $ Forced $ VAttr {[
201 "empty" := Forced (VLitI (LitBool true));
202 "key" := Forced (VLitI LitNull);
203 "head" := Forced (VLitI LitNull);
204 "tail" := Forced (VLitI LitNull) ]}
205 | Some x => Some $ Forced $ VAttr {[
206 "empty" := Forced (VLitI (LitBool false));
207 "key" := Forced (VLitI (LitString x));
208 "head" := ts1 !!! x;
209 "tail" := Forced (VAttr (delete x ts1)) ]}
210 end
211 | _ => None
212 end
213 | _ => None
214 end.
215
216Definition interp_match
217 (ts : gmap string thunk) (mds : gmap string (option expr))
218 (strict : bool) : option (gmap string tattr) :=
219 map_mapM id $ merge (λ mt mmd,
220 (* Some (Some _) means keep, Some None means fail, None means skip *)
221 match mt, mmd with
222 | Some t, Some _ => Some $ Some (inr t)
223 | None, Some (Some e) => Some $ Some (inl e)
224 | None, Some _ => Some None (* bad *)
225 | Some _, None => guard strict;; Some None
226 | _, _ => None (* skip *)
227 end) ts mds.
228
229Definition force_deep1
230 (force_deep : val → res val)
231 (interp_thunk : thunk → res val) (v : val) : res val :=
232 match v with
233 | VList ts => VList ∘ fmap Forced <$>
234 mapM (mbind force_deep ∘ interp_thunk) ts
235 | VAttr ts => VAttr ∘ fmap Forced <$>
236 map_mapM_sorted attr_le (mbind force_deep ∘ interp_thunk) ts
237 | _ => mret v
238 end.
239
240Definition indirects_env (E : env) (tαs : gmap string tattr) : env :=
241 map_imap (λ y _, Some (ABS, Indirect y E tαs)) tαs ∪ E.
242
243Definition attr_to_tattr (E : env) (α : attr) : tattr :=
244 from_attr inl (inr ∘ Thunk E) α.
245
246Definition interp1
247 (force_deep : val → res val)
248 (interp : env → expr → res val)
249 (interp_thunk : thunk → res val)
250 (interp_app : val → thunk → res val)
251 (E : env) (e : expr) : res val :=
252 match e with
253 | ELit bl =>
254 bl_ok ← guard (base_lit_ok bl);
255 mret (VLit bl bl_ok)
256 | EId x mke =>
257 '(_,t) ← Res $ union_kinded (E !! x) (prod_map id (Thunk ∅) <$> mke);
258 interp_thunk t
259 | EAbs x e => mret (VClo x E e)
260 | EAbsMatch ms strict e => mret (VCloMatch E ms strict e)
261 | EApp e1 e2 =>
262 v1 ← interp E e1;
263 interp_app v1 (Thunk E e2)
264 | ESeq μ' e1 e2 =>
265 v ← interp E e1;
266 (if μ' is DEEP then force_deep else mret) v;;
267 interp E e2
268 | EList es => mret (VList (Thunk E <$> es))
269 | EAttr αs =>
270 let E' := indirects_env E (attr_to_tattr E <$> αs) in
271 mret (VAttr (from_attr (Thunk E') (Thunk E) <$> αs))
272 | ELetAttr k e1 e2 =>
273 v1 ← interp E e1;
274 ts ← Res (maybe VAttr v1);
275 interp (union_kinded ((k,.) <$> ts) E) e2
276 | EBinOp op e1 e2 =>
277 v1 ← interp E e1;
278 f ← Res (interp_bin_op op v1);
279 v2 ← interp E e2;
280 t2 ← Res (f v2);
281 interp_thunk t2
282 | EIf e1 e2 e3 =>
283 v1 ← interp E e1;
284 '(b : bool) ← Res (maybe_VLit v1 ≫= maybe LitBool);
285 interp E (if b then e2 else e3)
286 end.
287
288Definition interp_thunk1
289 (interp : env → expr → res val)
290 (interp_thunk : thunk → res val)
291 (t : thunk) : res val :=
292 match t with
293 | Forced v => mret v
294 | Thunk E e => interp E e
295 | Indirect x E tαs =>
296 tα ← Res $ tαs !! x;
297 match tα with
298 | inl e => interp (indirects_env E tαs) e
299 | inr t => interp_thunk t
300 end
301 end.
302
303Definition interp_app1
304 (interp : env → expr → res val)
305 (interp_thunk : thunk → res val)
306 (interp_app : val → thunk → res val)
307 (v1 : val) (t2 : thunk) : res val :=
308 match v1 with
309 | VClo x E e =>
310 interp (<[x:=(ABS, t2)]> E) e
311 | VCloMatch E ms strict e =>
312 vt ← interp_thunk t2;
313 ts ← Res (maybe VAttr vt);
314 tαs ← Res $ interp_match ts ms strict;
315 interp (indirects_env E tαs) e
316 | VAttr ts =>
317 t ← Res (ts !! "__functor");
318 vt ← interp_thunk t;
319 v ← interp_app vt (Forced v1);
320 interp_app v t2
321 | _ => mfail
322 end.
323
324Fixpoint force_deep (n : nat) (v : val) : res val :=
325 match n with
326 | O => NoFuel
327 | S n => force_deep1 (force_deep n) (interp_thunk n) v
328 end
329with interp (n : nat) (E : env) (e : expr) : res val :=
330 match n with
331 | O => NoFuel
332 | S n => interp1 (force_deep n) (interp n) (interp_thunk n) (interp_app n) E e
333 end
334with interp_thunk (n : nat) (t : thunk) : res val :=
335 match n with
336 | O => NoFuel
337 | S n => interp_thunk1 (interp n) (interp_thunk n) t
338 end
339with interp_app (n : nat) (v1 : val) (t2 : thunk) : res val :=
340 match n with
341 | O => NoFuel
342 | S n => interp_app1 (interp n) (interp_thunk n) (interp_app n) v1 t2
343 end.
344
345Definition force_deep' (n : nat) (μ : mode) : val → res val :=
346 match μ with SHALLOW => mret | DEEP => force_deep n end.
347
348Definition interp' (n : nat) (μ : mode) (E : env) (e : expr) : res val :=
349 interp n E e ≫= force_deep' n μ.
350
351Global Opaque force_deep interp interp_thunk interp_app.