This requires a few pieces: * Make node tags use `Fin n` intead of natural numbers. This makes it possible to build a finite lattice over AST nodes, and also ensure automatic, total indexing from CFG nodes into the AST that created them. For this, use the elaborator to derive the ordering statements etc. where possible. * Adjust the forward framework to enable proofs that don't just state correctness on the environment, but also on an arbitrary additional state accumulated from traversing the trace. * State the reaching definition analysis's correctness in terms of this new framework. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
98 lines
4.5 KiB
Lean4
98 lines
4.5 KiB
Lean4
import Spa.Language.Base
|
||
import Spa.Lattice
|
||
import Spa.Interp
|
||
|
||
/-!
|
||
|
||
# Operational Semantics
|
||
|
||
This file contains the operational semantics for the object language defined in
|
||
`Spa.Language.Base`. Right now, all values in the language are integers.
|
||
The semantics are big-step, and lead to a fully constructed proof tree
|
||
containing the derivation connecting the initial and final states.
|
||
All pretty standard.
|
||
|
||
-/
|
||
|
||
namespace Spa
|
||
|
||
/-- A value in the object language. Currently, the only possible case is
|
||
an integer. -/
|
||
inductive Value where
|
||
| int (z : ℤ)
|
||
deriving DecidableEq
|
||
|
||
/-- An environment mapping variables to their values. -/
|
||
def Env : Type := List (String × Value)
|
||
|
||
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') :: ρ)
|
||
|
||
/-- Inference rules for evaluating an expression (`Spa.Expr`) in a given
|
||
environment. Pretty standard big-step expression evaluation. -/
|
||
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₂))
|
||
|
||
/-- Inference rules for evaluating a basic statement (`Spa.BasicStmt`) in
|
||
a given environment, potentially changing the environment.
|
||
Pretty standard big-step evaluation. -/
|
||
inductive EvalBasicStmt : Env → BasicStmt → Env → Type
|
||
| noop (ρ : Env) : EvalBasicStmt ρ .noop ρ
|
||
| assign (ρ : Env) (x : String) (e : Expr) (v : Value) :
|
||
EvalExpr ρ e v → EvalBasicStmt ρ (.assign x e) ((x, v) :: ρ)
|
||
|
||
inductive EvalBasicStmtOpt : Env → Option BasicStmt → Env → Type
|
||
| none {ρ : Env} : EvalBasicStmtOpt ρ Option.none ρ
|
||
| some {ρ₁ ρ₂ : Env} {bs : BasicStmt} :
|
||
EvalBasicStmt ρ₁ bs ρ₂ → EvalBasicStmtOpt ρ₁ (Option.some bs) ρ₂
|
||
|
||
/-- Inference rules for evaluating statements (`Spa.Stmt`) in a given
|
||
environment, potentially changing the environment.
|
||
Pretty standard big-step evaluation. -/
|
||
inductive EvalStmt : Env → Stmt → Env → Type
|
||
| 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) ρ
|
||
|
||
/-- For the purpose of static analysis, lattices we define describe program
|
||
state, or better yet, they describe _values_ in the program.
|
||
This class should be provided by each analysis' lattice (see also `Spa/Analysis/Forward.lean`)
|
||
to describe what each lattice value means in terms of the language.
|
||
|
||
In addition to providing the interpretation (`Spa.Interp`), the lattice
|
||
combinators `⊔` and `⊓` must respect disjunction and conjunction respectively.
|
||
This is because possible paths through a control flow graph (`Spa/Language/Graphs.lean`),
|
||
are tied to lattice operations used by the analysis engine. -/
|
||
class LatticeInterpretation (L : Type*) [Lattice L] extends Interp L (Value → Prop) where
|
||
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
|