diff options
Diffstat (limited to 'theories/nix/operational.v')
-rw-r--r-- | theories/nix/operational.v | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/theories/nix/operational.v b/theories/nix/operational.v new file mode 100644 index 0000000..d3f0777 --- /dev/null +++ b/theories/nix/operational.v | |||
@@ -0,0 +1,527 @@ | |||
1 | From mininix Require Export utils nix.floats. | ||
2 | From stdpp Require Import options. | ||
3 | |||
4 | (** Our development does not rely on a particular order on attribute set names. | ||
5 | It can be any decidable total order. We pick something concrete (lexicographic | ||
6 | order on strings) to avoid having to parametrize the whole development. *) | ||
7 | Definition attr_le := String.le. | ||
8 | Global Instance attr_le_dec : RelDecision attr_le := _. | ||
9 | Global Instance attr_le_po : PartialOrder attr_le := _. | ||
10 | Global Instance attr_le_total : Total attr_le := _. | ||
11 | Global Typeclasses Opaque attr_le. | ||
12 | |||
13 | Inductive mode := SHALLOW | DEEP. | ||
14 | Inductive kind := ABS | WITH. | ||
15 | Inductive rec := REC | NONREC. | ||
16 | |||
17 | Global Instance rec_eq_dec : EqDecision rec. | ||
18 | Proof. solve_decision. Defined. | ||
19 | |||
20 | Inductive num := | ||
21 | | NInt (n : Z) | ||
22 | | NFloat (f : float). | ||
23 | |||
24 | Inductive base_lit := | ||
25 | | LitNum (n : num) | ||
26 | | LitBool (b : bool) | ||
27 | | LitString (s : string) | ||
28 | | LitNull. | ||
29 | |||
30 | Global Instance num_inhabited : Inhabited num := populate (NInt 0). | ||
31 | Global Instance base_lit_inhabited : Inhabited base_lit := populate LitNull. | ||
32 | |||
33 | Global Instance num_eq_dec : EqDecision num. | ||
34 | Proof. solve_decision. Defined. | ||
35 | Global Instance base_lit_eq_dec : EqDecision base_lit. | ||
36 | Proof. solve_decision. Defined. | ||
37 | |||
38 | Global Instance maybe_NInt : Maybe NInt := λ n, | ||
39 | if n is NInt i then Some i else None. | ||
40 | Global Instance maybe_NFloat : Maybe NFloat := λ n, | ||
41 | if n is NFloat f then Some f else None. | ||
42 | Global Instance maybe_LitNum : Maybe LitNum := λ bl, | ||
43 | if bl is LitNum n then Some n else None. | ||
44 | Global Instance maybe_LitBool : Maybe LitBool := λ bl, | ||
45 | if bl is LitBool b then Some b else None. | ||
46 | Global Instance maybe_LitString : Maybe LitString := λ bl, | ||
47 | if bl is LitString s then Some s else None. | ||
48 | |||
49 | Inductive bin_op : Set := | ||
50 | | AddOp | SubOp | MulOp | DivOp | AndOp | OrOp | XOrOp (* Arithmetic *) | ||
51 | | LtOp | EqOp (* Relations *) | ||
52 | | RoundOp (m : round_mode) (* Conversions *) | ||
53 | | MatchStringOp (* Strings *) | ||
54 | | MatchListOp | AppendListOp (* Lists *) | ||
55 | | SelectAttrOp | UpdateAttrOp | HasAttrOp | ||
56 | | DeleteAttrOp | SingletonAttrOp | MatchAttrOp (* Attribute sets *) | ||
57 | | FunctionArgsOp | TypeOfOp. | ||
58 | |||
59 | Global Instance bin_op_eq_dec : EqDecision bin_op. | ||
60 | Proof. solve_decision. Defined. | ||
61 | |||
62 | Global Instance maybe_RoundOp : Maybe RoundOp := λ op, | ||
63 | if op is RoundOp m then Some m else None. | ||
64 | |||
65 | Section expr. | ||
66 | Local Unset Elimination Schemes. | ||
67 | Inductive expr := | ||
68 | | ELit (bl : base_lit) | ||
69 | | EId (x : string) (mke : option (kind * expr)) | ||
70 | | EAbs (x : string) (e : expr) | ||
71 | | EAbsMatch (ms : gmap string (option expr)) (strict : bool) (e : expr) | ||
72 | | EApp (e1 e2 : expr) | ||
73 | | ESeq (μ : mode) (e1 e2 : expr) | ||
74 | | EList (es : list expr) | ||
75 | | EAttr (αs : gmap string attr) | ||
76 | | ELetAttr (k : kind) (e1 e2 : expr) | ||
77 | | EBinOp (op : bin_op) (e1 e2 : expr) | ||
78 | | EIf (e1 e2 e3 : expr) | ||
79 | with attr := | ||
80 | | Attr (τ : rec) (e : expr). | ||
81 | End expr. | ||
82 | |||
83 | Definition EId' x := EId x None. | ||
84 | Notation AttrR := (Attr REC). | ||
85 | Notation AttrN := (Attr NONREC). | ||
86 | Notation ESelect e x := (EBinOp SelectAttrOp e (ELit (LitString x))). | ||
87 | Notation ELet x e := (ELetAttr ABS (EAttr {[ x := AttrN e ]})). | ||
88 | Notation EWith := (ELetAttr WITH). | ||
89 | |||
90 | Definition attr_expr (α : attr) : expr := match α with Attr _ e => e end. | ||
91 | Definition attr_rec (α : attr) : rec := match α with Attr μ _ => μ end. | ||
92 | Definition attr_map (f : expr → expr) (α : attr) : attr := | ||
93 | match α with Attr μ e => Attr μ (f e) end. | ||
94 | |||
95 | Definition from_attr {A} (f g : expr → A) (α : attr) : A := | ||
96 | match α with AttrR e => f e | AttrN e => g e end. | ||
97 | |||
98 | Definition merge_kinded {A} (new old : kind * A) : option (kind * A) := | ||
99 | match new.1, old.1 with | ||
100 | | WITH, ABS => Some old | ||
101 | | _, _ => Some new | ||
102 | end. | ||
103 | Arguments merge_kinded {_} !_ !_ / : simpl nomatch. | ||
104 | Notation union_kinded := (union_with merge_kinded). | ||
105 | |||
106 | Definition no_recs : gmap string attr → Prop := | ||
107 | map_Forall (λ _ α, attr_rec α = NONREC). | ||
108 | |||
109 | Definition indirects (αs : gmap string attr) : gmap string (kind * expr) := | ||
110 | map_imap (λ x _, Some (ABS, ESelect (EAttr αs) x)) αs. | ||
111 | |||
112 | Fixpoint subst (ds : gmap string (kind * expr)) (e : expr) : expr := | ||
113 | match e with | ||
114 | | ELit b => ELit b | ||
115 | | EId x mkd => EId x $ union_kinded (ds !! x) mkd | ||
116 | | EAbs x e => EAbs x (subst ds e) | ||
117 | | EAbsMatch ms strict e => | ||
118 | EAbsMatch (fmap (M:=option) (subst ds) <$> ms) strict (subst ds e) | ||
119 | | EApp e1 e2 => EApp (subst ds e1) (subst ds e2) | ||
120 | | ESeq μ e1 e2 => ESeq μ (subst ds e1) (subst ds e2) | ||
121 | | EList es => EList (subst ds <$> es) | ||
122 | | EAttr αs => EAttr (attr_map (subst ds) <$> αs) | ||
123 | | ELetAttr k e1 e2 => ELetAttr k (subst ds e1) (subst ds e2) | ||
124 | | EBinOp op e1 e2 => EBinOp op (subst ds e1) (subst ds e2) | ||
125 | | EIf e1 e2 e3 => EIf (subst ds e1) (subst ds e2) (subst ds e3) | ||
126 | end. | ||
127 | |||
128 | Notation attr_subst ds := (attr_map (subst ds)). | ||
129 | |||
130 | Definition int_min : Z := -(1 ≪ 63). | ||
131 | Definition int_max : Z := 1 ≪ 63 - 1. | ||
132 | |||
133 | Definition int_ok (i : Z) : bool := bool_decide (int_min ≤ i ≤ int_max)%Z. | ||
134 | Definition num_ok (n : num) : bool := | ||
135 | match n with NInt i => int_ok i | _ => true end. | ||
136 | Definition base_lit_ok (bl : base_lit) : bool := | ||
137 | match bl with LitNum n => num_ok n | _ => true end. | ||
138 | |||
139 | Inductive final : mode → expr → Prop := | ||
140 | | ELitFinal μ bl : base_lit_ok bl → final μ (ELit bl) | ||
141 | | EAbsFinal μ x e : final μ (EAbs x e) | ||
142 | | EAbsMatchFinal μ ms strict e : final μ (EAbsMatch ms strict e) | ||
143 | | EListShallowFinal es : final SHALLOW (EList es) | ||
144 | | EListDeepFinal es : Forall (final DEEP) es → final DEEP (EList es) | ||
145 | | EAttrShallowFinal αs : no_recs αs → final SHALLOW (EAttr αs) | ||
146 | | EAttrDeepFinal αs : | ||
147 | no_recs αs → | ||
148 | map_Forall (λ _, final DEEP ∘ attr_expr) αs → | ||
149 | final DEEP (EAttr αs). | ||
150 | |||
151 | Fixpoint sem_eq_list (es1 es2 : list expr) : expr := | ||
152 | match es1, es2 with | ||
153 | | [], [] => ELit (LitBool true) | ||
154 | | e1 :: es1, e2 :: es2 => | ||
155 | EIf (EBinOp EqOp e1 e2) (sem_eq_list es1 es2) (ELit (LitBool false)) | ||
156 | | _, _ => ELit (LitBool false) | ||
157 | end. | ||
158 | |||
159 | Fixpoint sem_lt_list (es1 es2 : list expr) : expr := | ||
160 | match es1, es2 with | ||
161 | | [], _ => ELit (LitBool true) | ||
162 | | e1 :: es1, e2 :: es2 => | ||
163 | EIf (EBinOp LtOp e1 e2) (ELit (LitBool true)) $ | ||
164 | EIf (EBinOp EqOp e1 e2) (sem_lt_list es1 es2) (ELit (LitBool false)) | ||
165 | | _ :: _, [] => ELit (LitBool false) | ||
166 | end. | ||
167 | |||
168 | Definition sem_and_attr (es : gmap string expr) : expr := | ||
169 | map_fold_sorted attr_le | ||
170 | (λ _ e1 e2, EIf e1 e2 (ELit (LitBool false))) | ||
171 | (ELit (LitBool true)) es. | ||
172 | |||
173 | Definition sem_eq_attr (αs1 αs2 : gmap string attr) : expr := | ||
174 | sem_and_attr $ merge (λ mα1 mα2, | ||
175 | α1 ← mα1; α2 ← mα2; Some (EBinOp EqOp (attr_expr α1) (attr_expr α2))) αs1 αs2. | ||
176 | |||
177 | Definition num_to_float (n : num) : float := | ||
178 | match n with | ||
179 | | NInt i => Float.of_Z i | ||
180 | | NFloat f => f | ||
181 | end. | ||
182 | |||
183 | Definition sem_bin_op_lift | ||
184 | (fint : Z → Z → Z) (ffloat : float → float → float) | ||
185 | (n1 n2 : num) : option num := | ||
186 | match n1, n2 with | ||
187 | | NInt i1, NInt i2 => | ||
188 | let i := fint i1 i2 in | ||
189 | guard (int_ok i);; | ||
190 | Some (NInt i) | ||
191 | | _, _ => Some $ NFloat $ ffloat (num_to_float n1) (num_to_float n2) | ||
192 | end. | ||
193 | |||
194 | Definition sem_bin_rel_lift | ||
195 | (fint : Z → Z → bool) (ffloat : float → float → bool) | ||
196 | (n1 n2 : num) : bool := | ||
197 | match n1, n2 with | ||
198 | | NInt i1, NInt i2 => fint i1 i2 | ||
199 | | _, _ => ffloat (num_to_float n1) (num_to_float n2) | ||
200 | end. | ||
201 | |||
202 | Definition sem_eq_base_lit (bl1 bl2 : base_lit) : bool := | ||
203 | match bl1, bl2 with | ||
204 | | LitNum n1, LitNum n2 => sem_bin_rel_lift Z.eqb Float.eqb n1 n2 | ||
205 | | LitBool b1, LitBool b2 => bool_decide (b1 = b2) | ||
206 | | LitString s1, LitString s2 => bool_decide (s1 = s2) | ||
207 | | LitNull, LitNull => true | ||
208 | | _, _ => false | ||
209 | end. | ||
210 | |||
211 | (** Precondition e1 and e2 are final *) | ||
212 | Definition sem_eq (e1 e2 : expr) : option expr := | ||
213 | match e1, e2 with | ||
214 | | ELit bl1, ELit bl2 => Some $ ELit (LitBool (sem_eq_base_lit bl1 bl2)) | ||
215 | | EAbs _ _, EAbs _ _ => None | ||
216 | | EList es1, EList es2 => Some $ | ||
217 | if decide (length es1 = length es2) then sem_eq_list es1 es2 | ||
218 | else ELit $ LitBool false | ||
219 | | EAttr αs1, EAttr αs2 => Some $ | ||
220 | if decide (dom αs1 = dom αs2) then sem_eq_attr αs1 αs2 | ||
221 | else ELit $ LitBool false | ||
222 | | _, _ => Some $ ELit (LitBool false) | ||
223 | end. | ||
224 | |||
225 | Definition div_allowed (n : num) : bool := | ||
226 | match n with | ||
227 | | NInt n => bool_decide (n ≠ 0%Z) | ||
228 | | NFloat f => negb (Float.eqb f (Float.of_Z 0)) (* TODO: Check NaNs *) | ||
229 | end. | ||
230 | |||
231 | Definition sem_bin_op_num (op : bin_op) (n1 : num) : option (num → option base_lit) := | ||
232 | match op with | ||
233 | | AddOp => Some $ λ n2, | ||
234 | LitNum <$> sem_bin_op_lift Z.add Float.add n1 n2 | ||
235 | | SubOp => Some $ λ n2, | ||
236 | LitNum <$> sem_bin_op_lift Z.sub Float.sub n1 n2 | ||
237 | | MulOp => Some $ λ n2, | ||
238 | LitNum <$> sem_bin_op_lift Z.mul Float.mul n1 n2 | ||
239 | | DivOp => Some $ λ n2, | ||
240 | (* Quot can overflow: [MIN_INT `quot` -1] equals [MAX_INT + 1] *) | ||
241 | guard (div_allowed n2);; | ||
242 | LitNum <$> sem_bin_op_lift Z.quot Float.div n1 n2 | ||
243 | | AndOp => | ||
244 | i1 ← maybe NInt n1; | ||
245 | Some $ λ n2, i2 ← maybe NInt n2; | ||
246 | Some $ LitNum $ NInt $ Z.land i1 i2 | ||
247 | | OrOp => | ||
248 | i1 ← maybe NInt n1; | ||
249 | Some $ λ n2, i2 ← maybe NInt n2; | ||
250 | Some $ LitNum $ NInt $ Z.lor i1 i2 | ||
251 | | XOrOp => | ||
252 | i1 ← maybe NInt n1; | ||
253 | Some $ λ n2, i2 ← maybe NInt n2; | ||
254 | Some $ LitNum $ NInt $ Z.lxor i1 i2 | ||
255 | | LtOp => Some $ λ n2, | ||
256 | Some $ LitBool (sem_bin_rel_lift Z.ltb Float.ltb n1 n2) | ||
257 | | _ => None | ||
258 | end%Z. | ||
259 | |||
260 | Definition sem_bin_op_string (op : bin_op) : option (string → string → base_lit) := | ||
261 | match op with | ||
262 | | AddOp => Some $ λ s1 s2, LitString (s1 +:+ s2) | ||
263 | | LtOp => Some $ λ s1 s2, LitBool (bool_decide (strict attr_le s1 s2)) | ||
264 | | _ => None | ||
265 | end. | ||
266 | |||
267 | Definition type_of_num (n : num) : string := | ||
268 | match n with | ||
269 | | NInt _ => "int" | ||
270 | | NFloat _ => "float" | ||
271 | end. | ||
272 | |||
273 | Definition type_of_base_lit (bl : base_lit) : string := | ||
274 | match bl with | ||
275 | | LitNum n => type_of_num n | ||
276 | | LitBool _ => "bool" | ||
277 | | LitString _ => "string" | ||
278 | | LitNull => "null" | ||
279 | end. | ||
280 | |||
281 | Definition type_of_expr (e : expr) := | ||
282 | match e with | ||
283 | | ELit bl => Some (type_of_base_lit bl) | ||
284 | | EAbs _ _ | EAbsMatch _ _ _ => Some "lambda" | ||
285 | | EList _ => Some "list" | ||
286 | | EAttr _ => Some "set" | ||
287 | | _ => None | ||
288 | end. | ||
289 | |||
290 | (* Used for [RoundOp] *) | ||
291 | Definition float_to_bounded_Z (f : float) : Z := | ||
292 | match Float.to_Z f with | ||
293 | | Some x => if decide (int_ok x) then x else int_min | ||
294 | | None => int_min | ||
295 | end. | ||
296 | |||
297 | Inductive sem_bin_op : bin_op → expr → (expr → expr → Prop) → Prop := | ||
298 | | EqSem e1 : | ||
299 | sem_bin_op EqOp e1 (λ e2 e, sem_eq e1 e2 = Some e) | ||
300 | | LitNumSem op n1 f : | ||
301 | sem_bin_op_num op n1 = Some f → | ||
302 | sem_bin_op op (ELit (LitNum n1)) (λ e2 e, ∃ n2 bl, | ||
303 | e2 = ELit (LitNum n2) ∧ f n2 = Some bl ∧ e = ELit bl) | ||
304 | | RoundSem m n1 : | ||
305 | sem_bin_op (RoundOp m) (ELit (LitNum n1)) (λ e2 e, | ||
306 | e2 = ELit LitNull ∧ | ||
307 | e = ELit $ LitNum $ NInt $ float_to_bounded_Z $ Float.round m $ num_to_float n1) | ||
308 | | LitStringSem op s1 f : | ||
309 | sem_bin_op_string op = Some f → | ||
310 | sem_bin_op op (ELit (LitString s1)) (λ e2 e, ∃ s2, | ||
311 | e2 = ELit (LitString s2) ∧ e = ELit (f s1 s2)) | ||
312 | | MatchStringSem s : | ||
313 | sem_bin_op MatchStringOp (ELit (LitString s)) (λ e2 e, | ||
314 | e2 = ELit LitNull ∧ | ||
315 | match s with | ||
316 | | EmptyString => e = EAttr {[ | ||
317 | "empty" := AttrN (ELit (LitBool true)); | ||
318 | "head" := AttrN (ELit LitNull); | ||
319 | "tail" := AttrN (ELit LitNull) ]} | ||
320 | | String a s => e = EAttr {[ | ||
321 | "empty" := AttrN (ELit (LitBool false)); | ||
322 | "head" := AttrN (ELit (LitString (String a EmptyString))); | ||
323 | "tail" := AttrN (ELit (LitString s)) ]} | ||
324 | end) | ||
325 | | LtListSem es : | ||
326 | sem_bin_op LtOp (EList es) (λ e2 e, ∃ es', | ||
327 | e2 = EList es' ∧ | ||
328 | e = sem_lt_list es es') | ||
329 | | MatchListSem es : | ||
330 | sem_bin_op MatchListOp (EList es) (λ e2 e, | ||
331 | e2 = ELit LitNull ∧ | ||
332 | match es with | ||
333 | | [] => e = EAttr {[ | ||
334 | "empty" := AttrN (ELit (LitBool true)); | ||
335 | "head" := AttrN (ELit LitNull); | ||
336 | "tail" := AttrN (ELit LitNull) ]} | ||
337 | | e' :: es => e = EAttr {[ | ||
338 | "empty" := AttrN (ELit (LitBool false)); | ||
339 | "head" := AttrN e'; | ||
340 | "tail" := AttrN (EList es) ]} | ||
341 | end) | ||
342 | | AppendListSem es : | ||
343 | sem_bin_op AppendListOp (EList es) (λ e2 e, ∃ es', | ||
344 | e2 = EList es' ∧ | ||
345 | e = EList (es ++ es')) | ||
346 | | SelectAttrSem αs : | ||
347 | no_recs αs → | ||
348 | sem_bin_op SelectAttrOp (EAttr αs) (λ e2 e, ∃ x, | ||
349 | e2 = ELit (LitString x) ∧ αs !! x = Some (AttrN e)) | ||
350 | | UpdateAttrSem αs1 : | ||
351 | no_recs αs1 → | ||
352 | sem_bin_op UpdateAttrOp (EAttr αs1) (λ e2 e, ∃ αs2, | ||
353 | e2 = EAttr αs2 ∧ no_recs αs2 ∧ e = EAttr (αs2 ∪ αs1)) | ||
354 | | HasAttrSem αs : | ||
355 | no_recs αs → | ||
356 | sem_bin_op HasAttrOp (EAttr αs) (λ e2 e, ∃ x, | ||
357 | e2 = ELit (LitString x) ∧ e = ELit (LitBool (bool_decide (is_Some (αs !! x))))) | ||
358 | | DeleteAttrSem αs : | ||
359 | no_recs αs → | ||
360 | sem_bin_op DeleteAttrOp (EAttr αs) (λ e2 e, ∃ x, | ||
361 | e2 = ELit (LitString x) ∧ e = EAttr (delete x αs)) | ||
362 | | SingletonAttrSem x : | ||
363 | sem_bin_op SingletonAttrOp (ELit (LitString x)) (λ e2 e, | ||
364 | e2 = ELit LitNull ∧ | ||
365 | e = EAbs "t" (EAttr {[ x := AttrN (EId' "t") ]})) | ||
366 | | MatchAttrSem αs : | ||
367 | no_recs αs → | ||
368 | sem_bin_op MatchAttrOp (EAttr αs) (λ e2 e, | ||
369 | e2 = ELit LitNull ∧ | ||
370 | ((αs = ∅ ∧ | ||
371 | e = EAttr {[ | ||
372 | "empty" := AttrN (ELit (LitBool true)); | ||
373 | "key" := AttrN (ELit LitNull); | ||
374 | "head" := AttrN (ELit LitNull); | ||
375 | "tail" := AttrN (ELit LitNull) ]}) ∨ | ||
376 | (∃ x e', | ||
377 | αs !! x = Some (AttrN e') ∧ | ||
378 | (∀ y, is_Some (αs !! y) → attr_le x y) ∧ | ||
379 | e = EAttr {[ | ||
380 | "empty" := AttrN (ELit (LitBool false)); | ||
381 | "key" := AttrN (ELit (LitString x)); | ||
382 | "head" := AttrN e'; | ||
383 | "tail" := AttrN (EAttr (delete x αs)) ]}))) | ||
384 | | FunctionArgsAbsSem x e' : | ||
385 | sem_bin_op FunctionArgsOp (EAbs x e') (λ e2 e, | ||
386 | e2 = ELit LitNull ∧ | ||
387 | e = EAttr ∅) | ||
388 | | FunctionArgsAbsMatchSem ms strict e' : | ||
389 | sem_bin_op FunctionArgsOp (EAbsMatch ms strict e') (λ e2 e, | ||
390 | e2 = ELit LitNull ∧ | ||
391 | e = EAttr (AttrN ∘ ELit ∘ LitBool ∘ from_option (λ _, true) false <$> ms)) | ||
392 | | TypeOfSem e1 : | ||
393 | sem_bin_op TypeOfOp e1 (λ e2 e, ∃ x, | ||
394 | e2 = ELit LitNull ∧ | ||
395 | type_of_expr e1 = Some x ∧ | ||
396 | e = ELit (LitString x)). | ||
397 | |||
398 | Inductive matches : | ||
399 | gmap string expr → gmap string (option expr) → bool → gmap string attr → Prop := | ||
400 | | MatchEmpty strict : | ||
401 | matches ∅ ∅ strict ∅ | ||
402 | | MatchAny es : | ||
403 | matches es ∅ false ∅ | ||
404 | | MatchAvail x e es ms md strict βs : | ||
405 | es !! x = None → | ||
406 | ms !! x = None → | ||
407 | matches es ms strict βs → | ||
408 | matches (<[x:=e]> es) (<[x:=md]> ms) strict (<[x:=AttrN e]> βs) | ||
409 | | MatchOptDefault x es ms d strict βs : | ||
410 | es !! x = None → | ||
411 | ms !! x = None → | ||
412 | matches es ms strict βs → | ||
413 | matches es (<[x:=Some d]> ms) strict (<[x:=AttrR d]> βs). | ||
414 | |||
415 | Reserved Notation "e1 -{ μ }-> e2" | ||
416 | (right associativity, at level 55, μ at level 1, format "e1 -{ μ }-> e2"). | ||
417 | |||
418 | Inductive ctx1 : mode → mode → (expr → expr) → Prop := | ||
419 | | CList es1 es2 : | ||
420 | Forall (final DEEP) es1 → | ||
421 | ctx1 DEEP DEEP (λ e, EList (es1 ++ e :: es2)) | ||
422 | | CAttr αs x : | ||
423 | no_recs αs → | ||
424 | αs !! x = None → | ||
425 | (∀ y α, αs !! y = Some α → final DEEP (attr_expr α) ∨ attr_le x y) → | ||
426 | ctx1 DEEP DEEP (λ e, EAttr (<[x:=AttrN e]> αs)) | ||
427 | | CAppL μ e2 : | ||
428 | ctx1 SHALLOW μ (λ e1, EApp e1 e2) | ||
429 | | CAppR μ ms strict e1 : | ||
430 | ctx1 SHALLOW μ (EApp (EAbsMatch ms strict e1)) | ||
431 | | CSeq μ μ' e2 : | ||
432 | ctx1 μ' μ (λ e1, ESeq μ' e1 e2) | ||
433 | | CLetAttr μ k e2 : | ||
434 | ctx1 SHALLOW μ (λ e1, ELetAttr k e1 e2) | ||
435 | | CBinOpL μ op e2 : | ||
436 | ctx1 SHALLOW μ (λ e1, EBinOp op e1 e2) | ||
437 | | CBinOpR μ op e1 Φ : | ||
438 | final SHALLOW e1 → | ||
439 | sem_bin_op op e1 Φ → | ||
440 | ctx1 SHALLOW μ (EBinOp op e1) | ||
441 | | CIf μ e2 e3 : | ||
442 | ctx1 SHALLOW μ (λ e1, EIf e1 e2 e3). | ||
443 | |||
444 | Inductive step : mode → relation expr := | ||
445 | | Sβ μ x e1 e2 : | ||
446 | EApp (EAbs x e1) e2 -{μ}-> subst {[x:=(ABS, e2)]} e1 | ||
447 | | SβMatch μ ms strict e1 αs βs : | ||
448 | no_recs αs → | ||
449 | matches (attr_expr <$> αs) ms strict βs → | ||
450 | EApp (EAbsMatch ms strict e1) (EAttr αs) -{μ}-> | ||
451 | subst (indirects βs) e1 | ||
452 | | SFunctor μ αs e1 e2 : | ||
453 | no_recs αs → | ||
454 | αs !! "__functor" = Some (AttrN e1) → | ||
455 | EApp (EAttr αs) e2 -{μ}-> EApp (EApp e1 (EAttr αs)) e2 | ||
456 | | SSeqFinal μ μ' e1 e2 : | ||
457 | final μ' e1 → ESeq μ' e1 e2 -{μ}-> e2 | ||
458 | | SLetAttrAttr μ k αs e : | ||
459 | no_recs αs → | ||
460 | ELetAttr k (EAttr αs) e -{μ}-> subst ((k,.) ∘ attr_expr <$> αs) e | ||
461 | | SAttr_rec μ αs : | ||
462 | ¬no_recs αs → | ||
463 | EAttr αs -{μ}-> | ||
464 | EAttr (AttrN ∘ from_attr (subst (indirects αs)) id <$> αs) | ||
465 | | SBinOp μ op e1 Φ e2 e : | ||
466 | final SHALLOW e1 → | ||
467 | final SHALLOW e2 → | ||
468 | sem_bin_op op e1 Φ → Φ e2 e → | ||
469 | EBinOp op e1 e2 -{μ}-> e | ||
470 | | SIfBool μ b e2 e3 : | ||
471 | EIf (ELit (LitBool b)) e2 e3 -{μ}-> if b then e2 else e3 | ||
472 | | SId μ x k e : | ||
473 | EId x (Some (k,e)) -{μ}-> e | ||
474 | | SCtx K μ μ' e e' : | ||
475 | ctx1 μ μ' K → e -{μ}-> e' → K e -{μ'}-> K e' | ||
476 | where "e1 -{ μ }-> e2" := (step μ e1 e2). | ||
477 | |||
478 | Notation "e1 -{ μ }->* e2" := (rtc (step μ) e1 e2) | ||
479 | (right associativity, at level 55, μ at level 1, format "e1 -{ μ }->* e2"). | ||
480 | Notation "e1 -{ μ }->+ e2" := (tc (step μ) e1 e2) | ||
481 | (right associativity, at level 55, μ at level 1, format "e1 -{ μ }->+ e2"). | ||
482 | |||
483 | Definition stuck (μ : mode) (e : expr) : Prop := | ||
484 | nf (step μ) e ∧ ¬final μ e. | ||
485 | |||
486 | (** Induction *) | ||
487 | Fixpoint expr_size (e : expr) : nat := | ||
488 | match e with | ||
489 | | ELit _ => 1 | ||
490 | | EId _ mkd => S (from_option (expr_size ∘ snd) 1 mkd) | ||
491 | | EAbs _ d => S (expr_size d) | ||
492 | | EAbsMatch ms _ e => | ||
493 | S (map_sum_with (from_option expr_size 1) ms + expr_size e) | ||
494 | | EApp e1 e2 | ESeq _ e1 e2 => S (expr_size e1 + expr_size e2) | ||
495 | | EList es => S (sum_list_with expr_size es) | ||
496 | | EAttr eτs => S (map_sum_with (expr_size ∘ attr_expr) eτs) | ||
497 | | ELetAttr _ e1 e2 => S (expr_size e1 + expr_size e2) | ||
498 | | EBinOp _ e1 e2 => S (expr_size e1 + expr_size e2) | ||
499 | | EIf e1 e2 e3 => S (expr_size e1 + expr_size e2 + expr_size e3) | ||
500 | end. | ||
501 | |||
502 | Lemma expr_ind (P : expr → Prop) : | ||
503 | (∀ b, P (ELit b)) → | ||
504 | (∀ x mkd, from_option (P ∘ snd) True mkd → P (EId x mkd)) → | ||
505 | (∀ x e, P e → P (EAbs x e)) → | ||
506 | (∀ ms strict e, | ||
507 | map_Forall (λ _, from_option P True) ms → P e → P (EAbsMatch ms strict e)) → | ||
508 | (∀ e1 e2, P e1 → P e2 → P (EApp e1 e2)) → | ||
509 | (∀ μ e1 e2, P e1 → P e2 → P (ESeq μ e1 e2)) → | ||
510 | (∀ es, Forall P es → P (EList es)) → | ||
511 | (∀ αs, map_Forall (λ _, P ∘ attr_expr) αs → P (EAttr αs)) → | ||
512 | (∀ k e1 e2, P e1 → P e2 → P (ELetAttr k e1 e2)) → | ||
513 | (∀ op e1 e2, P e1 → P e2 → P (EBinOp op e1 e2)) → | ||
514 | (∀ e1 e2 e3, P e1 → P e2 → P e3 → P (EIf e1 e2 e3)) → | ||
515 | ∀ e, P e. | ||
516 | Proof. | ||
517 | intros Hlit Hid Habs Hmatch Happ Hseq Hlist Hattr Hlet Hop Hif e. | ||
518 | induction (Nat.lt_wf_0_projected expr_size e) as [e _ IH]. | ||
519 | destruct e; repeat destruct select (option _); simpl in *; eauto with lia. | ||
520 | - apply Hmatch; [|by eauto with lia]=> y [e'|] Hx //=. apply IH, Nat.lt_succ_r. | ||
521 | etrans; [|apply Nat.le_add_r]. | ||
522 | eapply (map_sum_with_lookup_le (from_option expr_size 1) _ _ _ Hx). | ||
523 | - apply Hlist, Forall_forall=> e ?. apply IH, Nat.lt_succ_r. | ||
524 | by apply sum_list_with_in. | ||
525 | - apply Hattr, map_Forall_lookup=> y e ?. apply IH, Nat.lt_succ_r. | ||
526 | by eapply (map_sum_with_lookup_le (expr_size ∘ attr_expr)). | ||
527 | Qed. | ||