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

`StringSet` (built on `Lattice/MapSet.agda`, itself on `Lattice/Map.agda`) is
lifted to mathlib's `Finset String`: `insertˢ ↦ insert`, `emptyˢ ↦ ∅`,
`singletonˢ ↦ {·}`, `_⊔ˢ_ ↦ ∪`, `to-List ↦ Finset.toList` (with
`Finset.nodup_toList` standing in for the intrinsic `Unique` proof).

Constructor renaming (Agda mixfix has no direct Lean counterpart):
  _+_ ↦ Expr.add      _-_ ↦ Expr.sub     `_ ↦ Expr.var    #_ ↦ Expr.num
  _←_ ↦ BasicStmt.assign              noop ↦ BasicStmt.noop
  ⟨_⟩ ↦ Stmt.basic    _then_ ↦ Stmt.andThen
  if_then_else_ ↦ Stmt.ifElse         while_repeat_ ↦ Stmt.whileLoop

The `_∈ᵉ_` / `_∈ᵇ_` variable-occurrence relations are ported as
`Expr.HasVar` / `BasicStmt.HasVar`; the commented-out lemmas relating them to
`Expr-vars` remain unported (they were commented out in the Agda, too).
-/
import Mathlib.Data.Finset.Basic

namespace Spa

inductive Expr where
  | add (e₁ e₂ : Expr)
  | sub (e₁ e₂ : Expr)
  | var (x : String)
  | num (n : ℕ)
  deriving DecidableEq

inductive BasicStmt where
  | assign (x : String) (e : Expr)
  | noop
  deriving DecidableEq

inductive Stmt where
  | basic (bs : BasicStmt)
  | andThen (s₁ s₂ : Stmt)
  | ifElse (e : Expr) (s₁ s₂ : Stmt)
  | whileLoop (e : Expr) (s : Stmt)
  deriving DecidableEq

/-- Agda: `_∈ᵉ_`. -/
inductive Expr.HasVar : String → Expr → Prop
  | addLeft {e₁ e₂ k} : Expr.HasVar k e₁ → Expr.HasVar k (.add e₁ e₂)
  | addRight {e₁ e₂ k} : Expr.HasVar k e₂ → Expr.HasVar k (.add e₁ e₂)
  | subLeft {e₁ e₂ k} : Expr.HasVar k e₁ → Expr.HasVar k (.sub e₁ e₂)
  | subRight {e₁ e₂ k} : Expr.HasVar k e₂ → Expr.HasVar k (.sub e₁ e₂)
  | here {k} : Expr.HasVar k (.var k)

/-- Agda: `_∈ᵇ_`. -/
inductive BasicStmt.HasVar : String → BasicStmt → Prop
  | assignLeft {k e} : BasicStmt.HasVar k (.assign k e)
  | assignRight {k k' e} : Expr.HasVar k e → BasicStmt.HasVar k (.assign k' e)

/-- Agda: `Expr-vars`. -/
def Expr.vars : Expr → Finset String
  | .add l r => l.vars ∪ r.vars
  | .sub l r => l.vars ∪ r.vars
  | .var s => {s}
  | .num _ => ∅

/-- Agda: `BasicStmt-vars`. -/
def BasicStmt.vars : BasicStmt → Finset String
  | .assign x e => {x} ∪ e.vars
  | .noop => ∅

/-- Agda: `Stmt-vars`. -/
def Stmt.vars : Stmt → Finset String
  | .basic bs => bs.vars
  | .andThen s₁ s₂ => s₁.vars ∪ s₂.vars
  | .ifElse e s₁ s₂ => (e.vars ∪ s₁.vars) ∪ s₂.vars
  | .whileLoop e s => e.vars ∪ s.vars

/-- Agda: `Stmts-vars`. -/
def Stmt.varsList (ss : List Stmt) : Finset String :=
  ss.foldr (fun s acc => s.vars ∪ acc) ∅

end Spa
