A small-step semantics for LExpr.
Currently only defined for expressions paremeterized by LMonoTy, but it will
be expanded to an arbitrary type in the future.
The order of constructors matter because the constructor tactic will rely on
it.
This small-step definitions faithfully follows the behavior of LExpr.eval,
except that:
-
This inductive definition may get stuck early when there is no
assignment to a free variable available.
-
This semantics does not describe how metadata must change, because
metadata must not affect evaluation semantics. Different concrete evaluators
like LExpr.eval can have different strategy for updating metadata.
Constructors
expand_fvar {Tbase : LExprParams} [DecidableEq Tbase.IDMeta]
[Inhabited Tbase.IDMeta] {F : Lambda.Factory Tbase}
{rf : Lambda.Env Tbase} {m : Tbase.mono.base.Metadata}
{ty : Option Tbase.mono.TypeType} (x : Tbase.Identifier)
(e : LExpr Tbase.mono) :
rf x = some e → Step F rf (LExpr.fvar m x ty) e
A free variable. Stuck if fvar does not exist in FreeVarMap.
beta {Tbase : LExprParams} [DecidableEq Tbase.IDMeta]
[Inhabited Tbase.IDMeta] {F : Lambda.Factory Tbase}
{rf : Lambda.Env Tbase} {m1 m2 : Tbase.mono.base.Metadata}
{name : String} {ty : Option Tbase.mono.TypeType}
(e1 e2 eres : LExpr Tbase.mono) :
eres = LExpr.subst (fun x => e2) e1 →
Step F rf (LExpr.app m1 (LExpr.abs m2 name ty e1) e2)
eres
Beta reduction. The argument e2 need not be a canonical value;
this relaxation (compared to strict call-by-value) is necessary because LExpr.eval
evaluates both sub-expressions before performing substitution and we want the
semantics to be flexible enough to handle partial evaluation.
reduce_2 {Tbase : LExprParams} [DecidableEq Tbase.IDMeta]
[Inhabited Tbase.IDMeta] {F : Lambda.Factory Tbase}
{rf : Lambda.Env Tbase} {m m' : Tbase.mono.base.Metadata}
(e1 e2 e2' : LExpr Tbase.mono) :
Step F rf e2 e2' →
Step F rf (LExpr.app m e1 e2) (LExpr.app m' e1 e2')
Argument evaluation: reduce the argument of an application.
Note: this rule does NOT require the function part to be a canonical value.
Unlike the call-by-value strategy, this allows stepping any
argument of an application, which is needed for factory calls where LExpr.eval
evaluates all arguments independently (not left-to-right) with possibly limited
fuels.
ite_reduce_cond {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m m' : Tbase.mono.base.Metadata}
(econd econd' ethen eelse : LExpr Tbase.mono) :
Step F rf econd econd' →
Step F rf (LExpr.ite m econd ethen eelse)
(LExpr.ite m' econd' ethen eelse)
Evaluation of ite condition.
ite_reduce_then_branch {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m m' : Tbase.mono.base.Metadata}
(econd ethen ethen' eelse : LExpr Tbase.mono) :
Step F rf ethen ethen' →
Step F rf (LExpr.ite m econd ethen eelse)
(LExpr.ite m' econd ethen' eelse)
Evaluation of ite "then" branch (when condition is not yet resolved).
ite_reduce_else_branch {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m m' : Tbase.mono.base.Metadata}
(econd ethen eelse eelse' : LExpr Tbase.mono) :
Step F rf eelse eelse' →
Step F rf (LExpr.ite m econd ethen eelse)
(LExpr.ite m' econd ethen eelse')
Evaluation of ite "else" branch (when condition is not yet resolved).
eq_reduce_false {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m mc : Tbase.mono.base.Metadata}
(e1 e2 : LExpr Tbase.mono) :
LExpr.eql F e1 e2 = some false →
Step F rf (LExpr.eq m e1 e2)
(LExpr.const mc (LConst.boolConst false))
Evaluation of equality to false. Only when neither side contains a binder,
because syntactic inequality under binders does not imply semantic inequality
(e.g., λx. x+1 vs λx. 1+x).
eq_reduce_lhs {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m m' : Tbase.mono.base.Metadata}
(e1 e1' e2 : LExpr Tbase.mono) :
Step F rf e1 e1' →
Step F rf (LExpr.eq m e1 e2) (LExpr.eq m' e1' e2)
Evaluation of the left-hand side of an equality.
eq_reduce_rhs {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m m' : Tbase.mono.base.Metadata}
(e1 e2 e2' : LExpr Tbase.mono) :
Step F rf e2 e2' →
Step F rf (LExpr.eq m e1 e2) (LExpr.eq m' e1 e2')
Evaluation of the right-hand side of an equality.
Note: this rule does NOT require the LHS to be a canonical value.
expand_fn {Tbase : LExprParams} [DecidableEq Tbase.IDMeta]
[Inhabited Tbase.IDMeta] {F : Lambda.Factory Tbase}
{rf : Lambda.Env Tbase}
(e callee fnbody new_body : LExpr Tbase.mono)
(args :
List (LExpr { base := Tbase, TypeType := LMonoTy }))
(fn : LFunc Tbase) (tySubst : Subst) :
F.callOfLFunc e = some (callee, args, fn) →
fn.body = some fnbody →
fn.computeTypeSubst callee args = some tySubst →
new_body =
(fnbody.applySubst tySubst).substFvarsLifting
(fn.inputs.keys.zip args) →
Step F rf e new_body
Evaluate a built-in function when a body expression is available in the
Factory argument F. This is consistent with what LExpr.eval does (modulo
the inline flag). Note that it might also be possible to evaluate with
eval_fn. A key correctness property is that doing so will yield the same
result. Note that this rule does not enforce an evaluation order.
eval_fn {Tbase : LExprParams} [DecidableEq Tbase.IDMeta]
[Inhabited Tbase.IDMeta] {F : Lambda.Factory Tbase}
{rf : Lambda.Env Tbase} {m : Tbase.Metadata}
(e callee e' : LExpr Tbase.mono)
(args :
List (LExpr { base := Tbase, TypeType := LMonoTy }))
(fn : LFunc Tbase)
(denotefn :
Tbase.Metadata →
List (LExpr Tbase.mono) → Option (LExpr Tbase.mono)) :
F.callOfLFunc e = some (callee, args, fn) →
fn.concreteEval = some denotefn →
some e' = denotefn m args → Step F rf e e'
Evaluate a built-in function when a concrete evaluation function is
available in the Factory argument F. Note that it might also be possible to
evaluate with expand_fn. A key correctness property is that doing so will
yield the same result. Note that this rule does not enforce an evaluation
order.
abs_subst_fvars {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m : Tbase.mono.base.Metadata} {name : String}
{ty : Option Tbase.mono.TypeType}
{m' : Tbase.mono.base.Metadata} (body : LExpr Tbase.mono)
(x : Tbase.Identifier) :
x ∈ List.map Prod.fst body.freeVars →
Step F rf (LExpr.abs m name ty body)
(LExpr.abs m' name ty
(LExpr.substFvarsFromEnv rf body))
Substitute free variables under an abstraction binder using the full state.
This is analogous to the closure rule in lambda calculi with explicit
substitutions: it substitutes all occurrences of a free variable in the body of
an abstraction, even though the substitution is not represented as an explicit
syntactic term.
The x ∈ freeVars body witness ensures the step only fires when the body
actually has free variables. This preserves canonical_value_not_step:
isCanonicalValue returns true for an abs only when it is closed
(freeVars e = []), so a canonical abs has no free variables for this rule
to fire on.
quant_subst_fvars_body {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m : Tbase.mono.base.Metadata} {qk : QuantifierKind}
{name : String} {ty : Option Tbase.mono.TypeType}
{m' : Tbase.mono.base.Metadata}
(tr body : LExpr Tbase.mono) (x : Tbase.Identifier) :
x ∈ List.map Prod.fst body.freeVars →
Step F rf (LExpr.quant m qk name ty tr body)
(LExpr.quant m' qk name ty tr
(LExpr.substFvarsFromEnv rf body))
Substitute free variables under a quantifier binder (body). Same motivation
as abs_subst_fvars.
quant_subst_fvars_trigger {Tbase : LExprParams}
[DecidableEq Tbase.IDMeta] [Inhabited Tbase.IDMeta]
{F : Lambda.Factory Tbase} {rf : Lambda.Env Tbase}
{m : Tbase.mono.base.Metadata} {qk : QuantifierKind}
{name : String} {ty : Option Tbase.mono.TypeType}
{m' : Tbase.mono.base.Metadata}
(tr body : LExpr Tbase.mono) (x : Tbase.Identifier) :
x ∈ List.map Prod.fst tr.freeVars →
Step F rf (LExpr.quant m qk name ty tr body)
(LExpr.quant m' qk name ty
(LExpr.substFvarsFromEnv rf tr) body)
Substitute free variables in the trigger of a quantifier. Same motivation
as abs_subst_fvars.