1015 lines
36 KiB
Text
1015 lines
36 KiB
Text
/-
|
||
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Leonardo de Moura
|
||
-/
|
||
import Lean.Data.KVMap
|
||
import Lean.Level
|
||
|
||
namespace Lean
|
||
|
||
inductive Literal where
|
||
| natVal (val : Nat)
|
||
| strVal (val : String)
|
||
deriving Inhabited, BEq
|
||
|
||
protected def Literal.hash : Literal → USize
|
||
| Literal.natVal v => hash v
|
||
| Literal.strVal v => hash v
|
||
|
||
instance : Hashable Literal := ⟨Literal.hash⟩
|
||
|
||
def Literal.lt : Literal → Literal → Bool
|
||
| Literal.natVal _, Literal.strVal _ => true
|
||
| Literal.natVal v₁, Literal.natVal v₂ => v₁ < v₂
|
||
| Literal.strVal v₁, Literal.strVal v₂ => v₁ < v₂
|
||
| _, _ => false
|
||
|
||
instance : HasLess Literal := ⟨fun a b => a.lt b⟩
|
||
|
||
instance (a b : Literal) : Decidable (a < b) :=
|
||
inferInstanceAs (Decidable (a.lt b))
|
||
|
||
inductive BinderInfo where
|
||
| default | implicit | strictImplicit | instImplicit | auxDecl
|
||
deriving Inhabited, BEq
|
||
|
||
def BinderInfo.hash : BinderInfo → USize
|
||
| BinderInfo.default => 947
|
||
| BinderInfo.implicit => 1019
|
||
| BinderInfo.strictImplicit => 1087
|
||
| BinderInfo.instImplicit => 1153
|
||
| BinderInfo.auxDecl => 1229
|
||
|
||
def BinderInfo.isExplicit : BinderInfo → Bool
|
||
| BinderInfo.implicit => false
|
||
| BinderInfo.strictImplicit => false
|
||
| BinderInfo.instImplicit => false
|
||
| _ => true
|
||
|
||
instance : Hashable BinderInfo := ⟨BinderInfo.hash⟩
|
||
|
||
def BinderInfo.isInstImplicit : BinderInfo → Bool
|
||
| BinderInfo.instImplicit => true
|
||
| _ => false
|
||
|
||
def BinderInfo.isAuxDecl : BinderInfo → Bool
|
||
| BinderInfo.auxDecl => true
|
||
| _ => false
|
||
|
||
abbrev MData := KVMap
|
||
abbrev MData.empty : MData := {}
|
||
|
||
/--
|
||
Cached hash code, cached results, and other data for `Expr`.
|
||
hash : 32-bits
|
||
hasFVar : 1-bit
|
||
hasExprMVar : 1-bit
|
||
hasLevelMVar : 1-bit
|
||
hasLevelParam : 1-bit
|
||
nonDepLet : 1-bit
|
||
binderInfo : 3-bits
|
||
looseBVarRange : 24-bits -/
|
||
def Expr.Data := UInt64
|
||
|
||
instance: Inhabited Expr.Data :=
|
||
inferInstanceAs (Inhabited UInt64)
|
||
|
||
def Expr.Data.hash (c : Expr.Data) : USize :=
|
||
c.toUInt32.toUSize
|
||
|
||
instance : BEq Expr.Data where
|
||
beq (a b : UInt64) := a == b
|
||
|
||
def Expr.Data.looseBVarRange (c : Expr.Data) : UInt32 :=
|
||
(c.shiftRight 40).toUInt32
|
||
|
||
def Expr.Data.hasFVar (c : Expr.Data) : Bool :=
|
||
((c.shiftRight 32).land 1) == 1
|
||
|
||
def Expr.Data.hasExprMVar (c : Expr.Data) : Bool :=
|
||
((c.shiftRight 33).land 1) == 1
|
||
|
||
def Expr.Data.hasLevelMVar (c : Expr.Data) : Bool :=
|
||
((c.shiftRight 34).land 1) == 1
|
||
|
||
def Expr.Data.hasLevelParam (c : Expr.Data) : Bool :=
|
||
((c.shiftRight 35).land 1) == 1
|
||
|
||
def Expr.Data.nonDepLet (c : Expr.Data) : Bool :=
|
||
((c.shiftRight 36).land 1) == 1
|
||
|
||
@[extern c inline "(uint8_t)((#1 << 24) >> 61)"]
|
||
def Expr.Data.binderInfo (c : Expr.Data) : BinderInfo :=
|
||
let bi := (c.shiftLeft 24).shiftRight 61
|
||
if bi == 0 then BinderInfo.default
|
||
else if bi == 1 then BinderInfo.implicit
|
||
else if bi == 2 then BinderInfo.strictImplicit
|
||
else if bi == 3 then BinderInfo.instImplicit
|
||
else BinderInfo.auxDecl
|
||
|
||
@[extern c inline "(uint64_t)#1"]
|
||
def BinderInfo.toUInt64 : BinderInfo → UInt64
|
||
| BinderInfo.default => 0
|
||
| BinderInfo.implicit => 1
|
||
| BinderInfo.strictImplicit => 2
|
||
| BinderInfo.instImplicit => 3
|
||
| BinderInfo.auxDecl => 4
|
||
|
||
@[inline] private def Expr.mkDataCore
|
||
(h : USize) (looseBVarRange : Nat)
|
||
(hasFVar hasExprMVar hasLevelMVar hasLevelParam nonDepLet : Bool) (bi : BinderInfo)
|
||
: Expr.Data :=
|
||
if looseBVarRange > Nat.pow 2 24 - 1 then panic! "bound variable index is too big"
|
||
else
|
||
let r : UInt64 :=
|
||
h.toUInt32.toUInt64 +
|
||
hasFVar.toUInt64.shiftLeft 32 +
|
||
hasExprMVar.toUInt64.shiftLeft 33 +
|
||
hasLevelMVar.toUInt64.shiftLeft 34 +
|
||
hasLevelParam.toUInt64.shiftLeft 35 +
|
||
nonDepLet.toUInt64.shiftLeft 36 +
|
||
bi.toUInt64.shiftLeft 37 +
|
||
looseBVarRange.toUInt64.shiftLeft 40
|
||
r
|
||
|
||
def Expr.mkData (h : USize) (looseBVarRange : Nat := 0) (hasFVar hasExprMVar hasLevelMVar hasLevelParam : Bool := false) : Expr.Data :=
|
||
Expr.mkDataCore h looseBVarRange hasFVar hasExprMVar hasLevelMVar hasLevelParam false BinderInfo.default
|
||
|
||
def Expr.mkDataForBinder (h : USize) (looseBVarRange : Nat) (hasFVar hasExprMVar hasLevelMVar hasLevelParam : Bool) (bi : BinderInfo) : Expr.Data :=
|
||
Expr.mkDataCore h looseBVarRange hasFVar hasExprMVar hasLevelMVar hasLevelParam false bi
|
||
|
||
def Expr.mkDataForLet (h : USize) (looseBVarRange : Nat) (hasFVar hasExprMVar hasLevelMVar hasLevelParam nonDepLet : Bool) : Expr.Data :=
|
||
Expr.mkDataCore h looseBVarRange hasFVar hasExprMVar hasLevelMVar hasLevelParam nonDepLet BinderInfo.default
|
||
|
||
open Expr
|
||
|
||
abbrev MVarId := Name
|
||
abbrev FVarId := Name
|
||
|
||
/- We use the `E` suffix (short for `Expr`) to avoid collision with keywords.
|
||
We considered using «...», but it is too inconvenient to use. -/
|
||
inductive Expr where
|
||
| bvar : Nat → Data → Expr -- bound variables
|
||
| fvar : FVarId → Data → Expr -- free variables
|
||
| mvar : MVarId → Data → Expr -- meta variables
|
||
| sort : Level → Data → Expr -- Sort
|
||
| const : Name → List Level → Data → Expr -- constants
|
||
| app : Expr → Expr → Data → Expr -- application
|
||
| lam : Name → Expr → Expr → Data → Expr -- lambda abstraction
|
||
| forallE : Name → Expr → Expr → Data → Expr -- (dependent) arrow
|
||
| letE : Name → Expr → Expr → Expr → Data → Expr -- let expressions
|
||
| lit : Literal → Data → Expr -- literals
|
||
| mdata : MData → Expr → Data → Expr -- metadata
|
||
| proj : Name → Nat → Expr → Data → Expr -- projection
|
||
deriving Inhabited
|
||
|
||
namespace Expr
|
||
|
||
@[inline] def data : Expr → Data
|
||
| bvar _ d => d
|
||
| fvar _ d => d
|
||
| mvar _ d => d
|
||
| sort _ d => d
|
||
| const _ _ d => d
|
||
| app _ _ d => d
|
||
| lam _ _ _ d => d
|
||
| forallE _ _ _ d => d
|
||
| letE _ _ _ _ d => d
|
||
| lit _ d => d
|
||
| mdata _ _ d => d
|
||
| proj _ _ _ d => d
|
||
|
||
def ctorName : Expr → String
|
||
| bvar _ _ => "bvar"
|
||
| fvar _ _ => "fvar"
|
||
| mvar _ _ => "mvar"
|
||
| sort _ _ => "sort"
|
||
| const _ _ _ => "const"
|
||
| app _ _ _ => "app"
|
||
| lam _ _ _ _ => "lam"
|
||
| forallE _ _ _ _ => "forallE"
|
||
| letE _ _ _ _ _ => "letE"
|
||
| lit _ _ => "lit"
|
||
| mdata _ _ _ => "mdata"
|
||
| proj _ _ _ _ => "proj"
|
||
|
||
protected def hash (e : Expr) : USize :=
|
||
e.data.hash
|
||
|
||
instance : Hashable Expr := ⟨Expr.hash⟩
|
||
|
||
def hasFVar (e : Expr) : Bool :=
|
||
e.data.hasFVar
|
||
|
||
def hasExprMVar (e : Expr) : Bool :=
|
||
e.data.hasExprMVar
|
||
|
||
def hasLevelMVar (e : Expr) : Bool :=
|
||
e.data.hasLevelMVar
|
||
|
||
def hasMVar (e : Expr) : Bool :=
|
||
let d := e.data
|
||
d.hasExprMVar || d.hasLevelMVar
|
||
|
||
def hasLevelParam (e : Expr) : Bool :=
|
||
e.data.hasLevelParam
|
||
|
||
def looseBVarRange (e : Expr) : Nat :=
|
||
e.data.looseBVarRange.toNat
|
||
|
||
def binderInfo (e : Expr) : BinderInfo :=
|
||
e.data.binderInfo
|
||
|
||
@[export lean_expr_hash] def hashEx : Expr → USize := hash
|
||
@[export lean_expr_has_fvar] def hasFVarEx : Expr → Bool := hasFVar
|
||
@[export lean_expr_has_expr_mvar] def hasExprMVarEx : Expr → Bool := hasExprMVar
|
||
@[export lean_expr_has_level_mvar] def hasLevelMVarEx : Expr → Bool := hasLevelMVar
|
||
@[export lean_expr_has_mvar] def hasMVarEx : Expr → Bool := hasMVar
|
||
@[export lean_expr_has_level_param] def hasLevelParamEx : Expr → Bool := hasLevelParam
|
||
@[export lean_expr_loose_bvar_range] def looseBVarRangeEx (e : Expr) : UInt32 := e.data.looseBVarRange
|
||
@[export lean_expr_binder_info] def binderInfoEx : Expr → BinderInfo := binderInfo
|
||
|
||
end Expr
|
||
|
||
def mkLit (l : Literal) : Expr :=
|
||
Expr.lit l $ mkData (mixHash 3 (hash l))
|
||
|
||
def mkNatLit (n : Nat) : Expr :=
|
||
mkLit (Literal.natVal n)
|
||
|
||
def mkStrLit (s : String) : Expr :=
|
||
mkLit (Literal.strVal s)
|
||
|
||
def mkConst (n : Name) (lvls : List Level := []) : Expr :=
|
||
Expr.const n lvls $ mkData (mixHash 5 $ mixHash (hash n) (hash lvls)) 0 false false (lvls.any Level.hasMVar) (lvls.any Level.hasParam)
|
||
|
||
def Literal.type : Literal → Expr
|
||
| Literal.natVal _ => mkConst `Nat
|
||
| Literal.strVal _ => mkConst `String
|
||
|
||
@[export lean_lit_type]
|
||
def Literal.typeEx : Literal → Expr := Literal.type
|
||
|
||
def mkBVar (idx : Nat) : Expr :=
|
||
Expr.bvar idx $ mkData (mixHash 7 $ hash idx) (idx+1)
|
||
|
||
def mkSort (lvl : Level) : Expr :=
|
||
Expr.sort lvl $ mkData (mixHash 11 $ hash lvl) 0 false false lvl.hasMVar lvl.hasParam
|
||
|
||
def mkFVar (fvarId : FVarId) : Expr :=
|
||
Expr.fvar fvarId $ mkData (mixHash 13 $ hash fvarId) 0 true
|
||
|
||
def mkMVar (fvarId : MVarId) : Expr :=
|
||
Expr.mvar fvarId $ mkData (mixHash 17 $ hash fvarId) 0 false true
|
||
|
||
def mkMData (d : MData) (e : Expr) : Expr :=
|
||
Expr.mdata d e $ mkData (mixHash 19 $ hash e) e.looseBVarRange e.hasFVar e.hasExprMVar e.hasLevelMVar e.hasLevelParam
|
||
|
||
def mkProj (s : Name) (i : Nat) (e : Expr) : Expr :=
|
||
Expr.proj s i e $ mkData (mixHash 23 $ mixHash (hash s) $ mixHash (hash i) (hash e))
|
||
e.looseBVarRange e.hasFVar e.hasExprMVar e.hasLevelMVar e.hasLevelParam
|
||
|
||
def mkApp (f a : Expr) : Expr :=
|
||
Expr.app f a $ mkData (mixHash 29 $ mixHash (hash f) (hash a))
|
||
(Nat.max f.looseBVarRange a.looseBVarRange)
|
||
(f.hasFVar || a.hasFVar)
|
||
(f.hasExprMVar || a.hasExprMVar)
|
||
(f.hasLevelMVar || a.hasLevelMVar)
|
||
(f.hasLevelParam || a.hasLevelParam)
|
||
|
||
def mkLambda (x : Name) (bi : BinderInfo) (t : Expr) (b : Expr) : Expr :=
|
||
-- let x := x.eraseMacroScopes
|
||
Expr.lam x t b $ mkDataForBinder (mixHash 31 $ mixHash (hash t) (hash b))
|
||
(Nat.max t.looseBVarRange (b.looseBVarRange - 1))
|
||
(t.hasFVar || b.hasFVar)
|
||
(t.hasExprMVar || b.hasExprMVar)
|
||
(t.hasLevelMVar || b.hasLevelMVar)
|
||
(t.hasLevelParam || b.hasLevelParam)
|
||
bi
|
||
|
||
def mkForall (x : Name) (bi : BinderInfo) (t : Expr) (b : Expr) : Expr :=
|
||
-- let x := x.eraseMacroScopes
|
||
Expr.forallE x t b $ mkDataForBinder (mixHash 37 $ mixHash (hash t) (hash b))
|
||
(Nat.max t.looseBVarRange (b.looseBVarRange - 1))
|
||
(t.hasFVar || b.hasFVar)
|
||
(t.hasExprMVar || b.hasExprMVar)
|
||
(t.hasLevelMVar || b.hasLevelMVar)
|
||
(t.hasLevelParam || b.hasLevelParam)
|
||
bi
|
||
|
||
/- Return `Unit -> type`. Do not confuse with `Thunk type` -/
|
||
def mkSimpleThunkType (type : Expr) : Expr :=
|
||
mkForall Name.anonymous BinderInfo.default (Lean.mkConst `Unit) type
|
||
|
||
/- Return `fun (_ : Unit), e` -/
|
||
def mkSimpleThunk (type : Expr) : Expr :=
|
||
mkLambda `_ BinderInfo.default (Lean.mkConst `Unit) type
|
||
|
||
def mkLet (x : Name) (t : Expr) (v : Expr) (b : Expr) (nonDep : Bool := false) : Expr :=
|
||
-- let x := x.eraseMacroScopes
|
||
Expr.letE x t v b $ mkDataForLet (mixHash 41 $ mixHash (hash t) $ mixHash (hash v) (hash b))
|
||
(Nat.max (Nat.max t.looseBVarRange v.looseBVarRange) (b.looseBVarRange - 1))
|
||
(t.hasFVar || v.hasFVar || b.hasFVar)
|
||
(t.hasExprMVar || v.hasExprMVar || b.hasExprMVar)
|
||
(t.hasLevelMVar || v.hasLevelMVar || b.hasLevelMVar)
|
||
(t.hasLevelParam || v.hasLevelParam || b.hasLevelParam)
|
||
nonDep
|
||
|
||
@[export lean_expr_mk_bvar] def mkBVarEx : Nat → Expr := mkBVar
|
||
@[export lean_expr_mk_fvar] def mkFVarEx : FVarId → Expr := mkFVar
|
||
@[export lean_expr_mk_mvar] def mkMVarEx : MVarId → Expr := mkMVar
|
||
@[export lean_expr_mk_sort] def mkSortEx : Level → Expr := mkSort
|
||
@[export lean_expr_mk_const] def mkConstEx (c : Name) (lvls : List Level) : Expr := mkConst c lvls
|
||
@[export lean_expr_mk_app] def mkAppEx : Expr → Expr → Expr := mkApp
|
||
@[export lean_expr_mk_lambda] def mkLambdaEx (n : Name) (d b : Expr) (bi : BinderInfo) : Expr := mkLambda n bi d b
|
||
@[export lean_expr_mk_forall] def mkForallEx (n : Name) (d b : Expr) (bi : BinderInfo) : Expr := mkForall n bi d b
|
||
@[export lean_expr_mk_let] def mkLetEx (n : Name) (t v b : Expr) : Expr := mkLet n t v b
|
||
@[export lean_expr_mk_lit] def mkLitEx : Literal → Expr := mkLit
|
||
@[export lean_expr_mk_mdata] def mkMDataEx : MData → Expr → Expr := mkMData
|
||
@[export lean_expr_mk_proj] def mkProjEx : Name → Nat → Expr → Expr := mkProj
|
||
|
||
def mkAppN (f : Expr) (args : Array Expr) : Expr :=
|
||
args.foldl mkApp f
|
||
|
||
private partial def mkAppRangeAux (n : Nat) (args : Array Expr) (i : Nat) (e : Expr) : Expr :=
|
||
if i < n then mkAppRangeAux n args (i+1) (mkApp e (args.get! i)) else e
|
||
|
||
/-- `mkAppRange f i j #[a_1, ..., a_i, ..., a_j, ... ]` ==> the expression `f a_i ... a_{j-1}` -/
|
||
def mkAppRange (f : Expr) (i j : Nat) (args : Array Expr) : Expr :=
|
||
mkAppRangeAux j args i f
|
||
|
||
def mkAppRev (fn : Expr) (revArgs : Array Expr) : Expr :=
|
||
revArgs.foldr (fun a r => mkApp r a) fn
|
||
|
||
namespace Expr
|
||
-- TODO: implement it in Lean
|
||
@[extern "lean_expr_dbg_to_string"]
|
||
constant dbgToString (e : @& Expr) : String
|
||
|
||
@[extern "lean_expr_quick_lt"]
|
||
constant quickLt (a : @& Expr) (b : @& Expr) : Bool
|
||
|
||
@[extern "lean_expr_lt"]
|
||
constant lt (a : @& Expr) (b : @& Expr) : Bool
|
||
|
||
/- Return true iff `a` and `b` are alpha equivalent.
|
||
Binder annotations are ignored. -/
|
||
@[extern "lean_expr_eqv"]
|
||
constant eqv (a : @& Expr) (b : @& Expr) : Bool
|
||
|
||
instance : BEq Expr where
|
||
beq := Expr.eqv
|
||
|
||
/- Return true iff `a` and `b` are equal.
|
||
Binder names and annotations are taking into account. -/
|
||
@[extern "lean_expr_equal"]
|
||
constant equal (a : @& Expr) (b : @& Expr) : Bool
|
||
|
||
def isSort : Expr → Bool
|
||
| sort _ _ => true
|
||
| _ => false
|
||
|
||
def isProp : Expr → Bool
|
||
| sort (Level.zero ..) _ => true
|
||
| _ => false
|
||
|
||
def isBVar : Expr → Bool
|
||
| bvar _ _ => true
|
||
| _ => false
|
||
|
||
def isMVar : Expr → Bool
|
||
| mvar _ _ => true
|
||
| _ => false
|
||
|
||
def isFVar : Expr → Bool
|
||
| fvar _ _ => true
|
||
| _ => false
|
||
|
||
def isApp : Expr → Bool
|
||
| app .. => true
|
||
| _ => false
|
||
|
||
def isProj : Expr → Bool
|
||
| proj .. => true
|
||
| _ => false
|
||
|
||
def isConst : Expr → Bool
|
||
| const .. => true
|
||
| _ => false
|
||
|
||
def isConstOf : Expr → Name → Bool
|
||
| const n _ _, m => n == m
|
||
| _, _ => false
|
||
|
||
def isForall : Expr → Bool
|
||
| forallE .. => true
|
||
| _ => false
|
||
|
||
def isLambda : Expr → Bool
|
||
| lam .. => true
|
||
| _ => false
|
||
|
||
def isBinding : Expr → Bool
|
||
| lam .. => true
|
||
| forallE .. => true
|
||
| _ => false
|
||
|
||
def isLet : Expr → Bool
|
||
| letE .. => true
|
||
| _ => false
|
||
|
||
def isMData : Expr → Bool
|
||
| mdata .. => true
|
||
| _ => false
|
||
|
||
def isLit : Expr → Bool
|
||
| lit .. => true
|
||
| _ => false
|
||
|
||
def getAppFn : Expr → Expr
|
||
| app f a _ => getAppFn f
|
||
| e => e
|
||
|
||
def getAppNumArgsAux : Expr → Nat → Nat
|
||
| app f a _, n => getAppNumArgsAux f (n+1)
|
||
| e, n => n
|
||
|
||
def getAppNumArgs (e : Expr) : Nat :=
|
||
getAppNumArgsAux e 0
|
||
|
||
private def getAppArgsAux : Expr → Array Expr → Nat → Array Expr
|
||
| app f a _, as, i => getAppArgsAux f (as.set! i a) (i-1)
|
||
| _, as, _ => as
|
||
|
||
@[inline] def getAppArgs (e : Expr) : Array Expr :=
|
||
let dummy := mkSort levelZero
|
||
let nargs := e.getAppNumArgs
|
||
getAppArgsAux e (mkArray nargs dummy) (nargs-1)
|
||
|
||
private def getAppRevArgsAux : Expr → Array Expr → Array Expr
|
||
| app f a _, as => getAppRevArgsAux f (as.push a)
|
||
| _, as => as
|
||
|
||
@[inline] def getAppRevArgs (e : Expr) : Array Expr :=
|
||
getAppRevArgsAux e (Array.mkEmpty e.getAppNumArgs)
|
||
|
||
@[specialize] def withAppAux (k : Expr → Array Expr → α) : Expr → Array Expr → Nat → α
|
||
| app f a _, as, i => withAppAux k f (as.set! i a) (i-1)
|
||
| f, as, i => k f as
|
||
|
||
@[inline] def withApp (e : Expr) (k : Expr → Array Expr → α) : α :=
|
||
let dummy := mkSort levelZero
|
||
let nargs := e.getAppNumArgs
|
||
withAppAux k e (mkArray nargs dummy) (nargs-1)
|
||
|
||
@[specialize] private def withAppRevAux (k : Expr → Array Expr → α) : Expr → Array Expr → α
|
||
| app f a _, as => withAppRevAux k f (as.push a)
|
||
| f, as => k f as
|
||
|
||
@[inline] def withAppRev (e : Expr) (k : Expr → Array Expr → α) : α :=
|
||
withAppRevAux k e (Array.mkEmpty e.getAppNumArgs)
|
||
|
||
def getRevArgD : Expr → Nat → Expr → Expr
|
||
| app f a _, 0, _ => a
|
||
| app f _ _, i+1, v => getRevArgD f i v
|
||
| _, _, v => v
|
||
|
||
def getRevArg! : Expr → Nat → Expr
|
||
| app f a _, 0 => a
|
||
| app f _ _, i+1 => getRevArg! f i
|
||
| _, _ => panic! "invalid index"
|
||
|
||
@[inline] def getArg! (e : Expr) (i : Nat) (n := e.getAppNumArgs) : Expr :=
|
||
getRevArg! e (n - i - 1)
|
||
|
||
@[inline] def getArgD (e : Expr) (i : Nat) (v₀ : Expr) (n := e.getAppNumArgs) : Expr :=
|
||
getRevArgD e (n - i - 1) v₀
|
||
|
||
def isAppOf (e : Expr) (n : Name) : Bool :=
|
||
match e.getAppFn with
|
||
| const c _ _ => c == n
|
||
| _ => false
|
||
|
||
def isAppOfArity : Expr → Name → Nat → Bool
|
||
| const c _ _, n, 0 => c == n
|
||
| app f _ _, n, a+1 => isAppOfArity f n a
|
||
| _, _, _ => false
|
||
|
||
def appFn! : Expr → Expr
|
||
| app f _ _ => f
|
||
| _ => panic! "application expected"
|
||
|
||
def appArg! : Expr → Expr
|
||
| app _ a _ => a
|
||
| _ => panic! "application expected"
|
||
|
||
def isNatLit : Expr → Bool
|
||
| lit (Literal.natVal _) _ => true
|
||
| _ => false
|
||
|
||
def natLit? : Expr → Option Nat
|
||
| lit (Literal.natVal v) _ => v
|
||
| _ => none
|
||
|
||
def isStringLit : Expr → Bool
|
||
| lit (Literal.strVal _) _ => true
|
||
| _ => false
|
||
|
||
def isCharLit (e : Expr) : Bool :=
|
||
e.isAppOfArity `Char.ofNat 1 && e.appArg!.isNatLit
|
||
|
||
def constName! : Expr → Name
|
||
| const n _ _ => n
|
||
| _ => panic! "constant expected"
|
||
|
||
def constName? : Expr → Option Name
|
||
| const n _ _ => some n
|
||
| _ => none
|
||
|
||
def constLevels! : Expr → List Level
|
||
| const _ ls _ => ls
|
||
| _ => panic! "constant expected"
|
||
|
||
def bvarIdx! : Expr → Nat
|
||
| bvar idx _ => idx
|
||
| _ => panic! "bvar expected"
|
||
|
||
def fvarId! : Expr → FVarId
|
||
| fvar n _ => n
|
||
| _ => panic! "fvar expected"
|
||
|
||
def mvarId! : Expr → MVarId
|
||
| mvar n _ => n
|
||
| _ => panic! "mvar expected"
|
||
|
||
def bindingName! : Expr → Name
|
||
| forallE n _ _ _ => n
|
||
| lam n _ _ _ => n
|
||
| _ => panic! "binding expected"
|
||
|
||
def bindingDomain! : Expr → Expr
|
||
| forallE _ d _ _ => d
|
||
| lam _ d _ _ => d
|
||
| _ => panic! "binding expected"
|
||
|
||
def bindingBody! : Expr → Expr
|
||
| forallE _ _ b _ => b
|
||
| lam _ _ b _ => b
|
||
| _ => panic! "binding expected"
|
||
|
||
def bindingInfo! : Expr → BinderInfo
|
||
| forallE _ _ _ c => c.binderInfo
|
||
| lam _ _ _ c => c.binderInfo
|
||
| _ => panic! "binding expected"
|
||
|
||
def letName! : Expr → Name
|
||
| letE n _ _ _ _ => n
|
||
| _ => panic! "let expression expected"
|
||
|
||
def consumeMData : Expr → Expr
|
||
| mdata _ e _ => consumeMData e
|
||
| e => e
|
||
|
||
def hasLooseBVars (e : Expr) : Bool :=
|
||
e.looseBVarRange > 0
|
||
|
||
/- Remark: the following function assumes `e` does not have loose bound variables. -/
|
||
def isArrow (e : Expr) : Bool :=
|
||
match e with
|
||
| forallE _ _ b _ => !b.hasLooseBVars
|
||
| _ => false
|
||
|
||
@[extern "lean_expr_has_loose_bvar"]
|
||
constant hasLooseBVar (e : @& Expr) (bvarIdx : @& Nat) : Bool
|
||
|
||
/-- Return true if `e` contains the loose bound variable `bvarIdx` in an explicit parameter, or in the range if `tryRange == true`. -/
|
||
def hasLooseBVarInExplicitDomain : Expr → Nat → Bool → Bool
|
||
| Expr.forallE _ d b c, bvarIdx, tryRange => (c.binderInfo.isExplicit && hasLooseBVar d bvarIdx) || hasLooseBVarInExplicitDomain b (bvarIdx+1) tryRange
|
||
| e, bvarIdx, tryRange => tryRange && hasLooseBVar e bvarIdx
|
||
|
||
/--
|
||
Lower the loose bound variables `>= s` in `e` by `d`.
|
||
That is, a loose bound variable `bvar i`.
|
||
`i >= s` is mapped into `bvar (i-d)`.
|
||
|
||
Remark: if `s < d`, then result is `e` -/
|
||
@[extern "lean_expr_lower_loose_bvars"]
|
||
constant lowerLooseBVars (e : @& Expr) (s d : @& Nat) : Expr
|
||
|
||
/--
|
||
Lift loose bound variables `>= s` in `e` by `d`. -/
|
||
@[extern "lean_expr_lift_loose_bvars"]
|
||
constant liftLooseBVars (e : @& Expr) (s d : @& Nat) : Expr
|
||
|
||
/--
|
||
`inferImplicit e numParams considerRange` updates the first `numParams` parameter binder annotations of the `e` forall type.
|
||
It marks any parameter with an explicit binder annotation if there is another explicit arguments that depends on it or
|
||
the resulting type if `considerRange == true`.
|
||
|
||
Remark: we use this function to infer the bind annotations of inductive datatype constructors, and structure projections.
|
||
When the `{}` annotation is used in these commands, we set `considerRange == false`.
|
||
-/
|
||
def inferImplicit : Expr → Nat → Bool → Expr
|
||
| Expr.forallE n d b c, i+1, considerRange =>
|
||
let b := inferImplicit b i considerRange
|
||
let newInfo := if c.binderInfo.isExplicit && hasLooseBVarInExplicitDomain b 0 considerRange then BinderInfo.implicit else c.binderInfo
|
||
mkForall n newInfo d b
|
||
| e, 0, _ => e
|
||
| e, _, _ => e
|
||
|
||
/-- Instantiate the loose bound variables in `e` using `subst`.
|
||
That is, a loose `Expr.bvar i` is replaced with `subst[i]`. -/
|
||
@[extern "lean_expr_instantiate"]
|
||
constant instantiate (e : @& Expr) (subst : @& Array Expr) : Expr
|
||
|
||
@[extern "lean_expr_instantiate1"]
|
||
constant instantiate1 (e : @& Expr) (subst : @& Expr) : Expr
|
||
|
||
/-- Similar to instantiate, but `Expr.bvar i` is replaced with `subst[subst.size - i - 1]` -/
|
||
@[extern "lean_expr_instantiate_rev"]
|
||
constant instantiateRev (e : @& Expr) (subst : @& Array Expr) : Expr
|
||
|
||
/-- Similar to `instantiate`, but consider only the variables `xs` in the range `[beginIdx, endIdx)`.
|
||
Function panics if `beginIdx <= endIdx <= xs.size` does not hold. -/
|
||
@[extern "lean_expr_instantiate_range"]
|
||
constant instantiateRange (e : @& Expr) (beginIdx endIdx : @& Nat) (xs : @& Array Expr) : Expr
|
||
|
||
/-- Similar to `instantiateRev`, but consider only the variables `xs` in the range `[beginIdx, endIdx)`.
|
||
Function panics if `beginIdx <= endIdx <= xs.size` does not hold. -/
|
||
@[extern "lean_expr_instantiate_rev_range"]
|
||
constant instantiateRevRange (e : @& Expr) (beginIdx endIdx : @& Nat) (xs : @& Array Expr) : Expr
|
||
|
||
/-- Replace free variables `xs` with loose bound variables. -/
|
||
@[extern "lean_expr_abstract"]
|
||
constant abstract (e : @& Expr) (xs : @& Array Expr) : Expr
|
||
|
||
/-- Similar to `abstract`, but consider only the first `min n xs.size` entries in `xs`. -/
|
||
@[extern "lean_expr_abstract_range"]
|
||
constant abstractRange (e : @& Expr) (n : @& Nat) (xs : @& Array Expr) : Expr
|
||
|
||
/-- Replace occurrences of the free variable `fvar` in `e` with `v` -/
|
||
def replaceFVar (e : Expr) (fvar : Expr) (v : Expr) : Expr :=
|
||
(e.abstract #[fvar]).instantiate1 v
|
||
|
||
/-- Replace occurrences of the free variable `fvarId` in `e` with `v` -/
|
||
def replaceFVarId (e : Expr) (fvarId : FVarId) (v : Expr) : Expr :=
|
||
replaceFVar e (mkFVar fvarId) v
|
||
|
||
/-- Replace occurrences of the free variables `fvars` in `e` with `vs` -/
|
||
def replaceFVars (e : Expr) (fvars : Array Expr) (vs : Array Expr) : Expr :=
|
||
(e.abstract fvars).instantiateRev vs
|
||
|
||
instance : ToString Expr where
|
||
toString := Expr.dbgToString
|
||
|
||
def isAtomic : Expr → Bool
|
||
| Expr.const _ _ _ => true
|
||
| Expr.sort _ _ => true
|
||
| Expr.bvar _ _ => true
|
||
| Expr.lit _ _ => true
|
||
| Expr.mvar _ _ => true
|
||
| Expr.fvar _ _ => true
|
||
| _ => false
|
||
|
||
end Expr
|
||
|
||
def mkAppB (f a b : Expr) := mkApp (mkApp f a) b
|
||
def mkApp2 (f a b : Expr) := mkAppB f a b
|
||
def mkApp3 (f a b c : Expr) := mkApp (mkAppB f a b) c
|
||
def mkApp4 (f a b c d : Expr) := mkAppB (mkAppB f a b) c d
|
||
def mkApp5 (f a b c d e : Expr) := mkApp (mkApp4 f a b c d) e
|
||
def mkApp6 (f a b c d e₁ e₂ : Expr) := mkAppB (mkApp4 f a b c d) e₁ e₂
|
||
def mkApp7 (f a b c d e₁ e₂ e₃ : Expr) := mkApp3 (mkApp4 f a b c d) e₁ e₂ e₃
|
||
def mkApp8 (f a b c d e₁ e₂ e₃ e₄ : Expr) := mkApp4 (mkApp4 f a b c d) e₁ e₂ e₃ e₄
|
||
def mkApp9 (f a b c d e₁ e₂ e₃ e₄ e₅ : Expr) := mkApp5 (mkApp4 f a b c d) e₁ e₂ e₃ e₄ e₅
|
||
def mkApp10 (f a b c d e₁ e₂ e₃ e₄ e₅ e₆ : Expr) := mkApp6 (mkApp4 f a b c d) e₁ e₂ e₃ e₄ e₅ e₆
|
||
|
||
def mkDecIsTrue (pred proof : Expr) :=
|
||
mkAppB (mkConst `Decidable.isTrue) pred proof
|
||
|
||
def mkDecIsFalse (pred proof : Expr) :=
|
||
mkAppB (mkConst `Decidable.isFalse) pred proof
|
||
|
||
open Std (HashMap HashSet PHashMap PHashSet)
|
||
|
||
abbrev ExprMap (α : Type) := HashMap Expr α
|
||
abbrev PersistentExprMap (α : Type) := PHashMap Expr α
|
||
abbrev ExprSet := HashSet Expr
|
||
abbrev PersistentExprSet := PHashSet Expr
|
||
abbrev PExprSet := PersistentExprSet
|
||
|
||
/- Auxiliary type for forcing `==` to be structural equality for `Expr` -/
|
||
structure ExprStructEq where
|
||
val : Expr
|
||
deriving Inhabited
|
||
|
||
instance : Coe Expr ExprStructEq := ⟨ExprStructEq.mk⟩
|
||
|
||
namespace ExprStructEq
|
||
|
||
protected def beq : ExprStructEq → ExprStructEq → Bool
|
||
| ⟨e₁⟩, ⟨e₂⟩ => Expr.equal e₁ e₂
|
||
|
||
protected def hash : ExprStructEq → USize
|
||
| ⟨e⟩ => e.hash
|
||
|
||
instance : BEq ExprStructEq := ⟨ExprStructEq.beq⟩
|
||
instance : Hashable ExprStructEq := ⟨ExprStructEq.hash⟩
|
||
instance : ToString ExprStructEq := ⟨fun e => toString e.val⟩
|
||
|
||
end ExprStructEq
|
||
|
||
abbrev ExprStructMap (α : Type) := HashMap ExprStructEq α
|
||
abbrev PersistentExprStructMap (α : Type) := PHashMap ExprStructEq α
|
||
|
||
namespace Expr
|
||
|
||
private partial def mkAppRevRangeAux (revArgs : Array Expr) (start : Nat) (b : Expr) (i : Nat) : Expr :=
|
||
if i == start then b
|
||
else
|
||
let i := i - 1
|
||
mkAppRevRangeAux revArgs start (mkApp b (revArgs.get! i)) i
|
||
|
||
/-- `mkAppRevRange f b e args == mkAppRev f (revArgs.extract b e)` -/
|
||
def mkAppRevRange (f : Expr) (beginIdx endIdx : Nat) (revArgs : Array Expr) : Expr :=
|
||
mkAppRevRangeAux revArgs beginIdx f endIdx
|
||
|
||
private def betaRevAux (revArgs : Array Expr) (sz : Nat) : Expr → Nat → Expr
|
||
| Expr.lam _ _ b _, i =>
|
||
if i + 1 < sz then
|
||
betaRevAux revArgs sz b (i+1)
|
||
else
|
||
let n := sz - (i + 1)
|
||
mkAppRevRange (b.instantiateRange n sz revArgs) 0 n revArgs
|
||
| Expr.mdata _ b _, i => betaRevAux revArgs sz b i
|
||
| b, i =>
|
||
let n := sz - i
|
||
mkAppRevRange (b.instantiateRange n sz revArgs) 0 n revArgs
|
||
|
||
/-- If `f` is a lambda expression, than "beta-reduce" it using `revArgs`.
|
||
This function is often used with `getAppRev` or `withAppRev`.
|
||
Examples:
|
||
- `betaRev (fun x y => t x y) #[]` ==> `fun x y => t x y`
|
||
- `betaRev (fun x y => t x y) #[a]` ==> `fun y => t a y`
|
||
- `betaRev (fun x y => t x y) #[a, b]` ==> t b a`
|
||
- `betaRev (fun x y => t x y) #[a, b, c, d]` ==> t d c b a`
|
||
Suppose `t` is `(fun x y => t x y) a b c d`, then
|
||
`args := t.getAppRev` is `#[d, c, b, a]`,
|
||
and `betaRev (fun x y => t x y) #[d, c, b, a]` is `t a b c d`. -/
|
||
def betaRev (f : Expr) (revArgs : Array Expr) : Expr :=
|
||
if revArgs.size == 0 then f
|
||
else betaRevAux revArgs revArgs.size f 0
|
||
|
||
def isHeadBetaTargetFn : Expr → Bool
|
||
| Expr.lam _ _ _ _ => true
|
||
| Expr.mdata _ b _ => isHeadBetaTargetFn b
|
||
| _ => false
|
||
|
||
def headBeta (e : Expr) : Expr :=
|
||
let f := e.getAppFn
|
||
if f.isHeadBetaTargetFn then betaRev f e.getAppRevArgs else e
|
||
|
||
def isHeadBetaTarget (e : Expr) : Bool :=
|
||
e.getAppFn.isHeadBetaTargetFn
|
||
|
||
private def etaExpandedBody : Expr → Nat → Nat → Option Expr
|
||
| app f (bvar j _) _, n+1, i => if j == i then etaExpandedBody f n (i+1) else none
|
||
| _, n+1, _ => none
|
||
| f, 0, _ => if f.hasLooseBVars then none else some f
|
||
|
||
private def etaExpandedAux : Expr → Nat → Option Expr
|
||
| lam _ _ b _, n => etaExpandedAux b (n+1)
|
||
| e, n => etaExpandedBody e n 0
|
||
|
||
/--
|
||
If `e` is of the form `(fun x₁ ... xₙ => f x₁ ... xₙ)` and `f` does not contain `x₁`, ..., `xₙ`,
|
||
then return `some f`. Otherwise, return `none`.
|
||
|
||
It assumes `e` does not have loose bound variables.
|
||
|
||
Remark: `ₙ` may be 0 -/
|
||
def etaExpanded? (e : Expr) : Option Expr :=
|
||
etaExpandedAux e 0
|
||
|
||
/-- Similar to `etaExpanded?`, but only succeeds if `ₙ ≥ 1`. -/
|
||
def etaExpandedStrict? : Expr → Option Expr
|
||
| lam _ _ b _ => etaExpandedAux b 1
|
||
| _ => none
|
||
|
||
def getOptParamDefault? (e : Expr) : Option Expr :=
|
||
if e.isAppOfArity `optParam 2 then
|
||
some e.appArg!
|
||
else
|
||
none
|
||
|
||
def getAutoParamTactic? (e : Expr) : Option Expr :=
|
||
if e.isAppOfArity `autoParam 2 then
|
||
some e.appArg!
|
||
else
|
||
none
|
||
|
||
def isOptParam (e : Expr) : Bool :=
|
||
e.isAppOfArity `optParam 2
|
||
|
||
def isAutoParam (e : Expr) : Bool :=
|
||
e.isAppOfArity `autoParam 2
|
||
|
||
/-- Return true iff `e` contains a free variable which statisfies `p`. -/
|
||
@[inline] def hasAnyFVar (e : Expr) (p : FVarId → Bool) : Bool :=
|
||
let rec @[specialize] visit (e : Expr) := if !e.hasFVar then false else
|
||
match e with
|
||
| Expr.forallE _ d b _ => visit d || visit b
|
||
| Expr.lam _ d b _ => visit d || visit b
|
||
| Expr.mdata _ e _ => visit e
|
||
| Expr.letE _ t v b _ => visit t || visit v || visit b
|
||
| Expr.app f a _ => visit f || visit a
|
||
| Expr.proj _ _ e _ => visit e
|
||
| e@(Expr.fvar fvarId _) => p fvarId
|
||
| e => false
|
||
visit e
|
||
|
||
def containsFVar (e : Expr) (fvarId : FVarId) : Bool :=
|
||
e.hasAnyFVar (· == fvarId)
|
||
|
||
/- The update functions here are defined using C code. They will try to avoid
|
||
allocating new values using pointer equality.
|
||
The hypotheses `(h : e.is... = true)` are used to ensure Lean will not crash
|
||
at runtime.
|
||
The `update*!` functions are inlined and provide a convenient way of using the
|
||
update proofs without providing proofs.
|
||
Note that if they are used under a match-expression, the compiler will eliminate
|
||
the double-match. -/
|
||
|
||
@[extern "lean_expr_update_app"]
|
||
def updateApp (e : Expr) (newFn : Expr) (newArg : Expr) (h : e.isApp = true) : Expr :=
|
||
mkApp newFn newArg
|
||
|
||
@[inline] def updateApp! (e : Expr) (newFn : Expr) (newArg : Expr) : Expr :=
|
||
match e with
|
||
| app fn arg c => updateApp (app fn arg c) newFn newArg rfl
|
||
| _ => panic! "application expected"
|
||
|
||
@[extern "lean_expr_update_const"]
|
||
def updateConst (e : Expr) (newLevels : List Level) (h : e.isConst = true) : Expr :=
|
||
mkConst e.constName! newLevels
|
||
|
||
@[inline] def updateConst! (e : Expr) (newLevels : List Level) : Expr :=
|
||
match e with
|
||
| const n ls c => updateConst (const n ls c) newLevels rfl
|
||
| _ => panic! "constant expected"
|
||
|
||
@[extern "lean_expr_update_sort"]
|
||
def updateSort (e : Expr) (newLevel : Level) (h : e.isSort = true) : Expr :=
|
||
mkSort newLevel
|
||
|
||
@[inline] def updateSort! (e : Expr) (newLevel : Level) : Expr :=
|
||
match e with
|
||
| sort l c => updateSort (sort l c) newLevel rfl
|
||
| _ => panic! "level expected"
|
||
|
||
@[extern "lean_expr_update_proj"]
|
||
def updateProj (e : Expr) (newExpr : Expr) (h : e.isProj = true) : Expr :=
|
||
match e with
|
||
| proj s i _ _ => mkProj s i newExpr
|
||
| _ => e -- unreachable because of `h`
|
||
|
||
@[extern "lean_expr_update_mdata"]
|
||
def updateMData (e : Expr) (newExpr : Expr) (h : e.isMData = true) : Expr :=
|
||
match e with
|
||
| mdata d _ _ => mkMData d newExpr
|
||
| _ => e -- unreachable because of `h`
|
||
|
||
@[inline] def updateMData! (e : Expr) (newExpr : Expr) : Expr :=
|
||
match e with
|
||
| mdata d e c => updateMData (mdata d e c) newExpr rfl
|
||
| _ => panic! "mdata expected"
|
||
|
||
@[inline] def updateProj! (e : Expr) (newExpr : Expr) : Expr :=
|
||
match e with
|
||
| proj s i e c => updateProj (proj s i e c) newExpr rfl
|
||
| _ => panic! "proj expected"
|
||
|
||
@[extern "lean_expr_update_forall"]
|
||
def updateForall (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) (h : e.isForall = true) : Expr :=
|
||
mkForall e.bindingName! newBinfo newDomain newBody
|
||
|
||
@[inline] def updateForall! (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| forallE n d b c => updateForall (forallE n d b c) newBinfo newDomain newBody rfl
|
||
| _ => panic! "forall expected"
|
||
|
||
@[inline] def updateForallE! (e : Expr) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| forallE n d b c => updateForall (forallE n d b c) c.binderInfo newDomain newBody rfl
|
||
| _ => panic! "forall expected"
|
||
|
||
@[extern "lean_expr_update_lambda"]
|
||
def updateLambda (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) (h : e.isLambda = true) : Expr :=
|
||
mkLambda e.bindingName! newBinfo newDomain newBody
|
||
|
||
@[inline] def updateLambda! (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| lam n d b c => updateLambda (lam n d b c) newBinfo newDomain newBody rfl
|
||
| _ => panic! "lambda expected"
|
||
|
||
@[inline] def updateLambdaE! (e : Expr) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| lam n d b c => updateLambda (lam n d b c) c.binderInfo newDomain newBody rfl
|
||
| _ => panic! "lambda expected"
|
||
|
||
@[extern "lean_expr_update_let"]
|
||
def updateLet (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) (h : e.isLet = true) : Expr :=
|
||
mkLet e.letName! newType newVal newBody
|
||
|
||
@[inline] def updateLet! (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| letE n t v b c => updateLet (letE n t v b c) newType newVal newBody rfl
|
||
| _ => panic! "let expression expected"
|
||
|
||
def updateFn : Expr → Expr → Expr
|
||
| e@(app f a _), g => e.updateApp! (updateFn f g) a
|
||
| _, g => g
|
||
|
||
/- Instantiate level parameters -/
|
||
|
||
@[inline] def instantiateLevelParamsCore (s : Name → Option Level) (e : Expr) : Expr :=
|
||
let rec @[specialize] visit (e : Expr) : Expr :=
|
||
if !e.hasLevelParam then e
|
||
else match e with
|
||
| lam n d b _ => e.updateLambdaE! (visit d) (visit b)
|
||
| forallE n d b _ => e.updateForallE! (visit d) (visit b)
|
||
| letE n t v b _ => e.updateLet! (visit t) (visit v) (visit b)
|
||
| app f a _ => e.updateApp! (visit f) (visit a)
|
||
| proj _ _ s _ => e.updateProj! (visit s)
|
||
| mdata _ b _ => e.updateMData! (visit b)
|
||
| const _ us _ => e.updateConst! (us.map (fun u => u.instantiateParams s))
|
||
| sort u _ => e.updateSort! (u.instantiateParams s)
|
||
| e => e
|
||
visit e
|
||
|
||
private def getParamSubst : List Name → List Level → Name → Option Level
|
||
| p::ps, u::us, p' => if p == p' then some u else getParamSubst ps us p'
|
||
| _, _, _ => none
|
||
|
||
def instantiateLevelParams (e : Expr) (paramNames : List Name) (lvls : List Level) : Expr :=
|
||
instantiateLevelParamsCore (getParamSubst paramNames lvls) e
|
||
|
||
private partial def getParamSubstArray (ps : Array Name) (us : Array Level) (p' : Name) (i : Nat) : Option Level :=
|
||
if h : i < ps.size then
|
||
let p := ps.get ⟨i, h⟩
|
||
if h : i < us.size then
|
||
let u := us.get ⟨i, h⟩
|
||
if p == p' then some u else getParamSubstArray ps us p' (i+1)
|
||
else none
|
||
else none
|
||
|
||
def instantiateLevelParamsArray (e : Expr) (paramNames : Array Name) (lvls : Array Level) : Expr :=
|
||
instantiateLevelParamsCore (fun p => getParamSubstArray paramNames lvls p 0) e
|
||
|
||
/- Annotate `e` with the given option. -/
|
||
def setOption (e : Expr) (optionName : Name) [KVMap.Value α] (val : α) : Expr :=
|
||
mkMData (MData.empty.set optionName val) e
|
||
|
||
/- Annotate `e` with `pp.explicit := true`
|
||
The delaborator uses `pp` options. -/
|
||
def setPPExplicit (e : Expr) (flag : Bool) :=
|
||
e.setOption `pp.explicit flag
|
||
|
||
/- If `e` is an application `f a_1 ... a_n` annotate `f`, `a_1` ... `a_n` with `pp.explicit := false`,
|
||
and annotate `e` with `pp.explicit := true`. -/
|
||
def setAppPPExplicit (e : Expr) : Expr :=
|
||
match e with
|
||
| app .. =>
|
||
let f := e.getAppFn.setPPExplicit false
|
||
let args := e.getAppArgs.map (·.setPPExplicit false)
|
||
mkAppN f args |>.setPPExplicit true
|
||
| _ => e
|
||
|
||
/- Similar for `setAppPPExplicit`, but only annotate children with `pp.explicit := false` if
|
||
`e` does not contain metavariables. -/
|
||
def setAppPPExplicitForExposingMVars (e : Expr) : Expr :=
|
||
match e with
|
||
| app .. =>
|
||
let f := e.getAppFn.setPPExplicit false
|
||
let args := e.getAppArgs.map fun arg => if arg.hasMVar then arg else arg.setPPExplicit false
|
||
mkAppN f args |>.setPPExplicit true
|
||
| _ => e
|
||
|
||
end Expr
|
||
|
||
def mkAnnotation (kind : Name) (e : Expr) : Expr :=
|
||
mkMData (KVMap.empty.insert kind (DataValue.ofBool true)) e
|
||
|
||
def annotation? (kind : Name) (e : Expr) : Option Expr :=
|
||
match e with
|
||
| Expr.mdata d b _ => if d.size == 1 && d.getBool kind false then some b else none
|
||
| _ => none
|
||
|
||
def mkFreshFVarId {m : Type → Type} [Monad m] [MonadNameGenerator m] : m FVarId :=
|
||
mkFreshId
|
||
|
||
def mkFreshMVarId {m : Type → Type} [Monad m] [MonadNameGenerator m] : m FVarId :=
|
||
mkFreshId
|
||
|
||
end Lean
|