/-! # Arithmetic expressions for a simple imperative language. -/ namespace imp open tactic /-- Variable names -/ @[reducible] def uname := string /-- #brief Arithmetic expressions abstract syntax tree. We encode x + 1 as ``` #check aexp.plus (aexp.var "x") (aexp.val 1) ``` -/ inductive aexp | val : nat → aexp | var : uname → aexp | plus : aexp → aexp → aexp /-- #brief Arithmetic expressions have decidable equality. -/ instance : decidable_eq aexp := by mk_dec_eq_instance /-- #brief Value assigned to variables. -/ @[reducible] def value := nat /-- #brief The state is a mapping from variable names to their values. -/ def state := uname → value open aexp /-- #brief Given an arithmetic expression and a state, this function returns the value for the expression. ``` example : aval (plus (val 3) (var "x")) (λ x, 0) = 3 := rfl ``` See [`aexp`](#imp.aexp) -/ def aval : aexp → state → value | (val n) s := n | (var x) s := s x | (plus a₁ a₂) s := aval a₁ s + aval a₂ s /-- #brief Update the state with then entry `x -> v`. We say we are assigning `v` to `x`. -/ def updt (s : state) (x : uname) (v : value) : state := λ y, if x = y then v else s y /-- #brief Very basic constant folding simplification procedure. For example, it reduces subexpressions such as (3 + 2) to 5. -/ def asimp_const : aexp → aexp | (val n) := val n | (var x) := var x | (plus a₁ a₂) := match asimp_const a₁, asimp_const a₂ with | val n₁, val n₂ := val (n₁ + n₂) | b₁, b₂ := plus b₁ b₂ end /-! _Remark_: we can prove by reflexivity the fact that the constant folder simplifies `(2+3)+x` into `5+x`. ``` example : asimp_const (plus (plus (val 2) (val 3)) (var "x")) = plus (val 5) (var "x") := rfl ``` -/ /-- #brief Prove that constant folding preserves the value of an artihmetic expressions. -/ lemma aval_asimp_const (a : aexp) (s : state) : aval (asimp_const a) s = aval a s := begin induction a with n x a₁ a₂ ih₁ ih₂, repeat {reflexivity}, {unfold asimp_const aval, rewrite [-ih₁, -ih₂], cases (asimp_const a₁), repeat {cases (asimp_const a₂), repeat {reflexivity}}} end /-- #brief Alternative proof without tactics that constant folding preserves the value of an arithmetic expression. This alternative proof is more verbose because we are essentially writing the proof term. -/ lemma aval_asimp_const₂ : ∀ (a : aexp) (s : state), aval (asimp_const a) s = aval a s | (val n) s := rfl | (var x) s := rfl | (plus a₁ a₂) s := show aval (asimp_const (plus a₁ a₂)) s = aval a₁ s + aval a₂ s, from suffices aval (asimp_const._match_1 (asimp_const a₁) (asimp_const a₂)) s = aval (asimp_const a₁) s + aval (asimp_const a₂) s, from aval_asimp_const₂ a₁ s ▸ aval_asimp_const₂ a₂ s ▸ this, match asimp_const a₁, asimp_const a₂ with | val _, val _ := rfl | val _, var _ := rfl | val _, plus _ _ := rfl | var _, val _ := rfl | var _, var _ := rfl | var _, plus _ _ := rfl | plus _ _, val _ := rfl | plus _ _, var _ := rfl | plus _ _, plus _ _ := rfl end /-- #brief Alternative proof that mixes proof terms and tactics. See [`asimp_const`](#imp.asimp_const) -/ lemma aval_asimp_const₃ : ∀ (a : aexp) (s : state), aval (asimp_const a) s = aval a s | (val n) s := rfl | (var x) s := rfl | (plus a₁ a₂) s := begin note h₁ := aval_asimp_const₃ a₁ s, note h₂ := aval_asimp_const₃ a₂ s, unfold asimp_const aval, rewrite [-h₁, -h₂], cases (asimp_const a₁); cases (asimp_const a₂); repeat {reflexivity} end end imp