import Spa.Analysis.Forward
import Spa.Lattice.Bool
import Spa.Showable

namespace Spa

open Forward

instance : Showable Bool := ⟨fun b => if b then "true" else "false"⟩

abbrev DefSet (prog : Program) : Type := FiniteMap prog.State Bool prog.states

namespace ReachingAnalysis

variable (prog : Program)

def genSet (s : prog.State) : DefSet prog :=
  FiniteMap.updating (⊥ : DefSet prog) [s] (fun _ => true)

def eval (s : prog.State) :
    BasicStmt → VariableValues (DefSet prog) prog → VariableValues (DefSet prog) prog
  | .assign k _, vs =>
    FiniteMap.generalizedUpdate id (fun _ _ => genSet prog s) [k] vs
  | .noop, vs => vs

theorem eval_mono (s : prog.State) (bs : BasicStmt) :
    Monotone (eval prog s bs) := by
  cases bs with
  | assign k e =>
    exact FiniteMap.generalizedUpdate_monotone monotone_id (fun _ => monotone_const)
  | noop => exact monotone_id

instance stmtEvaluator : StmtEvaluator (DefSet prog) prog :=
  ⟨eval prog, eval_mono prog⟩

def output : String :=
  show' (result (DefSet prog) prog)

end ReachingAnalysis

end Spa
