1743 lines
61 KiB
Text
1743 lines
61 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
|
||
|
||
/-- Literal values for `Expr`. -/
|
||
inductive Literal where
|
||
| /-- Natural number literal -/
|
||
natVal (val : Nat)
|
||
| /-- String literal -/
|
||
strVal (val : String)
|
||
deriving Inhabited, BEq, Repr
|
||
|
||
protected def Literal.hash : Literal → UInt64
|
||
| .natVal v => hash v
|
||
| .strVal v => hash v
|
||
|
||
instance : Hashable Literal := ⟨Literal.hash⟩
|
||
|
||
/--
|
||
Total order on `Expr` literal values.
|
||
Natural number values are smaller than string literal values.
|
||
-/
|
||
def Literal.lt : Literal → Literal → Bool
|
||
| .natVal _, .strVal _ => true
|
||
| .natVal v₁, .natVal v₂ => v₁ < v₂
|
||
| .strVal v₁, .strVal v₂ => v₁ < v₂
|
||
| _, _ => false
|
||
|
||
instance : LT Literal := ⟨fun a b => a.lt b⟩
|
||
|
||
instance (a b : Literal) : Decidable (a < b) :=
|
||
inferInstanceAs (Decidable (a.lt b))
|
||
|
||
/--
|
||
Arguments in forallE binders can be labelled as implicit or explicit.
|
||
|
||
Each `lam` or `forallE` binder comes with a `binderInfo` argument (stored in ExprData).
|
||
This can be set to
|
||
- `default` -- `(x : α)`
|
||
- `implicit` -- `{x : α}`
|
||
- `strict_implicit` -- `⦃x : α⦄`
|
||
- `inst_implicit` -- `[x : α]`.
|
||
- `aux_decl` -- Auxillary definitions are helper methods that
|
||
Lean generates. `aux_decl` is used for `_match`, `_fun_match`,
|
||
`_let_match` and the self reference that appears in recursive pattern matching.
|
||
|
||
The difference between implicit `{}` and strict-implicit `⦃⦄` is how
|
||
implicit arguments are treated that are *not* followed by explicit arguments.
|
||
`{}` arguments are applied eagerly, while `⦃⦄` arguments are left partially applied:
|
||
```lean4
|
||
def foo {x : Nat} : Nat := x
|
||
def bar ⦃x : Nat⦄ : Nat := x
|
||
#check foo -- foo : Nat
|
||
#check bar -- bar : ⦃x : Nat⦄ → Nat
|
||
```
|
||
|
||
See also the Lean manual: https://leanprover.github.io/lean4/doc/expressions.html#implicit-arguments
|
||
-/
|
||
inductive BinderInfo where
|
||
| /-- Default binder annotation, e.g. `(x : α)` -/
|
||
default
|
||
| /-- Implicit binder annotation, e.g., `{x : α}` -/
|
||
implicit
|
||
| /-- Strict implict binder annotation, e.g., `{{ x : α }}` -/
|
||
strictImplicit
|
||
| /-- Local instance binder annotataion, e.g., `[Decidable α]` -/
|
||
instImplicit
|
||
| /--
|
||
Auxiliary declarations used by Lean when elaborating recursive declarations.
|
||
When defining a function such as
|
||
```
|
||
def f : Nat → Nat
|
||
| 0 => 1
|
||
| x+1 => (x+1)*f x
|
||
```
|
||
Lean adds a local declaration `f : Nat → Nat` to the local context (`LocalContext`)
|
||
with `BinderInfo` set to `auxDecl`.
|
||
This local declaration is later removed by the termination checker.
|
||
-/
|
||
auxDecl
|
||
deriving Inhabited, BEq, Repr
|
||
|
||
def BinderInfo.hash : BinderInfo → UInt64
|
||
| .default => 947
|
||
| .implicit => 1019
|
||
| .strictImplicit => 1087
|
||
| .instImplicit => 1153
|
||
| .auxDecl => 1229
|
||
|
||
/--
|
||
Return `true` if the given `BinderInfo` does not correspond to an implicit binder annotation
|
||
(i.e., `implicit`, `strictImplicit`, or `instImplicit`).
|
||
-/
|
||
def BinderInfo.isExplicit : BinderInfo → Bool
|
||
| .implicit => false
|
||
| .strictImplicit => false
|
||
| .instImplicit => false
|
||
| _ => true
|
||
|
||
instance : Hashable BinderInfo := ⟨BinderInfo.hash⟩
|
||
|
||
/-- Return `true` if the given `BinderInfo` is an instance implicit annotation (e.g., `[Decidable α]`) -/
|
||
def BinderInfo.isInstImplicit : BinderInfo → Bool
|
||
| BinderInfo.instImplicit => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given `BinderInfo` is a regular implicit annotation (e.g., `{α : Type u}`) -/
|
||
def BinderInfo.isImplicit : BinderInfo → Bool
|
||
| BinderInfo.implicit => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given `BinderInfo` is a strict implicit annotation (e.g., `{{α : Type u}}`) -/
|
||
def BinderInfo.isStrictImplicit : BinderInfo → Bool
|
||
| BinderInfo.strictImplicit => true
|
||
| _ => false
|
||
|
||
def BinderInfo.isAuxDecl : BinderInfo → Bool
|
||
| BinderInfo.auxDecl => true
|
||
| _ => false
|
||
|
||
/-- Expression metadata. Used with the `Expr.mdata` constructor. -/
|
||
abbrev MData := KVMap
|
||
abbrev MData.empty : MData := {}
|
||
|
||
/--
|
||
Cached hash code, cached results, and other data for `Expr`.
|
||
- hash : 32-bits
|
||
- hasFVar : 1-bit -- does it contain free variables?
|
||
- hasExprMVar : 1-bit -- does it contain metavariables?
|
||
- hasLevelMVar : 1-bit -- does it contain level metavariables?
|
||
- hasLevelParam : 1-bit -- does it contain level parameters?
|
||
- nonDepLet : 1-bit -- internal flag, for tracking whether let x := v; b is equivalent to (fun x => b) v
|
||
- binderInfo : 3-bits -- encoding of BinderInfo -- TODO remove
|
||
- approxDepth : 8-bits -- the approximate depth is used to minimize the number of hash collisions
|
||
- looseBVarRange : 16-bits
|
||
|
||
Remark: this is mostly an internal datastructure used to implement `Expr`,
|
||
most will never have to use it.
|
||
-/
|
||
def Expr.Data := UInt64
|
||
|
||
instance: Inhabited Expr.Data :=
|
||
inferInstanceAs (Inhabited UInt64)
|
||
|
||
def Expr.Data.hash (c : Expr.Data) : UInt64 :=
|
||
c.toUInt32.toUInt64
|
||
|
||
instance : BEq Expr.Data where
|
||
beq (a b : UInt64) := a == b
|
||
|
||
def Expr.Data.approxDepth (c : Expr.Data) : UInt8 :=
|
||
((c.shiftRight 40).land 255).toUInt8
|
||
|
||
def Expr.Data.looseBVarRange (c : Expr.Data) : UInt32 :=
|
||
(c.shiftRight 48).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
|
||
|
||
def Expr.mkData
|
||
(h : UInt64) (looseBVarRange : Nat := 0) (approxDepth : UInt32 := 0)
|
||
(hasFVar hasExprMVar hasLevelMVar hasLevelParam : Bool := false) (bi : BinderInfo := BinderInfo.default) (nonDepLet : Bool := false)
|
||
: Expr.Data :=
|
||
let approxDepth : UInt8 := if approxDepth > 255 then 255 else approxDepth.toUInt8
|
||
assert! (looseBVarRange ≤ Nat.pow 2 16 - 1)
|
||
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 +
|
||
approxDepth.toUInt64.shiftLeft 40 +
|
||
looseBVarRange.toUInt64.shiftLeft 48
|
||
r
|
||
|
||
/-- Optimized version of `Expr.mkData` for applications. -/
|
||
@[inline] def Expr.mkAppData (fData : Data) (aData : Data) : Data :=
|
||
let depth := (max fData.approxDepth.toUInt16 aData.approxDepth.toUInt16) + 1
|
||
let approxDepth := if depth > 255 then 255 else depth.toUInt8
|
||
let looseBVarRange := max fData.looseBVarRange aData.looseBVarRange
|
||
let hash := mixHash fData aData
|
||
let fData : UInt64 := fData
|
||
let aData : UInt64 := aData
|
||
assert! (looseBVarRange ≤ (Nat.pow 2 16 - 1).toUInt32)
|
||
((fData ||| aData) &&& ((15 : UInt64) <<< (32 : UInt64))) ||| hash.toUInt32.toUInt64 ||| (approxDepth.toUInt64 <<< (40 : UInt64)) ||| (looseBVarRange.toUInt64 <<< (48 : UInt64))
|
||
|
||
@[inline] def Expr.mkDataForBinder (h : UInt64) (looseBVarRange : Nat) (approxDepth : UInt32) (hasFVar hasExprMVar hasLevelMVar hasLevelParam : Bool) (bi : BinderInfo) : Expr.Data :=
|
||
Expr.mkData h looseBVarRange approxDepth hasFVar hasExprMVar hasLevelMVar hasLevelParam bi false
|
||
|
||
@[inline] def Expr.mkDataForLet (h : UInt64) (looseBVarRange : Nat) (approxDepth : UInt32) (hasFVar hasExprMVar hasLevelMVar hasLevelParam nonDepLet : Bool) : Expr.Data :=
|
||
Expr.mkData h looseBVarRange approxDepth hasFVar hasExprMVar hasLevelMVar hasLevelParam BinderInfo.default nonDepLet
|
||
|
||
instance : Repr Expr.Data where
|
||
reprPrec v prec := Id.run do
|
||
let mut r := "Expr.mkData " ++ toString v.hash
|
||
if v.looseBVarRange != 0 then
|
||
r := r ++ " (looseBVarRange := " ++ toString v.looseBVarRange ++ ")"
|
||
if v.approxDepth != 0 then
|
||
r := r ++ " (approxDepth := " ++ toString v.approxDepth ++ ")"
|
||
if v.hasFVar then
|
||
r := r ++ " (hasFVar := " ++ toString v.hasFVar ++ ")"
|
||
if v.hasExprMVar then
|
||
r := r ++ " (hasExprMVar := " ++ toString v.hasExprMVar ++ ")"
|
||
if v.hasLevelMVar then
|
||
r := r ++ " (hasLevelMVar := " ++ toString v.hasLevelMVar ++ ")"
|
||
if v.nonDepLet then
|
||
r := r ++ " (nonDepLet := " ++ toString v.nonDepLet ++ ")"
|
||
if v.binderInfo == BinderInfo.default then
|
||
r := r ++ " (bi := " ++ toString (repr v.binderInfo) ++ ")"
|
||
Repr.addAppParen r prec
|
||
|
||
open Expr
|
||
|
||
/--
|
||
The unique free variable identifier. It is just a hierarchical name,
|
||
but we wrap it in `FVarId` to make sure they don't get mixed up with `MVarId`.
|
||
|
||
This is not the user-facing name for a free variable. This information is stored
|
||
in the local context (`LocalContext`). The unique identifiers are generated using
|
||
a `NameGenerator`.
|
||
-/
|
||
structure FVarId where
|
||
name : Name
|
||
deriving Inhabited, BEq, Hashable
|
||
|
||
instance : Repr FVarId where
|
||
reprPrec n p := reprPrec n.name p
|
||
|
||
/--
|
||
A set of unique free variable identifiers.
|
||
This is a persistent data structure implemented using red-black trees. -/
|
||
def FVarIdSet := Std.RBTree FVarId (Name.quickCmp ·.name ·.name)
|
||
deriving Inhabited, EmptyCollection
|
||
|
||
instance : ForIn m FVarIdSet FVarId := inferInstanceAs (ForIn _ (Std.RBTree ..) ..)
|
||
|
||
/--
|
||
A set of unique free variable identifiers implemented using hashtables.
|
||
Hashtables are faster than red-black trees if they are used linearly.
|
||
They are not persistent data-structures. -/
|
||
def FVarIdHashSet := Std.HashSet FVarId
|
||
deriving Inhabited, EmptyCollection
|
||
|
||
/--
|
||
A mapping from free variable identifiers to values of type `α`.
|
||
This is a persistent data structure implemented using red-black trees. -/
|
||
def FVarIdMap (α : Type) := Std.RBMap FVarId α (Name.quickCmp ·.name ·.name)
|
||
|
||
instance : EmptyCollection (FVarIdMap α) := inferInstanceAs (EmptyCollection (Std.RBMap ..))
|
||
|
||
instance : Inhabited (FVarIdMap α) where
|
||
default := {}
|
||
|
||
/--
|
||
Lean expressions. This datastructure is used in the kernel and
|
||
elaborator. Howewer, expressions sent to the kernel should not
|
||
contain metavariables.
|
||
|
||
Remark: 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
|
||
| /--
|
||
Bound variables. The natural number is the "de Bruijn" index
|
||
for the bound variable. See https://en.wikipedia.org/wiki/De_Bruijn_index for additional information.
|
||
Example, the expression `fun x : Nat => forall y : Nat, x = y`
|
||
```lean
|
||
.lam `x (.const `Nat [])
|
||
(.forall `y (.const `Nat [])
|
||
(.app (.app (.app (.const `Eq [.succ .zero])
|
||
(.const `Nat [])) (.bvar 1))
|
||
(.bvar 0))
|
||
.default)
|
||
.default
|
||
```
|
||
-/
|
||
bvar (deBruijnIndex : Nat)
|
||
| /--
|
||
Free variable. Lean uses the locally nameless approach.
|
||
See https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.365.2479&rep=rep1&type=pdf for additional details.
|
||
When "visiting" the body of a binding expression (`lam`, `forallE`, or `letE`), bound variables
|
||
are converted into free variables using a unique identifier, and their user-facing name, type,
|
||
value (for `LetE`), and binder annotation are stored in the `LocalContext`.
|
||
-/
|
||
fvar (fvarId : FVarId)
|
||
| /--
|
||
Metavariables are used to represent "holes" in expressions, and goals in the
|
||
tactic framework. Metavariable declarations are stored in the `MetavarContext`.
|
||
Metavariables are used during elaboration, and are not allowed in the kernel,
|
||
or in the code generator.
|
||
-/
|
||
mvar (mvarId : MVarId)
|
||
| /--
|
||
`Type u`, `Sort u`, `Prop`. -
|
||
- `Prop` is represented as `.sort .zero`,
|
||
- `Sort u` as ``.sort (.param `u)``, and
|
||
- `Type u` as ``.sort (.succ (.param `u))``
|
||
-/
|
||
sort (u : Level)
|
||
| /--
|
||
A (universe polymorphic) constant. For example,
|
||
`@Eq.{1}` is represented as ``.const `Eq [.succ .zero]``, and
|
||
`@Array.map.{0, 0}` is represented as ``.cons `Array.map [.zero, .zero]``.
|
||
-/
|
||
const (declName : Name) (us : List Level)
|
||
| /--
|
||
Function application. `Nat.succ Nat.zero` is represented as
|
||
```lean4
|
||
.app (.const `Nat.succ []) (.const .zero [])
|
||
```
|
||
-/
|
||
app (fn : Expr) (arg : Expr)
|
||
| /--
|
||
Lambda abstraction (aka anonymous functions).
|
||
- `fun x : Nat => x` is represented as
|
||
```lean4
|
||
.lam `x (.const `Nat []) (.bvar 0) .default
|
||
```
|
||
-/
|
||
lam (binderName : Name) (binderType : Expr) (body : Expr) (binderInfo : BinderInfo)
|
||
| /--
|
||
A dependent arrow (aka forall-expression). It is also used to represent non-dependent arrows.
|
||
Examples:
|
||
|
||
- `forall x : Prop, x ∧ x` is represented as
|
||
```lean4
|
||
.forallE `x
|
||
(.sort .zero)
|
||
(.app (.app (.const `And []) (.bvar 0)) (.bvar 0))
|
||
.default
|
||
```
|
||
- `Nat → Bool` as
|
||
```lean4
|
||
.forallE `a (.const `Nat []) (.const `Bool []) .default
|
||
```
|
||
-/
|
||
forallE (binderName : Name) (binderType : Expr) (body : Expr) (binderInfo : BinderInfo)
|
||
| /--
|
||
Let-expressions. The field `nonDep` is not currently used, but will be used in the future
|
||
by the code generator (and possibly `simp`) to track whether a let-expression is non-dependent
|
||
or not. Given an environment, metavariable context, and local context, we say a let-expression
|
||
`let x : t := v; e` is non-dependent when it is equivalent to `(fun x : t => e) v`.
|
||
Here is an example of a dependent let-expression
|
||
`let n : Nat := 2; fun (a : Array Nat n) (b : Array Nat 2) => a = b` is type correct, but
|
||
`(fun (n : Nat) (a : Array Nat n) (b : Array Nat 2) => a = b) 2` is not.
|
||
The let-expression `let x : Nat := 2; Nat.succ x` is represented as
|
||
```
|
||
.letE `x (.const `Nat []) (.lit (.natVal 2)) (.bvar 0) true
|
||
```
|
||
-/
|
||
letE (declName : Name) (type : Expr) (value : Expr) (body : Expr) (nonDep : Bool)
|
||
| /--
|
||
Natural number and string literal values. They are not really needed, but provide a more
|
||
compact representation in memory for these two kinds of literals, and are used to implement
|
||
efficient reduction in the elaborator and kernel.
|
||
The "raw" natural number `2` can be represented as `.lit (.natVal 2)`. Note that, it is
|
||
definitionally equal to
|
||
```lean4
|
||
.app (.const `Nat.succ []) (.app (.const `Nat.succ []) (.const `Nat.zero []))
|
||
```
|
||
-/
|
||
lit : Literal → Expr
|
||
| /--
|
||
Metadata (aka annotations). We use annotations to provide hints to the pretty-printer,
|
||
store references to `Syntax` nodes, position information, and save information for
|
||
elaboration procedures (e.g., we use the `inaccessible` annotation during elaboration to
|
||
mark `Expr`s that correspond to inaccessible patterns).
|
||
Note that `.mdata data e` is definitionally equal to `e`.
|
||
-/
|
||
mdata (data : MData) (expr : Expr)
|
||
| /--
|
||
Projection-expressions. They are redundant, but are used to create more compact
|
||
terms, speedup reduction, and implement eta for structures.
|
||
The type of `struct` must be an structure-like inductive type. That is, it has only one
|
||
constructor, is not recursive, and it is not an inductive predicate. The kernel and elaborators
|
||
check whether the `typeName` matches the type of `struct`, and whether the (zero-based) index
|
||
is valid (i.e., it is smaller than the numbef of constructor fields).
|
||
When exporting Lean developments to other systems, `proj` can be replaced with `typeName`.`rec`
|
||
applications.
|
||
Example, given `a : Nat x Bool`, `a.1` is represented as
|
||
```lean4
|
||
.proj `Prod 0 a
|
||
```
|
||
-/
|
||
proj (typeName : Name) (idx : Nat) (struct : Expr)
|
||
with
|
||
@[computedField, extern c inline "lean_ctor_get_uint64(#1, lean_ctor_num_objs(#1)*sizeof(void*))"]
|
||
data : @& Expr → Data
|
||
| .const n lvls => mkData (mixHash 5 <| mixHash (hash n) (hash lvls)) 0 0 false false (lvls.any Level.hasMVar) (lvls.any Level.hasParam)
|
||
| .bvar idx => mkData (mixHash 7 <| hash idx) (idx+1)
|
||
| .sort lvl => mkData (mixHash 11 <| hash lvl) 0 0 false false lvl.hasMVar lvl.hasParam
|
||
| .fvar fvarId => mkData (mixHash 13 <| hash fvarId) 0 0 true
|
||
| .mvar fvarId => mkData (mixHash 17 <| hash fvarId) 0 0 false true
|
||
| .mdata _m e =>
|
||
let d := e.data.approxDepth.toUInt32+1
|
||
mkData (mixHash d.toUInt64 <| e.data.hash) e.data.looseBVarRange.toNat d e.data.hasFVar e.data.hasExprMVar e.data.hasLevelMVar e.data.hasLevelParam
|
||
| .proj s i e =>
|
||
let d := e.data.approxDepth.toUInt32+1
|
||
mkData (mixHash d.toUInt64 <| mixHash (hash s) <| mixHash (hash i) e.data.hash)
|
||
e.data.looseBVarRange.toNat d e.data.hasFVar e.data.hasExprMVar e.data.hasLevelMVar e.data.hasLevelParam
|
||
| .app f a => mkAppData f.data a.data
|
||
| .lam _x t b bi =>
|
||
let d := (max t.data.approxDepth.toUInt32 b.data.approxDepth.toUInt32) + 1
|
||
mkDataForBinder (mixHash d.toUInt64 <| mixHash t.data.hash b.data.hash)
|
||
(max t.data.looseBVarRange.toNat (b.data.looseBVarRange.toNat - 1))
|
||
d
|
||
(t.data.hasFVar || b.data.hasFVar)
|
||
(t.data.hasExprMVar || b.data.hasExprMVar)
|
||
(t.data.hasLevelMVar || b.data.hasLevelMVar)
|
||
(t.data.hasLevelParam || b.data.hasLevelParam)
|
||
bi
|
||
| .forallE _x t b bi =>
|
||
let d := (max t.data.approxDepth.toUInt32 b.data.approxDepth.toUInt32) + 1
|
||
mkDataForBinder (mixHash d.toUInt64 <| mixHash t.data.hash b.data.hash)
|
||
(max t.data.looseBVarRange.toNat (b.data.looseBVarRange.toNat - 1))
|
||
d
|
||
(t.data.hasFVar || b.data.hasFVar)
|
||
(t.data.hasExprMVar || b.data.hasExprMVar)
|
||
(t.data.hasLevelMVar || b.data.hasLevelMVar)
|
||
(t.data.hasLevelParam || b.data.hasLevelParam)
|
||
bi
|
||
| .letE _x t v b nonDep =>
|
||
let d := (max (max t.data.approxDepth.toUInt32 v.data.approxDepth.toUInt32) b.data.approxDepth.toUInt32) + 1
|
||
mkDataForLet (mixHash d.toUInt64 <| mixHash t.data.hash <| mixHash v.data.hash b.data.hash)
|
||
(max (max t.data.looseBVarRange.toNat v.data.looseBVarRange.toNat) (b.data.looseBVarRange.toNat - 1))
|
||
d
|
||
(t.data.hasFVar || v.data.hasFVar || b.data.hasFVar)
|
||
(t.data.hasExprMVar || v.data.hasExprMVar || b.data.hasExprMVar)
|
||
(t.data.hasLevelMVar || v.data.hasLevelMVar || b.data.hasLevelMVar)
|
||
(t.data.hasLevelParam || v.data.hasLevelParam || b.data.hasLevelParam)
|
||
nonDep
|
||
| .lit l => mkData (mixHash 3 (hash l))
|
||
deriving Inhabited, Repr
|
||
|
||
namespace Expr
|
||
|
||
/-- The constructor name for the given expression. This is used for debugging purposes. -/
|
||
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) : UInt64 :=
|
||
e.data.hash
|
||
|
||
instance : Hashable Expr := ⟨Expr.hash⟩
|
||
|
||
/--
|
||
Return `true` if `e` contains free variables.
|
||
This is a constant time operation.
|
||
-/
|
||
def hasFVar (e : Expr) : Bool :=
|
||
e.data.hasFVar
|
||
|
||
/--
|
||
Return `true` if `e` contains expression metavariables.
|
||
This is a constant time operation.
|
||
-/
|
||
def hasExprMVar (e : Expr) : Bool :=
|
||
e.data.hasExprMVar
|
||
|
||
/--
|
||
Return `true` if `e` contains universe (aka `Level`) metavariables.
|
||
This is a constant time operation.
|
||
-/
|
||
def hasLevelMVar (e : Expr) : Bool :=
|
||
e.data.hasLevelMVar
|
||
|
||
/--
|
||
Does the expression contain level (aka universe) or expression metavariables?
|
||
This is a constant time operation.
|
||
-/
|
||
def hasMVar (e : Expr) : Bool :=
|
||
let d := e.data
|
||
d.hasExprMVar || d.hasLevelMVar
|
||
|
||
/--
|
||
Return true if `e` contains universe level parameters.
|
||
This is a constant time operation.
|
||
-/
|
||
def hasLevelParam (e : Expr) : Bool :=
|
||
e.data.hasLevelParam
|
||
|
||
/--
|
||
Return the approximated depth of an expression. This information is used to compute
|
||
the expression hash code, and speedup comparisons.
|
||
This is a constant time operation. We say it is approximate because it maxes out at `255`.
|
||
-/
|
||
def approxDepth (e : Expr) : UInt32 :=
|
||
e.data.approxDepth.toUInt32
|
||
|
||
/--
|
||
The range of de-Bruijn variables that are loose.
|
||
That is, bvars that are not bound by a binder.
|
||
For example, `bvar i` has range `i + 1` and
|
||
an expression with no loose bvars has range `0`.
|
||
-/
|
||
def looseBVarRange (e : Expr) : Nat :=
|
||
e.data.looseBVarRange.toNat
|
||
|
||
/--
|
||
Return the binder information if `e` is a lambda or forall expression, and `.default` otherwise.
|
||
-/
|
||
def binderInfo (e : Expr) : BinderInfo :=
|
||
e.data.binderInfo
|
||
|
||
/-!
|
||
Export functions.
|
||
-/
|
||
@[export lean_expr_hash] def hashEx : Expr → UInt64 := 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
|
||
|
||
/-- `mkConst declName us` return `.const declName us`. -/
|
||
def mkConst (declName : Name) (us : List Level := []) : Expr :=
|
||
.const declName us
|
||
|
||
/-- Return the type of a literal value. -/
|
||
def Literal.type : Literal → Expr
|
||
| .natVal _ => mkConst `Nat
|
||
| .strVal _ => mkConst `String
|
||
|
||
@[export lean_lit_type]
|
||
def Literal.typeEx : Literal → Expr := Literal.type
|
||
|
||
/-- `.bvar idx` is now the preferred form. -/
|
||
def mkBVar (idx : Nat) : Expr :=
|
||
.bvar idx
|
||
|
||
/-- `.sort u` is now the preferred form. -/
|
||
def mkSort (u : Level) : Expr :=
|
||
.sort u
|
||
|
||
/--
|
||
`.fvar fvarId` is now the preferred form.
|
||
This function is seldom used, free variables are often automatically created using the
|
||
telescope functions (e.g., `forallTelescope` and `lambdaTelescope`) at `MetaM`.
|
||
-/
|
||
def mkFVar (fvarId : FVarId) : Expr :=
|
||
.fvar fvarId
|
||
|
||
/--
|
||
`.mvar mvarId` is now the preferred form.
|
||
This function is seldom used, metavariables are often created using functions such
|
||
as `mkFresheExprMVar` at `MetaM`.
|
||
-/
|
||
def mkMVar (mvarId : MVarId) : Expr :=
|
||
.mvar mvarId
|
||
|
||
/--
|
||
`.mdata m e` is now the preferred form.
|
||
-/
|
||
def mkMData (m : MData) (e : Expr) : Expr :=
|
||
.mdata m e
|
||
|
||
/--
|
||
`.proj structName idx struct` is now the preferred form.
|
||
-/
|
||
def mkProj (structName : Name) (idx : Nat) (struct : Expr) : Expr :=
|
||
.proj structName idx struct
|
||
|
||
/--
|
||
`.app f a` is now the preferred form.
|
||
-/
|
||
def mkApp (f a : Expr) : Expr :=
|
||
.app f a
|
||
|
||
/--
|
||
`.lam x t b bi` is now the preferred form.
|
||
-/
|
||
def mkLambda (x : Name) (bi : BinderInfo) (t : Expr) (b : Expr) : Expr :=
|
||
.lam x t b bi
|
||
|
||
/--
|
||
`.forallE x t b bi` is now the preferred form.
|
||
-/
|
||
def mkForall (x : Name) (bi : BinderInfo) (t : Expr) (b : Expr) : Expr :=
|
||
.forallE x t b bi
|
||
|
||
/-- Return `Unit -> type`. Do not confuse with `Thunk type` -/
|
||
def mkSimpleThunkType (type : Expr) : Expr :=
|
||
mkForall Name.anonymous .default (mkConst `Unit) type
|
||
|
||
/-- Return `fun (_ : Unit), e` -/
|
||
def mkSimpleThunk (type : Expr) : Expr :=
|
||
mkLambda `_ BinderInfo.default (mkConst `Unit) type
|
||
|
||
/--
|
||
`.letE x t v b nonDep` is now the preferred form.
|
||
-/
|
||
def mkLet (x : Name) (t : Expr) (v : Expr) (b : Expr) (nonDep : Bool := false) : Expr :=
|
||
.letE x t v b nonDep
|
||
|
||
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₆
|
||
|
||
/--
|
||
`.lit l` is now the preferred form.
|
||
-/
|
||
def mkLit (l : Literal) : Expr :=
|
||
.lit l
|
||
|
||
/--
|
||
Return the "raw" natural number `.lit (.natVal n)`.
|
||
This is not the default representation used by the Lean frontend.
|
||
See `mkNatLit`.
|
||
-/
|
||
def mkRawNatLit (n : Nat) : Expr :=
|
||
mkLit (.natVal n)
|
||
|
||
/--
|
||
Return a natural number literal used in the frontend. It is a `OfNat.ofNat` application.
|
||
Recall that all theorems and definitions containing numeric literals are encoded using
|
||
`OfNat.ofNat` applications in the frontend.
|
||
-/
|
||
def mkNatLit (n : Nat) : Expr :=
|
||
let r := mkRawNatLit n
|
||
mkApp3 (mkConst ``OfNat.ofNat [levelZero]) (mkConst ``Nat) r (mkApp (mkConst ``instOfNatNat) r)
|
||
|
||
/-- Return the string literal `.lit (.strVal s)` -/
|
||
def mkStrLit (s : String) : Expr :=
|
||
mkLit (.strVal s)
|
||
|
||
@[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
|
||
|
||
/-- `mkAppN f #[a₀, ..., aₙ]` ==> `f a₀ a₁ .. aₙ`-/
|
||
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
|
||
|
||
/-- Same as `mkApp f args` but reversing `args`. -/
|
||
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"]
|
||
opaque dbgToString (e : @& Expr) : String
|
||
|
||
/-- A total order for expressions. We say it is quick because it first compares the hashcodes. -/
|
||
@[extern "lean_expr_quick_lt"]
|
||
opaque quickLt (a : @& Expr) (b : @& Expr) : Bool
|
||
|
||
/-- A total order for expressions that takes the structure into account (e.g., variable names). -/
|
||
@[extern "lean_expr_lt"]
|
||
opaque lt (a : @& Expr) (b : @& Expr) : Bool
|
||
|
||
/--
|
||
Return true iff `a` and `b` are alpha equivalent.
|
||
Binder annotations are ignored.
|
||
-/
|
||
@[extern "lean_expr_eqv"]
|
||
opaque 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"]
|
||
opaque equal (a : @& Expr) (b : @& Expr) : Bool
|
||
|
||
/-- Return `true` if the given expression is a `.sort ..` -/
|
||
def isSort : Expr → Bool
|
||
| sort .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is of the form `.sort (.succ ..)`. -/
|
||
def isType : Expr → Bool
|
||
| sort (.succ ..) => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a `.sort .zero` -/
|
||
def isProp : Expr → Bool
|
||
| sort (.zero ..) => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a bound variable. -/
|
||
def isBVar : Expr → Bool
|
||
| bvar .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a metavariable. -/
|
||
def isMVar : Expr → Bool
|
||
| mvar .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a free variable. -/
|
||
def isFVar : Expr → Bool
|
||
| fvar .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is an application. -/
|
||
def isApp : Expr → Bool
|
||
| app .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a projection `.proj ..` -/
|
||
def isProj : Expr → Bool
|
||
| proj .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a constant. -/
|
||
def isConst : Expr → Bool
|
||
| const .. => true
|
||
| _ => false
|
||
|
||
/--
|
||
Return `true` if the given expression is a constant of the give name.
|
||
Examples:
|
||
- `` (.const `Nat []).isConstOf `Nat `` is `true`
|
||
- `` (.const `Nat []).isConstOf `False `` is `false`
|
||
-/
|
||
def isConstOf : Expr → Name → Bool
|
||
| const n .., m => n == m
|
||
| _, _ => false
|
||
|
||
/-- Return `true` if the given expression is a forall-expression aka (dependent) arrow. -/
|
||
def isForall : Expr → Bool
|
||
| forallE .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a lambda abstraction aka anonymous function. -/
|
||
def isLambda : Expr → Bool
|
||
| lam .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a forall or lambda expression. -/
|
||
def isBinding : Expr → Bool
|
||
| lam .. => true
|
||
| forallE .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a let-expression. -/
|
||
def isLet : Expr → Bool
|
||
| letE .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a metadata. -/
|
||
def isMData : Expr → Bool
|
||
| mdata .. => true
|
||
| _ => false
|
||
|
||
/-- Return `true` if the given expression is a literal value. -/
|
||
def isLit : Expr → Bool
|
||
| lit .. => true
|
||
| _ => false
|
||
|
||
/--
|
||
Return the "body" of a forall expression.
|
||
Example: let `e` be the representation for `forall (p : Prop) (q : Prop), p ∧ q`, then
|
||
`getForallBody e` returns ``.app (.app (.const `And []) (.bvar 1)) (.bvar 0)``
|
||
-/
|
||
def getForallBody : Expr → Expr
|
||
| forallE _ _ b .. => getForallBody b
|
||
| e => e
|
||
|
||
/--
|
||
If the given expression is a sequence of
|
||
function applications `f a₁ .. aₙ`, return `f`.
|
||
Otherwise return the input expression.
|
||
-/
|
||
def getAppFn : Expr → Expr
|
||
| app f _ => getAppFn f
|
||
| e => e
|
||
|
||
private def getAppNumArgsAux : Expr → Nat → Nat
|
||
| app f _, n => getAppNumArgsAux f (n+1)
|
||
| _, n => n
|
||
|
||
/-- Counts the number `n` of arguments for an expression `f a₁ .. aₙ`. -/
|
||
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
|
||
|
||
/-- Given `f a₁ a₂ ... aₙ`, returns `#[a₁, ..., aₙ]` -/
|
||
@[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
|
||
|
||
/-- Same as `getAppArgs` but reverse the output array. -/
|
||
@[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, _ => k f as
|
||
|
||
/-- Given `e = f a₁ a₂ ... aₙ`, returns `k f #[a₁, ..., aₙ]`. -/
|
||
@[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)
|
||
|
||
/-- Given `e = fn a₁ ... aₙ`, runs `f` on `fn` and each of the arguments `aᵢ` and
|
||
makes a new function application with the results. -/
|
||
def traverseApp {M} [Monad M]
|
||
(f : Expr → M Expr) (e : Expr) : M Expr :=
|
||
e.withApp fun fn args => mkAppN <$> f fn <*> args.mapM f
|
||
|
||
@[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
|
||
|
||
/-- Same as `withApp` but with arguments reversed. -/
|
||
@[inline] def withAppRev (e : Expr) (k : Expr → Array Expr → α) : α :=
|
||
withAppRevAux k e (Array.mkEmpty e.getAppNumArgs)
|
||
|
||
def getRevArgD : Expr → Nat → Expr → Expr
|
||
| app _ a, 0, _ => a
|
||
| app f _, i+1, v => getRevArgD f i v
|
||
| _, _, v => v
|
||
|
||
def getRevArg! : Expr → Nat → Expr
|
||
| app _ a, 0 => a
|
||
| app f _, i+1 => getRevArg! f i
|
||
| _, _ => panic! "invalid index"
|
||
|
||
/-- Given `f a₀ a₁ ... aₙ`, returns the `i`th argument or panics if out of bounds. -/
|
||
@[inline] def getArg! (e : Expr) (i : Nat) (n := e.getAppNumArgs) : Expr :=
|
||
getRevArg! e (n - i - 1)
|
||
|
||
/-- Given `f a₀ a₁ ... aₙ`, returns the `i`th argument or returns `v₀` if out of bounds. -/
|
||
@[inline] def getArgD (e : Expr) (i : Nat) (v₀ : Expr) (n := e.getAppNumArgs) : Expr :=
|
||
getRevArgD e (n - i - 1) v₀
|
||
|
||
/-- Given `f a₀ a₁ ... aₙ`, returns true if `f` is a constant with name `n`. -/
|
||
def isAppOf (e : Expr) (n : Name) : Bool :=
|
||
match e.getAppFn with
|
||
| const c _ => c == n
|
||
| _ => false
|
||
|
||
/--
|
||
Given `f a₁ ... aᵢ`, returns true if `f` is a constant
|
||
with name `n` and has the correct number of arguments.
|
||
-/
|
||
def isAppOfArity : Expr → Name → Nat → Bool
|
||
| const c _, n, 0 => c == n
|
||
| app f _, n, a+1 => isAppOfArity f n a
|
||
| _, _, _ => false
|
||
|
||
/-- Similar to `isAppOfArity` but skips `Expr.mdata`. -/
|
||
def isAppOfArity' : Expr → Name → Nat → Bool
|
||
| mdata _ b , n, a => isAppOfArity' b n a
|
||
| 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 appFn!' : Expr → Expr
|
||
| mdata _ b => appFn!' b
|
||
| app f _ => f
|
||
| _ => panic! "application expected"
|
||
|
||
def appArg!' : Expr → Expr
|
||
| mdata _ b => appArg!' b
|
||
| app _ a => a
|
||
| _ => panic! "application expected"
|
||
|
||
def sortLevel! : Expr → Level
|
||
| sort u => u
|
||
| _ => panic! "sort expected"
|
||
|
||
def litValue! : Expr → Literal
|
||
| lit v => v
|
||
| _ => panic! "literal 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 _ _ _ bi => bi
|
||
| lam _ _ _ bi => bi
|
||
| _ => panic! "binding expected"
|
||
|
||
def letName! : Expr → Name
|
||
| letE n .. => n
|
||
| _ => panic! "let expression expected"
|
||
|
||
def letType! : Expr → Expr
|
||
| letE _ t .. => t
|
||
| _ => panic! "let expression expected"
|
||
|
||
def letValue! : Expr → Expr
|
||
| letE _ _ v .. => v
|
||
| _ => panic! "let expression expected"
|
||
|
||
def letBody! : Expr → Expr
|
||
| letE _ _ _ b .. => b
|
||
| _ => panic! "let expression expected"
|
||
|
||
def consumeMData : Expr → Expr
|
||
| mdata _ e => consumeMData e
|
||
| e => e
|
||
|
||
def mdataExpr! : Expr → Expr
|
||
| mdata _ e => e
|
||
| _ => panic! "mdata expression expected"
|
||
|
||
def projExpr! : Expr → Expr
|
||
| proj _ _ e => e
|
||
| _ => panic! "proj expression expected"
|
||
|
||
def projIdx! : Expr → Nat
|
||
| proj _ i _ => i
|
||
| _ => panic! "proj expression expected"
|
||
|
||
def hasLooseBVars (e : Expr) : Bool :=
|
||
e.looseBVarRange > 0
|
||
|
||
/--
|
||
Return `true` if `e` is a non-dependent arrow.
|
||
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"]
|
||
opaque 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 bi, bvarIdx, tryRange =>
|
||
(bi.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"]
|
||
opaque lowerLooseBVars (e : @& Expr) (s d : @& Nat) : Expr
|
||
|
||
/--
|
||
Lift loose bound variables `>= s` in `e` by `d`. -/
|
||
@[extern "lean_expr_lift_loose_bvars"]
|
||
opaque 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 bi, i+1, considerRange =>
|
||
let b := inferImplicit b i considerRange
|
||
let newInfo := if bi.isExplicit && hasLooseBVarInExplicitDomain b 0 considerRange then BinderInfo.implicit else bi
|
||
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"]
|
||
opaque instantiate (e : @& Expr) (subst : @& Array Expr) : Expr
|
||
|
||
@[extern "lean_expr_instantiate1"]
|
||
opaque 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"]
|
||
opaque 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"]
|
||
opaque 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"]
|
||
opaque instantiateRevRange (e : @& Expr) (beginIdx endIdx : @& Nat) (xs : @& Array Expr) : Expr
|
||
|
||
/-- Replace free (or meta) variables `xs` with loose bound variables. -/
|
||
@[extern "lean_expr_abstract"]
|
||
opaque 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"]
|
||
opaque 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
|
||
|
||
/-- Returns true when the expression does not have any sub-expressions. -/
|
||
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 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 → UInt64
|
||
| ⟨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
|
||
|
||
/--
|
||
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`.
|
||
|
||
If `useZeta` is true, the function also performs zeta-reduction (reduction of let binders) to create further
|
||
opportunities for beta reduction.
|
||
-/
|
||
partial def betaRev (f : Expr) (revArgs : Array Expr) (useZeta := false) (preserveMData := false) : Expr :=
|
||
if revArgs.size == 0 then f
|
||
else
|
||
let sz := revArgs.size
|
||
let rec go (e : Expr) (i : Nat) : Expr :=
|
||
match e with
|
||
| Expr.lam _ _ b _ =>
|
||
if i + 1 < sz then
|
||
go b (i+1)
|
||
else
|
||
let n := sz - (i + 1)
|
||
mkAppRevRange (b.instantiateRange n sz revArgs) 0 n revArgs
|
||
| Expr.letE _ _ v b _ =>
|
||
if useZeta && i < sz then
|
||
go (b.instantiate1 v) i
|
||
else
|
||
let n := sz - i
|
||
mkAppRevRange (e.instantiateRange n sz revArgs) 0 n revArgs
|
||
| Expr.mdata k b =>
|
||
if preserveMData then
|
||
let n := sz - i
|
||
mkMData k (mkAppRevRange (b.instantiateRange n sz revArgs) 0 n revArgs)
|
||
else
|
||
go b i
|
||
| b =>
|
||
let n := sz - i
|
||
mkAppRevRange (b.instantiateRange n sz revArgs) 0 n revArgs
|
||
go f 0
|
||
|
||
/--
|
||
Apply the given arguments to `f`, beta-reducing if `f` is a
|
||
lambda expression. See docstring for `betaRev` for examples.
|
||
-/
|
||
def beta (f : Expr) (args : Array Expr) : Expr :=
|
||
betaRev f args.reverse
|
||
|
||
/--
|
||
Return true if the given expression is the function of an expression that is target for (head) beta reduction.
|
||
If `useZeta = true`, then `let`-expressions are visited. That is, it assumes
|
||
that zeta-reduction (aka let-expansion) is going to be used.
|
||
|
||
See `isHeadBetaTarget`.
|
||
-/
|
||
def isHeadBetaTargetFn (useZeta : Bool) : Expr → Bool
|
||
| Expr.lam .. => true
|
||
| Expr.letE _ _ _ b _ => useZeta && isHeadBetaTargetFn useZeta b
|
||
| Expr.mdata _ b => isHeadBetaTargetFn useZeta b
|
||
| _ => false
|
||
|
||
/-- `(fun x => e) a` ==> `e[x/a]`. -/
|
||
def headBeta (e : Expr) : Expr :=
|
||
let f := e.getAppFn
|
||
if f.isHeadBetaTargetFn false then betaRev f e.getAppRevArgs else e
|
||
|
||
/--
|
||
Return true if the given expression is a target for (head) beta reduction.
|
||
If `useZeta = true`, then `let`-expressions are visited. That is, it assumes
|
||
that zeta-reduction (aka let-expansion) is going to be used.
|
||
-/
|
||
def isHeadBetaTarget (e : Expr) (useZeta := false) : Bool :=
|
||
e.getAppFn.isHeadBetaTargetFn useZeta
|
||
|
||
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
|
||
| _, _+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
|
||
|
||
/-- Return `some e'` if `e` is of the form `optParam _ e'` -/
|
||
def getOptParamDefault? (e : Expr) : Option Expr :=
|
||
if e.isAppOfArity ``optParam 2 then
|
||
some e.appArg!
|
||
else
|
||
none
|
||
|
||
/-- Return `some e'` if `e` is of the form `autoParam _ e'` -/
|
||
def getAutoParamTactic? (e : Expr) : Option Expr :=
|
||
if e.isAppOfArity ``autoParam 2 then
|
||
some e.appArg!
|
||
else
|
||
none
|
||
|
||
/-- Return `true` if `e` is of the form `outParam _` -/
|
||
@[export lean_is_out_param]
|
||
def isOutParam (e : Expr) : Bool :=
|
||
e.isAppOfArity ``outParam 1
|
||
|
||
/-- Return `true` if `e` is of the form `optParam _ _` -/
|
||
def isOptParam (e : Expr) : Bool :=
|
||
e.isAppOfArity ``optParam 2
|
||
|
||
/-- Return `true` if `e` is of the form `autoParam _ _` -/
|
||
def isAutoParam (e : Expr) : Bool :=
|
||
e.isAppOfArity ``autoParam 2
|
||
|
||
/--
|
||
Remove `outParam`, `optParam`, and `autoParam` applications/annotations from `e`.
|
||
Note that it does not remove nested annotations.
|
||
Examples:
|
||
- Given `e` of the form `outParam (optParam Nat b)`, `consumeTypeAnnotations e = b`.
|
||
- Given `e` of the form `Nat → outParam (optParam Nat b)`, `consumeTypeAnnotations e = e`.
|
||
-/
|
||
@[export lean_expr_consume_type_annotations]
|
||
partial def consumeTypeAnnotations (e : Expr) : Expr :=
|
||
if e.isOptParam || e.isAutoParam then
|
||
consumeTypeAnnotations e.appFn!.appArg!
|
||
else if e.isOutParam then
|
||
consumeTypeAnnotations e.appArg!
|
||
else
|
||
e
|
||
|
||
/--
|
||
Remove metadata annotations and `outParam`, `optParam`, and `autoParam` applications/annotations from `e`.
|
||
Note that it does not remove nested annotations.
|
||
Examples:
|
||
- Given `e` of the form `outParam (optParam Nat b)`, `cleanupAnnotations e = b`.
|
||
- Given `e` of the form `Nat → outParam (optParam Nat b)`, `cleanupAnnotations e = e`.
|
||
-/
|
||
partial def cleanupAnnotations (e : Expr) : Expr :=
|
||
let e' := e.consumeMData.consumeTypeAnnotations
|
||
if e' == e then e else cleanupAnnotations e'
|
||
|
||
/-- 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
|
||
| Expr.fvar fvarId => p fvarId
|
||
| _ => false
|
||
visit e
|
||
|
||
/-- Return `true` if `e` contains the given free variable. -/
|
||
def containsFVar (e : Expr) (fvarId : FVarId) : Bool :=
|
||
e.hasAnyFVar (· == fvarId)
|
||
|
||
/-!
|
||
The update functions try to avoid allocating new values using pointer equality.
|
||
Note that if the `update*!` functions are used under a match-expression,
|
||
the compiler will eliminate the double-match.
|
||
-/
|
||
|
||
@[inline] private unsafe def updateApp!Impl (e : Expr) (newFn : Expr) (newArg : Expr) : Expr :=
|
||
match e with
|
||
| app fn arg => if ptrEq fn newFn && ptrEq arg newArg then e else mkApp newFn newArg
|
||
| _ => panic! "application expected"
|
||
|
||
@[implementedBy updateApp!Impl]
|
||
def updateApp! (e : Expr) (newFn : Expr) (newArg : Expr) : Expr :=
|
||
match e with
|
||
| app _ _ => mkApp newFn newArg
|
||
| _ => panic! "application expected"
|
||
|
||
@[inline] private unsafe def updateConst!Impl (e : Expr) (newLevels : List Level) : Expr :=
|
||
match e with
|
||
| const n ls => if ptrEqList ls newLevels then e else mkConst n newLevels
|
||
| _ => panic! "constant expected"
|
||
|
||
@[implementedBy updateConst!Impl]
|
||
def updateConst! (e : Expr) (newLevels : List Level) : Expr :=
|
||
match e with
|
||
| const n _ => mkConst n newLevels
|
||
| _ => panic! "constant expected"
|
||
|
||
@[inline] private unsafe def updateSort!Impl (e : Expr) (u' : Level) : Expr :=
|
||
match e with
|
||
| sort u => if ptrEq u u' then e else mkSort u'
|
||
| _ => panic! "level expected"
|
||
|
||
@[implementedBy updateSort!Impl]
|
||
def updateSort! (e : Expr) (newLevel : Level) : Expr :=
|
||
match e with
|
||
| sort _ => mkSort newLevel
|
||
| _ => panic! "level expected"
|
||
|
||
@[inline] private unsafe def updateMData!Impl (e : Expr) (newExpr : Expr) : Expr :=
|
||
match e with
|
||
| mdata d a => if ptrEq a newExpr then e else mkMData d newExpr
|
||
| _ => panic! "mdata expected"
|
||
|
||
@[implementedBy updateMData!Impl]
|
||
def updateMData! (e : Expr) (newExpr : Expr) : Expr :=
|
||
match e with
|
||
| mdata d _ => mkMData d newExpr
|
||
| _ => panic! "mdata expected"
|
||
|
||
@[inline] private unsafe def updateProj!Impl (e : Expr) (newExpr : Expr) : Expr :=
|
||
match e with
|
||
| proj s i a => if ptrEq a newExpr then e else mkProj s i newExpr
|
||
| _ => panic! "proj expected"
|
||
|
||
@[implementedBy updateProj!Impl]
|
||
def updateProj! (e : Expr) (newExpr : Expr) : Expr :=
|
||
match e with
|
||
| proj s i _ => mkProj s i newExpr
|
||
| _ => panic! "proj expected"
|
||
|
||
@[inline] private unsafe def updateForall!Impl (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| forallE n d b bi =>
|
||
if ptrEq d newDomain && ptrEq b newBody && bi == newBinfo then
|
||
e
|
||
else
|
||
mkForall n newBinfo newDomain newBody
|
||
| _ => panic! "forall expected"
|
||
|
||
@[implementedBy updateForall!Impl]
|
||
def updateForall! (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| forallE n _ _ _ => mkForall n newBinfo newDomain newBody
|
||
| _ => panic! "forall expected"
|
||
|
||
@[inline] def updateForallE! (e : Expr) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| forallE n d b bi => updateForall! (forallE n d b bi) bi newDomain newBody
|
||
| _ => panic! "forall expected"
|
||
|
||
@[inline] private unsafe def updateLambda!Impl (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| lam n d b bi =>
|
||
if ptrEq d newDomain && ptrEq b newBody && bi == newBinfo then
|
||
e
|
||
else
|
||
mkLambda n newBinfo newDomain newBody
|
||
| _ => panic! "lambda expected"
|
||
|
||
@[implementedBy updateLambda!Impl]
|
||
def updateLambda! (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| lam n _ _ _ => mkLambda n newBinfo newDomain newBody
|
||
| _ => panic! "lambda expected"
|
||
|
||
@[inline] def updateLambdaE! (e : Expr) (newDomain : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| lam n d b bi => updateLambda! (lam n d b bi) bi newDomain newBody
|
||
| _ => panic! "lambda expected"
|
||
|
||
@[inline] private unsafe def updateLet!Impl (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| letE n t v b nonDep =>
|
||
if ptrEq t newType && ptrEq v newVal && ptrEq b newBody then
|
||
e
|
||
else
|
||
letE n newType newVal newBody nonDep
|
||
| _ => panic! "let expression expected"
|
||
|
||
@[implementedBy updateLet!Impl]
|
||
def updateLet! (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) : Expr :=
|
||
match e with
|
||
| letE n _ _ _ c => letE n newType newVal newBody c
|
||
| _ => panic! "let expression expected"
|
||
|
||
def updateFn : Expr → Expr → Expr
|
||
| e@(app f a), g => e.updateApp! (updateFn f g) a
|
||
| _, g => g
|
||
|
||
/--
|
||
Eta reduction. If `e` is of the form `(fun x => f x)`, then return `f`.
|
||
-/
|
||
partial def eta (e : Expr) : Expr :=
|
||
match e with
|
||
| Expr.lam _ d b _ =>
|
||
let b' := b.eta
|
||
match b' with
|
||
| .app f (.bvar 0) =>
|
||
if !f.hasLooseBVar 0 then
|
||
f.lowerLooseBVars 1 1
|
||
else
|
||
e.updateLambdaE! d b'
|
||
| _ => e.updateLambdaE! d b'
|
||
| _ => e
|
||
|
||
/--
|
||
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 _ d b _ => e.updateLambdaE! (visit d) (visit b)
|
||
| forallE _ d b _ => e.updateForallE! (visit d) (visit b)
|
||
| letE _ 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
|
||
|
||
/--
|
||
Instantiate univeres level parameters names `paramNames` with `lvls` in `e`.
|
||
If the two lists have different length, the smallest one is used.
|
||
-/
|
||
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
|
||
|
||
/--
|
||
Instantiate univeres level parameters names `paramNames` with `lvls` in `e`.
|
||
If the two arrays have different length, the smallest one is used.
|
||
-/
|
||
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.
|
||
The information is stored using metadata around `e`.
|
||
-/
|
||
def setOption (e : Expr) (optionName : Name) [KVMap.Value α] (val : α) : Expr :=
|
||
mkMData (MData.empty.set optionName val) e
|
||
|
||
/--
|
||
Annotate `e` with `pp.explicit := flag`
|
||
The delaborator uses `pp` options.
|
||
-/
|
||
def setPPExplicit (e : Expr) (flag : Bool) :=
|
||
e.setOption `pp.explicit flag
|
||
|
||
/--
|
||
Annotate `e` with `pp.universes := flag`
|
||
The delaborator uses `pp` options.
|
||
-/
|
||
def setPPUniverses (e : Expr) (flag : Bool) :=
|
||
e.setOption `pp.universes 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
|
||
|
||
/--
|
||
Annotate `e` with the given annotation name `kind`.
|
||
It uses metadata to store the annotation.
|
||
-/
|
||
def mkAnnotation (kind : Name) (e : Expr) : Expr :=
|
||
mkMData (KVMap.empty.insert kind (DataValue.ofBool true)) e
|
||
|
||
/--
|
||
Return `some e'` if `e = mkAnnotation kind e'`
|
||
-/
|
||
def annotation? (kind : Name) (e : Expr) : Option Expr :=
|
||
match e with
|
||
| .mdata d b => if d.size == 1 && d.getBool kind false then some b else none
|
||
| _ => none
|
||
|
||
/--
|
||
Annotate `e` with the `let_fun` annotation. This annotation is used as hint for the delaborator.
|
||
If `e` is of the form `(fun x : t => b) v`, then `mkLetFunAnnotation e` is delaborated at
|
||
`let_fun x : t := v; b`
|
||
-/
|
||
def mkLetFunAnnotation (e : Expr) : Expr :=
|
||
mkAnnotation `let_fun e
|
||
|
||
/--
|
||
Return `some e'` if `e = mkLetFunAnnotation e'`
|
||
-/
|
||
def letFunAnnotation? (e : Expr) : Option Expr :=
|
||
annotation? `let_fun e
|
||
|
||
/--
|
||
Return true if `e = mkLetFunAnnotation e'`, and `e'` is of the form `(fun x : t => b) v`
|
||
-/
|
||
def isLetFun (e : Expr) : Bool :=
|
||
match letFunAnnotation? e with
|
||
| none => false
|
||
| some e => e.isApp && e.appFn!.isLambda
|
||
|
||
/--
|
||
Auxiliary annotation used to mark terms marked with the "inaccessible" annotation `.(t)` and
|
||
`_` in patterns.
|
||
-/
|
||
def mkInaccessible (e : Expr) : Expr :=
|
||
mkAnnotation `_inaccessible e
|
||
|
||
/-- Return `some e'` if `e = mkInaccessible e'`. -/
|
||
def inaccessible? (e : Expr) : Option Expr :=
|
||
annotation? `_inaccessible e
|
||
|
||
private def patternRefAnnotationKey := `_patWithRef
|
||
|
||
/--
|
||
During elaboration expressions corresponding to pattern matching terms
|
||
are annotated with `Syntax` objects. This function returns `some (stx, p')` if
|
||
`p` is the pattern `p'` annotated with `stx`
|
||
-/
|
||
def patternWithRef? (p : Expr) : Option (Syntax × Expr) :=
|
||
match p with
|
||
| Expr.mdata d _ =>
|
||
match d.find patternRefAnnotationKey with
|
||
| some (DataValue.ofSyntax stx) => some (stx, p.mdataExpr!)
|
||
| _ => none
|
||
| _ => none
|
||
|
||
/--
|
||
Annotate the pattern `p` with `stx`. This is an auxiliary annotation
|
||
for producing better hover information.
|
||
-/
|
||
def mkPatternWithRef (p : Expr) (stx : Syntax) : Expr :=
|
||
if patternWithRef? p |>.isSome then
|
||
p
|
||
else
|
||
mkMData (KVMap.empty.insert patternRefAnnotationKey (DataValue.ofSyntax stx)) p
|
||
|
||
/-- Return `some p` if `e` is an annotated pattern (`inaccessible?` or `patternWithRef?`) -/
|
||
def patternAnnotation? (e : Expr) : Option Expr :=
|
||
if let some e := inaccessible? e then
|
||
some e
|
||
else if let some (_, e) := patternWithRef? e then
|
||
some e
|
||
else
|
||
none
|
||
|
||
/--
|
||
Annotate `e` with the LHS annotation. The delaborator displays
|
||
expressions of the form `lhs = rhs` as `lhs` when they have this annotation.
|
||
This is used to implement the infoview for the `conv` mode.
|
||
-/
|
||
def mkLHSGoal (e : Expr) : Expr :=
|
||
mkAnnotation `_lhsGoal e
|
||
|
||
/-- Return `some lhs` if `e = mkLHGoal e'`, where `e'` is of the form `lhs = rhs`. -/
|
||
def isLHSGoal? (e : Expr) : Option Expr :=
|
||
match annotation? `_lhsGoal e with
|
||
| none => none
|
||
| some e =>
|
||
if e.isAppOfArity `Eq 3 then
|
||
some e.appFn!.appArg!
|
||
else
|
||
none
|
||
|
||
/--
|
||
Polymorphic operation for generating unique/fresh free variable identifiers.
|
||
It is available in any monad `m` that implements the inferface `MonadNameGenerator`.
|
||
-/
|
||
def mkFreshFVarId [Monad m] [MonadNameGenerator m] : m FVarId :=
|
||
return { name := (← mkFreshId) }
|
||
|
||
/--
|
||
Polymorphic operation for generating unique/fresh metavariable identifiers.
|
||
It is available in any monad `m` that implements the inferface `MonadNameGenerator`.
|
||
-/
|
||
def mkFreshMVarId [Monad m] [MonadNameGenerator m] : m MVarId :=
|
||
return { name := (← mkFreshId) }
|
||
|
||
/-- Return `Not p` -/
|
||
def mkNot (p : Expr) : Expr := mkApp (mkConst ``Not) p
|
||
/-- Return `p ∨ q` -/
|
||
def mkOr (p q : Expr) : Expr := mkApp2 (mkConst ``Or) p q
|
||
/-- Return `p ∧ q` -/
|
||
def mkAnd (p q : Expr) : Expr := mkApp2 (mkConst ``And) p q
|
||
/-- Return `Classical.em p` -/
|
||
def mkEM (p : Expr) : Expr := mkApp (mkConst ``Classical.em) p
|
||
|
||
end Lean
|