Write more documentation

Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
2026-06-25 19:36:14 -05:00
parent cbad43efdc
commit a12b6c0c3c
3 changed files with 82 additions and 0 deletions

View File

@@ -2,12 +2,27 @@ import Spa.Language.Base
import Spa.Lattice import Spa.Lattice
import Spa.Interp 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 namespace Spa
/-- A value in the object language. Currently, the only possible case is
an integer. -/
inductive Value where inductive Value where
| int (z : ) | int (z : )
deriving DecidableEq deriving DecidableEq
/-- An environment mapping variables to their values. -/
def Env : Type := List (String × Value) def Env : Type := List (String × Value)
inductive Env.Mem : String × Value Env Prop inductive Env.Mem : String × Value Env Prop
@@ -15,6 +30,8 @@ inductive Env.Mem : String × Value → Env → Prop
| there (s s' : String) (v v' : Value) (ρ : Env) : | there (s s' : String) (v v' : Value) (ρ : Env) :
¬(s = s') Env.Mem (s, v) ρ Env.Mem (s, v) ((s', v') :: ρ) ¬(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 inductive EvalExpr : Env Expr Value Prop
| num (ρ : Env) (n : ) : EvalExpr ρ (.num n) (.int n) | num (ρ : Env) (n : ) : EvalExpr ρ (.num n) (.int n)
| var (ρ : Env) (x : String) (v : Value) : | var (ρ : Env) (x : String) (v : Value) :
@@ -26,17 +43,24 @@ inductive EvalExpr : Env → Expr → Value → Prop
EvalExpr ρ e₁ (.int z₁) EvalExpr ρ e₂ (.int z₂) EvalExpr ρ e₁ (.int z₁) EvalExpr ρ e₂ (.int z₂)
EvalExpr ρ (.sub e₁ e₂) (.int (z₁ - 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 Prop inductive EvalBasicStmt : Env BasicStmt Env Prop
| noop (ρ : Env) : EvalBasicStmt ρ .noop ρ | noop (ρ : Env) : EvalBasicStmt ρ .noop ρ
| assign (ρ : Env) (x : String) (e : Expr) (v : Value) : | assign (ρ : Env) (x : String) (e : Expr) (v : Value) :
EvalExpr ρ e v EvalBasicStmt ρ (.assign x e) ((x, v) :: ρ) EvalExpr ρ e v EvalBasicStmt ρ (.assign x e) ((x, v) :: ρ)
/-- Inference rules for evaluating a sequence of basic statements. -/
inductive EvalBasicStmts : Env List BasicStmt Env Prop inductive EvalBasicStmts : Env List BasicStmt Env Prop
| nil {ρ : Env} : EvalBasicStmts ρ [] ρ | nil {ρ : Env} : EvalBasicStmts ρ [] ρ
| cons {ρ₁ ρ₂ ρ₃ : Env} {bs : BasicStmt} {bss : List BasicStmt} : | cons {ρ₁ ρ₂ ρ₃ : Env} {bs : BasicStmt} {bss : List BasicStmt} :
EvalBasicStmt ρ₁ bs ρ₂ EvalBasicStmts ρ₂ bss ρ₃ EvalBasicStmt ρ₁ bs ρ₂ EvalBasicStmts ρ₂ bss ρ₃
EvalBasicStmts ρ₁ (bs :: bss) ρ₃ EvalBasicStmts ρ₁ (bs :: bss) ρ₃
/-- 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 Prop inductive EvalStmt : Env Stmt Env Prop
| basic (ρ₁ ρ₂ : Env) (bs : BasicStmt) : | basic (ρ₁ ρ₂ : Env) (bs : BasicStmt) :
EvalBasicStmt ρ₁ bs ρ₂ EvalStmt ρ₁ (.basic bs) ρ₂ EvalBasicStmt ρ₁ bs ρ₂ EvalStmt ρ₁ (.basic bs) ρ₂
@@ -57,6 +81,15 @@ inductive EvalStmt : Env → Stmt → Env → Prop
EvalExpr ρ e (.int 0) EvalExpr ρ e (.int 0)
EvalStmt ρ (.whileLoop e s) ρ 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 class LatticeInterpretation (L : Type*) [Lattice L] extends Interp L (Value Prop) where
interp_sup : {l₁ l₂ : L} (v : Value), interp_sup : {l₁ l₂ : L} (v : Value),
interp l₁ v interp l₂ v interp (l₁ l₂) v interp l₁ v interp l₂ v interp (l₁ l₂) v

View File

@@ -1,11 +1,46 @@
import Spa.Lattice import Spa.Lattice
/-!
# Product Lattice
This file provides a proof that, in addition to being a lattice,
the product of two types $\alpha \times \beta$ forms a `Spa.FiniteHeightLattice`
if both $\alpha$ and $\beta$ have a finite height.
The proof proceeds by "unzipping" a chain:
$$
(a_1, b_1) < (a_1, b_2) < \ldots < (a_n, b_m)
$$
In which, at each step, either an $\alpha$ or $\beta$ element
might ratchet up, into two chains:
$$
\begin{aligned}
a_1 < \ldots < a_n \\
b_1 < \ldots < b_m
\end{aligned}
$$
Because at least one of the two "unzipped" chains grows with
each element of the product chain, the full chain length
can't exceed the sum of the two components. By the definition
of finite height, these two chains are bounded, and therefore,
the product chain is bounded too.
-/
namespace Spa namespace Spa
section Unzip section Unzip
variable {α β : Type*} [PartialOrder α] [PartialOrder β] variable {α β : Type*} [PartialOrder α] [PartialOrder β]
/-- The unzipping lemma: any chain (`LTSeries`) of product
elements can be decomposed into chains of components,
whose lengths bound the chain. -/
lemma LTSeries.exists_unzip (c : LTSeries (α × β)) : lemma LTSeries.exists_unzip (c : LTSeries (α × β)) :
(c₁ : LTSeries α) (c₂ : LTSeries β), (c₁ : LTSeries α) (c₂ : LTSeries β),
c₁.head = c.head.1 c₁.last = c.last.1 c₁.head = c.head.1 c₁.last = c.last.1
@@ -60,6 +95,9 @@ section FixedHeight
variable {α β : Type*} variable {α β : Type*}
/-- The longest possible chain is one in which only one of the components grows
at a time, making the maximum height of $\alpha \times \beta$ be
$\text{height}_\alpha + \text{height}_\beta$. -/
instance prod [A : FiniteHeightLattice α] [B : FiniteHeightLattice β] : instance prod [A : FiniteHeightLattice α] [B : FiniteHeightLattice β] :
FiniteHeightLattice (α × β) where FiniteHeightLattice (α × β) where
toLattice := inferInstance toLattice := inferInstance

View File

@@ -1,7 +1,18 @@
import Spa.Lattice import Spa.Lattice
/-!
# Unit Lattice
This file provides a proof that in addition to being a lattice,
`PUnit` is a `Spa.FiniteHeightLattice`. This is fairly trivial result,
but the unit is used as a placeholder in various contexts (e.g.,
as a base case for the iterated product `Spa/Lattice/IterProd.lean`). -/
namespace Spa namespace Spa
/-- Since a singleton type's preorder has no nonempty `<` chains,
they are vacuously bounded by any minimum height. -/
lemma boundedChains_of_subsingleton (α : Type*) [Preorder α] [Subsingleton α] lemma boundedChains_of_subsingleton (α : Type*) [Preorder α] [Subsingleton α]
(n : ) : BoundedChains α n := fun c => by (n : ) : BoundedChains α n := fun c => by
by_contra hc by_contra hc