aboutsummaryrefslogtreecommitdiffstats
path: root/theories/nix/operational.v
diff options
context:
space:
mode:
Diffstat (limited to 'theories/nix/operational.v')
-rw-r--r--theories/nix/operational.v527
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 @@
1From mininix Require Export utils nix.floats.
2From stdpp Require Import options.
3
4(** Our development does not rely on a particular order on attribute set names.
5It can be any decidable total order. We pick something concrete (lexicographic
6order on strings) to avoid having to parametrize the whole development. *)
7Definition attr_le := String.le.
8Global Instance attr_le_dec : RelDecision attr_le := _.
9Global Instance attr_le_po : PartialOrder attr_le := _.
10Global Instance attr_le_total : Total attr_le := _.
11Global Typeclasses Opaque attr_le.
12
13Inductive mode := SHALLOW | DEEP.
14Inductive kind := ABS | WITH.
15Inductive rec := REC | NONREC.
16
17Global Instance rec_eq_dec : EqDecision rec.
18Proof. solve_decision. Defined.
19
20Inductive num :=
21 | NInt (n : Z)
22 | NFloat (f : float).
23
24Inductive base_lit :=
25 | LitNum (n : num)
26 | LitBool (b : bool)
27 | LitString (s : string)
28 | LitNull.
29
30Global Instance num_inhabited : Inhabited num := populate (NInt 0).
31Global Instance base_lit_inhabited : Inhabited base_lit := populate LitNull.
32
33Global Instance num_eq_dec : EqDecision num.
34Proof. solve_decision. Defined.
35Global Instance base_lit_eq_dec : EqDecision base_lit.
36Proof. solve_decision. Defined.
37
38Global Instance maybe_NInt : Maybe NInt := λ n,
39 if n is NInt i then Some i else None.
40Global Instance maybe_NFloat : Maybe NFloat := λ n,
41 if n is NFloat f then Some f else None.
42Global Instance maybe_LitNum : Maybe LitNum := λ bl,
43 if bl is LitNum n then Some n else None.
44Global Instance maybe_LitBool : Maybe LitBool := λ bl,
45 if bl is LitBool b then Some b else None.
46Global Instance maybe_LitString : Maybe LitString := λ bl,
47 if bl is LitString s then Some s else None.
48
49Inductive 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
59Global Instance bin_op_eq_dec : EqDecision bin_op.
60Proof. solve_decision. Defined.
61
62Global Instance maybe_RoundOp : Maybe RoundOp := λ op,
63 if op is RoundOp m then Some m else None.
64
65Section 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).
81End expr.
82
83Definition EId' x := EId x None.
84Notation AttrR := (Attr REC).
85Notation AttrN := (Attr NONREC).
86Notation ESelect e x := (EBinOp SelectAttrOp e (ELit (LitString x))).
87Notation ELet x e := (ELetAttr ABS (EAttr {[ x := AttrN e ]})).
88Notation EWith := (ELetAttr WITH).
89
90Definition attr_expr (α : attr) : expr := match α with Attr _ e => e end.
91Definition attr_rec (α : attr) : rec := match α with Attr μ _ => μ end.
92Definition attr_map (f : expr → expr) (α : attr) : attr :=
93 match α with Attr μ e => Attr μ (f e) end.
94
95Definition from_attr {A} (f g : expr → A) (α : attr) : A :=
96 match α with AttrR e => f e | AttrN e => g e end.
97
98Definition 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.
103Arguments merge_kinded {_} !_ !_ / : simpl nomatch.
104Notation union_kinded := (union_with merge_kinded).
105
106Definition no_recs : gmap string attr → Prop :=
107 map_Forall (λ _ α, attr_rec α = NONREC).
108
109Definition indirects (αs : gmap string attr) : gmap string (kind * expr) :=
110 map_imap (λ x _, Some (ABS, ESelect (EAttr αs) x)) αs.
111
112Fixpoint 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
128Notation attr_subst ds := (attr_map (subst ds)).
129
130Definition int_min : Z := -(1 ≪ 63).
131Definition int_max : Z := 1 ≪ 63 - 1.
132
133Definition int_ok (i : Z) : bool := bool_decide (int_min ≤ i ≤ int_max)%Z.
134Definition num_ok (n : num) : bool :=
135 match n with NInt i => int_ok i | _ => true end.
136Definition base_lit_ok (bl : base_lit) : bool :=
137 match bl with LitNum n => num_ok n | _ => true end.
138
139Inductive 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
151Fixpoint 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
159Fixpoint 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
168Definition 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
173Definition 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
177Definition num_to_float (n : num) : float :=
178 match n with
179 | NInt i => Float.of_Z i
180 | NFloat f => f
181 end.
182
183Definition 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
194Definition 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
202Definition 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 *)
212Definition 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
225Definition 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
231Definition 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
260Definition 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
267Definition type_of_num (n : num) : string :=
268 match n with
269 | NInt _ => "int"
270 | NFloat _ => "float"
271 end.
272
273Definition 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
281Definition 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] *)
291Definition 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
297Inductive 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
398Inductive 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
415Reserved Notation "e1 -{ μ }-> e2"
416 (right associativity, at level 55, μ at level 1, format "e1 -{ μ }-> e2").
417
418Inductive 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
444Inductive 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'
476where "e1 -{ μ }-> e2" := (step μ e1 e2).
477
478Notation "e1 -{ μ }->* e2" := (rtc (step μ) e1 e2)
479 (right associativity, at level 55, μ at level 1, format "e1 -{ μ }->* e2").
480Notation "e1 -{ μ }->+ e2" := (tc (step μ) e1 e2)
481 (right associativity, at level 55, μ at level 1, format "e1 -{ μ }->+ e2").
482
483Definition stuck (μ : mode) (e : expr) : Prop :=
484 nf (step μ) e ∧ ¬final μ e.
485
486(** Induction *)
487Fixpoint 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
502Lemma 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.
516Proof.
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)).
527Qed.