/-
Port of `Language/Semantics.agda`.

Correspondence:
  Value (↑ᶻ)        ↦ Value.int
  Env               ↦ Env (= List (String × Value))
  _∈_ (env lookup)  ↦ Env.Mem
  _,_⇒ᵉ_            ↦ EvalExpr
  _,_⇒ᵇ_            ↦ EvalBasicStmt
  _,_⇒ᵇˢ_           ↦ EvalBasicStmts
  _,_⇒ˢ_            ↦ EvalStmt
  LatticeInterpretation:
    ⟦_⟧             ↦ interp
    ⟦⟧-respects-≈   ↦ (trivial with `=`; field dropped)
    ⟦⟧-⊔-∨          ↦ interp_sup
    ⟦⟧-⊓-∧          ↦ interp_inf
    (the `Utils` combinators `_⇒_`, `_∨_`, `_∧_` are inlined as plain logic)
-/
import Spa.Language.Base
import Spa.Lattice

namespace Spa

inductive Value where
  | int (z : ℤ)
  deriving DecidableEq

def Env : Type := List (String × Value)

/-- Agda: `_∈_` on environments — lookup respecting shadowing. -/
inductive Env.Mem : String × Value → Env → Prop
  | here (s : String) (v : Value) (ρ : Env) : Env.Mem (s, v) ((s, v) :: ρ)
  | there (s s' : String) (v v' : Value) (ρ : Env) :
      ¬(s = s') → Env.Mem (s, v) ρ → Env.Mem (s, v) ((s', v') :: ρ)

/-- Agda: `_,_⇒ᵉ_`. -/
inductive EvalExpr : Env → Expr → Value → Prop
  | num (ρ : Env) (n : ℕ) : EvalExpr ρ (.num n) (.int n)
  | var (ρ : Env) (x : String) (v : Value) :
      Env.Mem (x, v) ρ → EvalExpr ρ (.var x) v
  | add (ρ : Env) (e₁ e₂ : Expr) (z₁ z₂ : ℤ) :
      EvalExpr ρ e₁ (.int z₁) → EvalExpr ρ e₂ (.int z₂) →
      EvalExpr ρ (.add e₁ e₂) (.int (z₁ + z₂))
  | sub (ρ : Env) (e₁ e₂ : Expr) (z₁ z₂ : ℤ) :
      EvalExpr ρ e₁ (.int z₁) → EvalExpr ρ e₂ (.int z₂) →
      EvalExpr ρ (.sub e₁ e₂) (.int (z₁ - z₂))

/-- Agda: `_,_⇒ᵇ_`. -/
inductive EvalBasicStmt : Env → BasicStmt → Env → Prop
  | noop (ρ : Env) : EvalBasicStmt ρ .noop ρ
  | assign (ρ : Env) (x : String) (e : Expr) (v : Value) :
      EvalExpr ρ e v → EvalBasicStmt ρ (.assign x e) ((x, v) :: ρ)

/-- Agda: `_,_⇒ᵇˢ_`. -/
inductive EvalBasicStmts : Env → List BasicStmt → Env → Prop
  | nil {ρ : Env} : EvalBasicStmts ρ [] ρ
  | cons {ρ₁ ρ₂ ρ₃ : Env} {bs : BasicStmt} {bss : List BasicStmt} :
      EvalBasicStmt ρ₁ bs ρ₂ → EvalBasicStmts ρ₂ bss ρ₃ →
      EvalBasicStmts ρ₁ (bs :: bss) ρ₃

/-- Agda: `_,_⇒ˢ_`. -/
inductive EvalStmt : Env → Stmt → Env → Prop
  | basic (ρ₁ ρ₂ : Env) (bs : BasicStmt) :
      EvalBasicStmt ρ₁ bs ρ₂ → EvalStmt ρ₁ (.basic bs) ρ₂
  | andThen (ρ₁ ρ₂ ρ₃ : Env) (s₁ s₂ : Stmt) :
      EvalStmt ρ₁ s₁ ρ₂ → EvalStmt ρ₂ s₂ ρ₃ →
      EvalStmt ρ₁ (.andThen s₁ s₂) ρ₃
  | ifTrue (ρ₁ ρ₂ : Env) (e : Expr) (z : ℤ) (s₁ s₂ : Stmt) :
      EvalExpr ρ₁ e (.int z) → ¬(z = 0) → EvalStmt ρ₁ s₁ ρ₂ →
      EvalStmt ρ₁ (.ifElse e s₁ s₂) ρ₂
  | ifFalse (ρ₁ ρ₂ : Env) (e : Expr) (s₁ s₂ : Stmt) :
      EvalExpr ρ₁ e (.int 0) → EvalStmt ρ₁ s₂ ρ₂ →
      EvalStmt ρ₁ (.ifElse e s₁ s₂) ρ₂
  | whileTrue (ρ₁ ρ₂ ρ₃ : Env) (e : Expr) (z : ℤ) (s : Stmt) :
      EvalExpr ρ₁ e (.int z) → ¬(z = 0) → EvalStmt ρ₁ s ρ₂ →
      EvalStmt ρ₂ (.whileLoop e s) ρ₃ →
      EvalStmt ρ₁ (.whileLoop e s) ρ₃
  | whileFalse (ρ : Env) (e : Expr) (s : Stmt) :
      EvalExpr ρ e (.int 0) →
      EvalStmt ρ (.whileLoop e s) ρ

/-- Agda: `LatticeInterpretation`. -/
structure LatticeInterpretation (L : Type*) [Lattice L] where
  interp : L → Value → Prop
  interp_sup : ∀ {l₁ l₂ : L} (v : Value),
    interp l₁ v ∨ interp l₂ v → interp (l₁ ⊔ l₂) v
  interp_inf : ∀ {l₁ l₂ : L} (v : Value),
    interp l₁ v ∧ interp l₂ v → interp (l₁ ⊓ l₂) v

end Spa
