| abbrev Var := String | |
| inductive Val where | |
| | int (i : Int) | |
| | bool (b : Bool) | |
| deriving DecidableEq, Repr | |
| instance : Coe Bool Val where | |
| coe b := .bool b | |
| instance : NatCast Val where | |
| natCast i := .int i | |
| instance : IntCast Val where | |
| intCast i := .int i | |
| instance : OfNat Val n where | |
| ofNat := .int n | |
| #check (true : Val) | |
| #check (0 : Val) | |
| inductive BinOp where | |
| | eq | and | lt | add | sub | |
| deriving Repr | |
| inductive UnaryOp where | |
| | not | |
| deriving Repr | |
| inductive Expr where | |
| | val (v : Val) | |
| | var (x : Var) | |
| | bin (lhs : Expr) (op : BinOp) (rhs : Expr) | |
| | una (op : UnaryOp) (arg : Expr) | |
| deriving Repr | |
| instance : Coe Val Expr where | |
| coe v := .val v | |
| instance : Coe Var Expr where | |
| coe x := .var x | |
| instance : OfNat Expr n where | |
| ofNat := .val n | |
| @[simp] def BinOp.eval : BinOp → Val → Val → Option Val | |
| | .eq, v₁, v₂ => some (.bool (v₁ = v₂)) | |
| | .and, .bool b₁, .bool b₂ => some (.bool (b₁ && b₂)) | |
| | .lt, .int i₁, .int i₂ => some (.bool (i₁ < i₂)) | |
| | .add, .int i₁, .int i₂ => some (.int (i₁ + i₂)) | |
| | .sub, .int i₁, .int i₂ => some (.int (i₁ - i₂)) | |
| | _, _, _ => none | |
| @[simp] def UnaryOp.eval : UnaryOp → Val → Option Val | |
| | .not, .bool b => some (.bool !b) | |
| | _, _ => none | |
| inductive Stmt where | |
| | skip | |
| | assign (x : Var) (e : Expr) | |
| | seq (s₁ s₂ : Stmt) | |
| | ite (c : Expr) (e t : Stmt) | |
| | while (c : Expr) (b : Stmt) | |
| deriving Repr | |
| infix:150 " ::= " => Stmt.assign | |
| infixr:130 ";; " => Stmt.seq | |
| syntax "`[Expr|" term "]" : term | |
| macro_rules | |
| | `(`[Expr|true]) => `((true : Expr)) | |
| | `(`[Expr|false]) => `((false : Expr)) | |
| | `(`[Expr|$n:num]) => `(($n : Expr)) | |
| | `(`[Expr|$x:ident]) => `(($(Lean.quote x.getId.toString) : Expr)) | |
| | `(`[Expr|$x = $y]) => `(Expr.bin `[Expr|$x] BinOp.eq `[Expr|$y]) | |
| | `(`[Expr|$x && $y]) => `(Expr.bin `[Expr|$x] BinOp.and `[Expr|$y]) | |
| | `(`[Expr|$x < $y]) => `(Expr.bin `[Expr|$x] BinOp.lt `[Expr|$y]) | |
| | `(`[Expr|$x + $y]) => `(Expr.bin `[Expr|$x] BinOp.add `[Expr|$y]) | |
| | `(`[Expr|$x - $y]) => `(Expr.bin `[Expr|$x] BinOp.sub `[Expr|$y]) | |
| | `(`[Expr|!$x]) => `(Expr.una UnaryOp.not `[Expr|$x]) | |
| | `(`[Expr|($x)]) => `(`[Expr|$x]) | |
| declare_syntax_cat stmt | |
| syntax ident " := " term ";": stmt | |
| syntax "if " "(" term ")" " {\n" stmt* "\n}" (" else " "{\n" stmt* "\n}")? : stmt | |
| syntax "while " "(" term ")" "{\n" stmt* "\n}" : stmt | |
| syntax "`[Stmt|" stmt* "]" : term | |
| macro_rules | |
| | `(`[Stmt| ]) => `(Stmt.skip) | |
| | `(`[Stmt| $x:ident := $e:term;]) => `(Stmt.assign $(Lean.quote x.getId.toString) `[Expr| $e:term]) | |
| | `(`[Stmt| if ($c) { $ts* } else { $es* }]) => `(Stmt.ite `[Expr| $c] `[Stmt| $ts*] `[Stmt| $es*]) | |
| | `(`[Stmt| if ($c) { $ts* }]) => `(Stmt.ite `[Expr| $c] `[Stmt| $ts*] Stmt.skip) | |
| | `(`[Stmt| while ($c) { $b* }]) => `(Stmt.while `[Expr|$c] `[Stmt|$b*]) | |
| | `(`[Stmt| $s $ss*]) => `(Stmt.seq `[Stmt|$s] `[Stmt|$ss*]) | |
| def example1 := `[Stmt| | |
| x := 8; | |
| y := 10; | |
| if (x < y) { | |
| x := x + 1; | |
| } else { | |
| y := y + 3; | |
| } | |
| ] | |
| /-- | |
| info: "x" ::= Expr.val (Val.int (Int.ofNat 8));; | |
| "y" ::= Expr.val (Val.int (Int.ofNat 10));; | |
| Stmt.ite ((Expr.var "x").bin BinOp.lt (Expr.var "y")) | |
| ("x" ::= (Expr.var "x").bin BinOp.add (Expr.val (Val.int (Int.ofNat 1)))) | |
| ("y" ::= (Expr.var "y").bin BinOp.add (Expr.val (Val.int (Int.ofNat 3)))) | |
| -/ | |
| #guard_msgs in | |
| #reduce example1 | |
| def example2 := `[Stmt| | |
| x := 8; | |
| if (x < y) { | |
| x := x + 1; | |
| } else { | |
| y := y + 3; | |
| x := 9; | |
| } | |
| y := x;] | |
| /-- | |
| info: Stmt.seq | |
| (Stmt.assign "x" (Expr.val (Val.int 8))) | |
| (Stmt.seq | |
| (Stmt.ite | |
| (Expr.bin (Expr.var "x") (BinOp.lt) (Expr.var "y")) | |
| (Stmt.assign "x" (Expr.bin (Expr.var "x") (BinOp.add) (Expr.val (Val.int 1)))) | |
| (Stmt.seq | |
| (Stmt.assign "y" (Expr.bin (Expr.var "y") (BinOp.add) (Expr.val (Val.int 3)))) | |
| (Stmt.assign "x" (Expr.val (Val.int 9))))) | |
| (Stmt.assign "y" (Expr.var "x"))) | |
| -/ | |
| #guard_msgs in | |
| #eval example2 | |
| abbrev State := List (Var × Val) | |
| @[simp] def State.update (σ : State) (x : Var) (v : Val) : State := | |
| match σ with | |
| | [] => [(x, v)] | |
| | (y, w)::σ => if x = y then (x, v)::σ else (y, w) :: update σ x v | |
| @[simp] def State.find? (σ : State) (x : Var) : Option Val := | |
| match σ with | |
| | [] => none | |
| | (y, v) :: σ => if x = y then some v else find? σ x | |
| def State.get (σ : State) (x : Var) : Val := | |
| σ.find? x |>.getD (.int 0) | |
| @[simp] def State.erase (σ : State) (x : Var) : State := | |
| match σ with | |
| | [] => [] | |
| | (y, v) :: σ => if x = y then erase σ x else (y, v) :: erase σ x | |
| @[simp] theorem State.find?_update_self (σ : State) (x : Var) (v : Val) : (σ.update x v).find? x = some v := by | |
| match σ with -- TODO: automate this proof | |
| | [] => simp | |
| | (y, w) :: s => | |
| simp | |
| split <;> simp [*] | |
| apply find?_update_self | |
| @[simp] theorem State.find?_update (σ : State) (v : Val) (h : x ≠ z) : (σ.update x v).find? z = σ.find? z := by | |
| match σ with -- TODO: automate this proof | |
| | [] => simp [h.symm] | |
| | (y, w) :: σ => | |
| simp | |
| split <;> simp [*] | |
| next hc => split <;> simp_all | |
| next => | |
| split | |
| next => rfl | |
| next => exact find?_update σ v h | |
| -- TODO: remove after we add better automation | |
| @[simp] theorem State.find?_update' (σ : State) (v : Val) (h : z ≠ x) : (σ.update x v).find? z = σ.find? z := | |
| State.find?_update σ v h.symm | |
| theorem State.get_of_find? {σ : State} (h : σ.find? x = some v) : σ.get x = v := by | |
| simp [State.get, h, Option.getD] | |
| @[simp] theorem State.find?_erase_self (σ : State) (x : Var) : (σ.erase x).find? x = none := by | |
| match σ with | |
| | [] => simp | |
| | (y, w) :: σ => | |
| simp | |
| split <;> simp [*] | |
| next => exact find?_erase_self σ y | |
| next => exact find?_erase_self σ x | |
| @[simp] theorem State.find?_erase (σ : State) (h : x ≠ z) : (σ.erase x).find? z = σ.find? z := by | |
| match σ with | |
| | [] => simp | |
| | (y, w) :: σ => | |
| simp | |
| split <;> simp [*] | |
| next hxy => rw [hxy] at h; simp [h.symm]; exact find?_erase σ h | |
| next => | |
| split | |
| next => rfl | |
| next => exact find?_erase σ h | |
| -- TODO: remove after we add better automation | |
| @[simp] theorem State.find?_erase' (σ : State) (h : z ≠ x) : (σ.erase x).find? z = σ.find? z := | |
| State.find?_erase σ h.symm | |
| syntax ident " ↦ " term : term | |
| macro_rules | |
| | `($id:ident ↦ $v:term) => `(($(Lean.quote id.getId.toString), $v:term)) | |
| example : State.get [x ↦ .int 10, y ↦ .int 20] "x" = .int 10 := rfl | |
| example : State.get [x ↦ 10, y ↦ 20] "x" = 10 := rfl | |
| example : State.get [x ↦ 10, y ↦ true] "y" = true := rfl | |
| @[simp] def Expr.eval (σ : State) : Expr → Option Val | |
| | val v => some v | |
| | var x => σ.get x | |
| | bin lhs op rhs => match lhs.eval σ, rhs.eval σ with | |
| | some v₁, some v₂ => op.eval v₁ v₂ -- BinOp.eval : BinOp → Val → Val → Option Val | |
| | _, _ => none | |
| | una op arg => match arg.eval σ with | |
| | some v => op.eval v | |
| | _ => none | |
| @[simp] def evalTrue (c : Expr) (σ : State) : Prop := c.eval σ = some (Val.bool true) | |
| @[simp] def evalFalse (c : Expr) (σ : State) : Prop := c.eval σ = some (Val.bool false) | |
| section | |
| set_option hygiene false -- HACK: allow forward reference in notation | |
| local notation:60 "(" σ ", " s ")" " ⇓ " σ':60 => Bigstep σ s σ' | |
| inductive Bigstep : State → Stmt → State → Prop where | |
| | skip : (σ, .skip) ⇓ σ | |
| | assign : e.eval σ = some v → (σ, x ::= e) ⇓ σ.update x v | |
| | seq : (σ₁, s₁) ⇓ σ₂ → (σ₂, s₂) ⇓ σ₃ → (σ₁, s₁ ;; s₂) ⇓ σ₃ | |
| | ifTrue : evalTrue c σ₁ → (σ₁, t) ⇓ σ₂ → (σ₁, .ite c t e) ⇓ σ₂ | |
| | ifFalse : evalFalse c σ₁ → (σ₁, e) ⇓ σ₂ → (σ₁, .ite c t e) ⇓ σ₂ | |
| | whileTrue : evalTrue c σ₁ → (σ₁, b) ⇓ σ₂ → (σ₂, .while c b) ⇓ σ₃ → (σ₁, .while c b) ⇓ σ₃ | |
| | whileFalse : evalFalse c σ → (σ, .while c b) ⇓ σ | |
| end | |
| notation:60 "(" σ ", " s ")" " ⇓ " σ':60 => Bigstep σ s σ' | |
| /- This proof can be automated using forward reasoning. -/ | |
| theorem Bigstem.det (h₁ : (σ, s) ⇓ σ₁) (h₂ : (σ, s) ⇓ σ₂) : σ₁ = σ₂ := by | |
| induction h₁ generalizing σ₂ <;> cases h₂ <;> try simp_all | |
| -- The rest of this proof should be automatic with congruence closure and a bit of forward reasoning | |
| case seq ih₁ ih₂ _ h₁ h₂ => | |
| simp [ih₁ h₁] at ih₂ | |
| simp [ih₂ h₂] | |
| case ifTrue ih h => | |
| simp [ih h] | |
| case ifFalse ih h => | |
| simp [ih h] | |
| case whileTrue ih₁ ih₂ h₁ h₂ => | |
| simp [ih₁ h₁] at ih₂ | |
| simp [ih₂ h₂] | |
| abbrev EvalM := ExceptT String (StateM State) | |
| def evalExpr (e : Expr) : EvalM Val := do | |
| match e.eval (← get) with | |
| | some v => return v | |
| | none => throw "failed to evaluate" | |
| @[simp] def Stmt.eval (stmt : Stmt) (fuel : Nat := 100) : EvalM Unit := do | |
| match fuel with | |
| | 0 => throw "out of fuel" | |
| | fuel+1 => | |
| match stmt with | |
| | skip => return () | |
| | assign x e => let v ← evalExpr e; modify fun s => s.update x v | |
| | seq s₁ s₂ => s₁.eval fuel; s₂.eval fuel | |
| | ite c e t => | |
| match (← evalExpr c) with | |
| | .bool true => e.eval fuel | |
| | .bool false => t.eval fuel | |
| | _ => throw "Boolean expected" | |
| | .while c b => | |
| match (← evalExpr c) with | |
| | .bool true => b.eval fuel; stmt.eval fuel | |
| | .bool false => return () | |
| | _ => throw "Boolean expected" | |
| /-- info: (Except.ok (), [("x", Val.int 8), ("y", Val.int 5)]) -/ | |
| #guard_msgs in | |
| #eval `[Stmt| x := 3; y := 5; x := x + y;].eval |>.run {} | |
| /-- info: (Except.error "out of fuel", [("x", Val.int 98)]) -/ | |
| #guard_msgs in | |
| #eval `[Stmt| x := 0; while (true) { x := x + 1; }].eval |>.run {} | |
| instance : Repr State where | |
| reprPrec a n := | |
| match a, n with | |
| | [], _ => "[]" | |
| | as, _ => | |
| let fs := as.map fun | |
| | (x, .int v) => f!"{x} ↦ {v}" | |
| | (x, .bool v) => f!"{x} ↦ {v}" | |
| Std.Format.bracket "[" (Std.Format.joinSep fs ("," ++ Std.Format.line)) "]" | |
| /-- info: (Except.ok (), [x ↦ 8, y ↦ 5]) -/ | |
| #guard_msgs in | |
| #eval `[Stmt| x := 3; y := 5; x := x + y; ].eval |>.run {} | |
| @[simp] def BinOp.simplify : BinOp → Expr → Expr → Expr | |
| | .eq, .val v₁, .val v₂ => .val (.bool (v₁ = v₂)) | |
| | .and, .val (.bool a), .val (.bool b) => .val (.bool (a && b)) | |
| | .lt, .val (.int a), .val (.int b) => .val (.bool (a < b)) | |
| | .add, .val (.int a), .val (.int b) => .val (.int (a + b)) | |
| | .sub, .val (.int a), .val (.int b) => .val (.int (a - b)) | |
| | op, a, b => .bin a op b | |
| @[simp] def UnaryOp.simplify : UnaryOp → Expr → Expr | |
| | .not, .val (.bool b) => .val (.bool !b) | |
| | op, a => .una op a | |
| @[simp] def Expr.simplify : Expr → Expr | |
| | bin lhs op rhs => op.simplify lhs.simplify rhs.simplify | |
| | una op arg => op.simplify arg.simplify | |
| | e => e | |
| @[simp] theorem Expr.eval_simplify (e : Expr) : e.simplify.eval σ = e.eval σ := by | |
| induction e with | |
| -- Due to fine-grained equational theorems we have to pass `eq_def` lemmas here | |
| simp only [simplify, BinOp.simplify.eq_def, eval, UnaryOp.simplify.eq_def] | |
| | bin lhs op rhs ih_lhs ih_rhs => | |
| simp [← ih_lhs, ← ih_rhs] | |
| split <;> simp [*] | |
| | una op arg ih_arg => | |
| simp [← ih_arg] | |
| split <;> simp [*] | |
| @[simp] def Stmt.simplify : Stmt → Stmt | |
| | skip => skip | |
| | assign x e => assign x e.simplify | |
| | seq s₁ s₂ => seq s₁.simplify s₂.simplify | |
| | ite c e t => | |
| match c.simplify with | |
| | .val (.bool true) => e.simplify | |
| | .val (.bool false) => t.simplify | |
| | c' => ite c' e.simplify t.simplify | |
| | .while c b => | |
| match c.simplify with | |
| | .val (.bool false) => skip | |
| | c' => .while c' b.simplify | |
| def example3 := `[Stmt| | |
| if (1 < 2 + 3) { | |
| x := 3 + 1; | |
| y := y + x; | |
| } else { | |
| y := y + 3; | |
| } | |
| ] | |
| /-- | |
| info: Stmt.seq (Stmt.assign "x" (Expr.val (Val.int 4))) (Stmt.assign "y" (Expr.bin (Expr.var "y") (BinOp.add) (Expr.var "x"))) | |
| -/ | |
| #guard_msgs in | |
| #eval example3.simplify | |
| theorem Stmt.simplify_correct (h : (σ, s) ⇓ σ') : (σ, s.simplify) ⇓ σ' := by | |
| induction h with simp_all | |
| | skip => exact Bigstep.skip | |
| | seq h₁ h₂ ih₁ ih₂ => exact Bigstep.seq ih₁ ih₂ | |
| | assign => apply Bigstep.assign; simp [*] | |
| | whileTrue heq h₁ h₂ ih₁ ih₂ => | |
| rw [← Expr.eval_simplify] at heq | |
| split | |
| next h => rw [h] at heq; simp at heq | |
| next hnp => simp [hnp] at ih₂; apply Bigstep.whileTrue heq ih₁ ih₂ | |
| | whileFalse heq => | |
| split | |
| next => exact Bigstep.skip | |
| next => apply Bigstep.whileFalse; simp [heq] | |
| | ifFalse heq h ih => | |
| rw [← Expr.eval_simplify] at heq | |
| split <;> simp_all | |
| rw [← Expr.eval_simplify] at heq | |
| apply Bigstep.ifFalse heq ih | |
| | ifTrue heq h ih => | |
| rw [← Expr.eval_simplify] at heq | |
| split <;> simp_all | |
| rw [← Expr.eval_simplify] at heq | |
| apply Bigstep.ifTrue heq ih | |
| @[simp] def Expr.constProp (e : Expr) (σ : State) : Expr := | |
| match e with | |
| | val v => v | |
| | var x => match σ.find? x with | |
| | some v => val v | |
| | none => var x | |
| | bin lhs op rhs => bin (lhs.constProp σ) op (rhs.constProp σ) | |
| | una op arg => una op (arg.constProp σ) | |
| @[simp] theorem Expr.constProp_nil (e : Expr) : e.constProp [] = e := by | |
| induction e <;> simp [*] | |
| def State.length_erase_le (σ : State) (x : Var) : (σ.erase x).length ≤ σ.length := by | |
| match σ with | |
| | [] => simp | |
| | (y, v) :: σ => | |
| by_cases hxy : x = y <;> simp [hxy] | |
| next => exact Nat.le_trans (length_erase_le σ y) (by simp +arith) | |
| next => simp +arith [length_erase_le σ x] | |
| def State.length_erase_lt (σ : State) (x : Var) : (σ.erase x).length < σ.length.succ := | |
| Nat.lt_of_le_of_lt (length_erase_le ..) (by simp +arith) | |
| @[simp] def State.join (σ₁ σ₂ : State) : State := | |
| match σ₁ with | |
| | [] => [] | |
| | (x, v) :: σ₁ => | |
| let σ₁' := erase σ₁ x -- Must remove duplicates. Alternative design: carry invariant that input state at constProp has no duplicates | |
| have : (erase σ₁ x).length < σ₁.length.succ := length_erase_lt .. | |
| match σ₂.find? x with | |
| | some w => if v = w then (x, v) :: join σ₁' σ₂ else join σ₁' σ₂ | |
| | none => join σ₁' σ₂ | |
| termination_by σ₁.length | |
| local notation "⊥" => [] | |
| @[simp] def Stmt.constProp (s : Stmt) (σ : State) : Stmt × State := | |
| match s with | |
| | skip => (skip, σ) | |
| | assign x e => match (e.constProp σ).simplify with | |
| | (.val v) => (assign x (.val v), σ.update x v) | |
| | e' => (assign x e', σ.erase x) | |
| | seq s₁ s₂ => match s₁.constProp σ with | |
| | (s₁', σ₁) => match s₂.constProp σ₁ with | |
| | (s₂', σ₂) => (seq s₁' s₂', σ₂) | |
| | ite c s₁ s₂ => | |
| match s₁.constProp σ, s₂.constProp σ with | |
| | (s₁', σ₁), (s₂', σ₂) => (ite (c.constProp σ) s₁' s₂', σ₁.join σ₂) | |
| | .while c b => (.while (c.constProp ⊥) (b.constProp ⊥).1, ⊥) | |
| def State.le (σ₁ σ₂ : State) : Prop := | |
| ∀ ⦃x : Var⦄ ⦃v : Val⦄, σ₁.find? x = some v → σ₂.find? x = some v | |
| infix:50 " ≼ " => State.le | |
| theorem State.le_refl (σ : State) : σ ≼ σ := | |
| fun _ _ h => h | |
| theorem State.le_trans : σ₁ ≼ σ₂ → σ₂ ≼ σ₃ → σ₁ ≼ σ₃ := | |
| fun h₁ h₂ x v h => h₂ (h₁ h) | |
| theorem State.bot_le (σ : State) : ⊥ ≼ σ := | |
| fun _ _ h => by contradiction | |
| theorem State.erase_le_cons (h : σ' ≼ σ) : σ'.erase x ≼ ((x, v) :: σ) := by | |
| intro y w hf' | |
| by_cases hyx : y = x <;> simp [*] at hf' |- | |
| exact h hf' | |
| theorem State.cons_le_cons (h : σ' ≼ σ) : (x, v) :: σ' ≼ (x, v) :: σ := by | |
| intro y w hf' | |
| by_cases hyx : y = x <;> simp [*] at hf' |- | |
| next => assumption | |
| next => exact h hf' | |
| theorem State.cons_le_of_eq (h₁ : σ' ≼ σ) (h₂ : σ.find? x = some v) : (x, v) :: σ' ≼ σ := by | |
| intro y w hf' | |
| by_cases hyx : y = x <;> simp [*] at hf' |- | |
| next => assumption | |
| next => exact h₁ hf' | |
| theorem State.erase_le (σ : State) : σ.erase x ≼ σ := by | |
| match σ with | |
| | [] => simp; apply le_refl | |
| | (y, v) :: σ => | |
| simp | |
| split <;> try simp [*] | |
| next => apply erase_le_cons; apply le_refl | |
| next => apply cons_le_cons; apply erase_le | |
| theorem State.join_le_left (σ₁ σ₂ : State) : σ₁.join σ₂ ≼ σ₁ := by | |
| match σ₁ with | |
| | [] => simp; apply le_refl | |
| | (x, v) :: σ₁ => | |
| simp | |
| have : (erase σ₁ x).length < σ₁.length.succ := length_erase_lt .. | |
| have ih := join_le_left (State.erase σ₁ x) σ₂ | |
| split | |
| next y w h => | |
| split | |
| next => apply cons_le_cons; apply le_trans ih (erase_le _) | |
| next => apply le_trans ih (erase_le_cons (le_refl _)) | |
| next h => apply le_trans ih (erase_le_cons (le_refl _)) | |
| termination_by σ₁.length | |
| theorem State.join_le_left_of (h : σ₁ ≼ σ₂) (σ₃ : State) : σ₁.join σ₃ ≼ σ₂ := | |
| le_trans (join_le_left σ₁ σ₃) h | |
| theorem State.join_le_right (σ₁ σ₂ : State) : σ₁.join σ₂ ≼ σ₂ := by | |
| match σ₁ with | |
| | [] => simp; apply bot_le | |
| | (x, v) :: σ₁ => | |
| simp | |
| have : (erase σ₁ x).length < σ₁.length.succ := length_erase_lt .. | |
| have ih := join_le_right (erase σ₁ x) σ₂ | |
| split | |
| next y w h => | |
| split <;> simp [*] | |
| next => apply cons_le_of_eq ih h | |
| next h => assumption | |
| termination_by σ₁.length | |
| theorem State.join_le_right_of (h : σ₁ ≼ σ₂) (σ₃ : State) : σ₃.join σ₁ ≼ σ₂ := | |
| le_trans (join_le_right σ₃ σ₁) h | |
| theorem State.eq_bot (h : σ ≼ ⊥) : σ = ⊥ := by | |
| match σ with | |
| | [] => simp | |
| | (y, v) :: σ => | |
| have : State.find? ((y, v) :: σ) y = some v := by simp | |
| have := h this | |
| contradiction | |
| theorem State.erase_le_of_le_cons (h : σ' ≼ (x, v) :: σ) : σ'.erase x ≼ σ := by | |
| intro y w hf' | |
| by_cases hxy : x = y <;> simp [*] at hf' | |
| have hf := h hf' | |
| simp [hxy, Ne.symm hxy] at hf | |
| assumption | |
| theorem State.erase_le_update (h : σ' ≼ σ) : σ'.erase x ≼ σ.update x v := by | |
| intro y w hf' | |
| by_cases hxy : x = y <;> simp [*] at hf' |- | |
| exact h hf' | |
| theorem State.update_le_update (h : σ' ≼ σ) : σ'.update x v ≼ σ.update x v := by | |
| intro y w hf | |
| induction σ generalizing σ' hf with | |
| | nil => rw [eq_bot h] at hf; assumption | |
| | cons zw' σ ih => | |
| have (z, w') := zw'; simp | |
| have : σ'.erase z ≼ σ := erase_le_of_le_cons h | |
| have ih := ih this | |
| revert ih hf | |
| split <;> simp [*] <;> by_cases hyz : y = z <;> simp (config := { contextual := true }) [*] | |
| next => | |
| intro he' | |
| have he := h he' | |
| simp [*] at he | |
| assumption | |
| next => | |
| by_cases hxy : x = y <;> simp_all | |
| theorem Expr.eval_constProp_of_sub (e : Expr) (h : σ' ≼ σ) : (e.constProp σ').eval σ = e.eval σ := by | |
| induction e with simp [*] | |
| | var x => | |
| split <;> simp | |
| next he => rw [State.get_of_find? (h he)] | |
| theorem Expr.eval_constProp_of_eq_of_sub {e : Expr} (h₁ : e.eval σ = v) (h₂ : σ' ≼ σ) : (e.constProp σ').eval σ = v := by | |
| have := eval_constProp_of_sub e h₂ | |
| simp [h₁] at this | |
| assumption | |
| theorem Stmt.constProp_sub (h₁ : (σ₁, s) ⇓ σ₂) (h₂ : σ₁' ≼ σ₁) : (s.constProp σ₁').2 ≼ σ₂ := by | |
| induction h₁ generalizing σ₁' with simp | |
| | skip => assumption | |
| | assign heq => | |
| split <;> simp | |
| next h => | |
| have heq' := Expr.eval_constProp_of_eq_of_sub heq h₂ | |
| rw [← Expr.eval_simplify, h] at heq' | |
| simp at heq' | |
| rw [heq'] | |
| apply State.update_le_update h₂ | |
| next h _ _ => | |
| exact State.erase_le_update h₂ | |
| | whileTrue heq h₃ h₄ ih₃ ih₄ => | |
| have ih₃ := ih₃ h₂ | |
| have ih₄ := ih₄ ih₃ | |
| simp [heq] at ih₄ | |
| exact ih₄ | |
| | whileFalse heq => apply State.bot_le | |
| | ifTrue heq h ih => | |
| have ih := ih h₂ | |
| apply State.join_le_left_of ih | |
| | ifFalse heq h ih => | |
| have ih := ih h₂ | |
| apply State.join_le_right_of ih | |
| | seq h₃ h₄ ih₃ ih₄ => exact ih₄ (ih₃ h₂) | |
| theorem Stmt.constProp_correct (h₁ : (σ₁, s) ⇓ σ₂) (h₂ : σ₁' ≼ σ₁) : (σ₁, (s.constProp σ₁').1) ⇓ σ₂ := by | |
| induction h₁ generalizing σ₁' with simp_all | |
| | skip => exact Bigstep.skip | |
| | assign heq => | |
| split <;> simp | |
| next h => | |
| have heq' := Expr.eval_constProp_of_eq_of_sub heq h₂ | |
| rw [← Expr.eval_simplify, h] at heq' | |
| simp at heq' | |
| apply Bigstep.assign; simp [*] | |
| next => | |
| have heq' := Expr.eval_constProp_of_eq_of_sub heq h₂ | |
| rw [← Expr.eval_simplify] at heq' | |
| apply Bigstep.assign heq' | |
| | seq h₁ h₂ ih₁ ih₂ => | |
| apply Bigstep.seq (ih₁ h₂) (ih₂ (constProp_sub h₁ h₂)) | |
| | whileTrue heq h₁ h₂ ih₁ ih₂ => | |
| have ih₁ := ih₁ (State.bot_le _) | |
| have ih₂ := ih₂ (State.bot_le _) | |
| exact Bigstep.whileTrue heq ih₁ ih₂ | |
| | whileFalse heq => | |
| exact Bigstep.whileFalse heq | |
| | ifTrue heq h ih => | |
| exact Bigstep.ifTrue (Expr.eval_constProp_of_eq_of_sub heq h₂) (ih h₂) | |
| | ifFalse heq h ih => | |
| exact Bigstep.ifFalse (Expr.eval_constProp_of_eq_of_sub heq h₂) (ih h₂) | |
| def Stmt.constPropagation (s : Stmt) : Stmt := | |
| (s.constProp ⊥).1 | |
| theorem Stmt.constPropagation_correct (h : (σ, s) ⇓ σ') : (σ, s.constPropagation) ⇓ σ' := | |
| constProp_correct h (State.bot_le _) | |
| def example4 := `[Stmt| | |
| x := 2; | |
| if (x < 3) { | |
| x := x + 1; | |
| y := y + x; | |
| } else { | |
| y := y + 3; | |
| } | |
| ] | |
| /-- | |
| info: Stmt.seq | |
| (Stmt.assign "x" (Expr.val (Val.int 2))) | |
| (Stmt.seq | |
| (Stmt.assign "x" (Expr.val (Val.int 3))) | |
| (Stmt.assign "y" (Expr.bin (Expr.var "y") (BinOp.add) (Expr.val (Val.int 3))))) | |
| -/ | |
| #guard_msgs in | |
| #eval example4.constPropagation.simplify | |
| #exit | |
| -- TODO: add simp theorems for monadic code | |
| theorem Stmt.eval_correct {s : Stmt} (h : (s.eval fuel).run σ = (.ok unit, σ')) : (σ, s) ⇓ σ' := by | |
| induction fuel generalizing s σ σ' with simp at h | |
| | zero => injection h; contradiction | |
| | succ fuel ih => | |
| split at h | |
| next => injection h with _ h; rw [h]; exact Bigstep.skip | |
| next => done |