forked from leanprover/lean4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExprTraverse.lean
96 lines (81 loc) · 4.37 KB
/
ExprTraverse.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/-
Copyright (c) 2022 E.W.Ayers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: E.W.Ayers
-/
import Lean.Meta.Basic
import Lean.SubExpr
namespace Lean.Meta
open Lean.SubExpr (Pos)
open Lean.SubExpr.Pos
variable {M} [Monad M] [MonadLiftT MetaM M] [MonadControlT MetaM M]
/-- Convert a traversal function to a form without the `Pos` argument. -/
private def forgetPos (t : (Pos → Expr → M Expr) → (Pos → Expr → M Expr)) (visit : Expr → M Expr) (e : Expr) : M Expr :=
t (fun _ => visit) Pos.root e
/-- Similar to `traverseLambda` but with an additional pos argument to track position. -/
def traverseLambdaWithPos
(f : Pos → Expr → M Expr) (p : Pos) (e : Expr) : M Expr := visit #[] p e
where visit (fvars : Array Expr) (p : Pos) : Expr → M Expr
| (Expr.lam n d b c) => do
let d ← f p.pushBindingDomain <| d.instantiateRev fvars
withLocalDecl n c d fun x =>
visit (fvars.push x) p.pushBindingBody b
| e => do
let body ← f p <| e.instantiateRev fvars
mkLambdaFVars fvars body
/-- Similar to `traverseForall` but with an additional pos argument to track position. -/
def traverseForallWithPos
(f : Pos → Expr → M Expr) (p : Pos) (e : Expr) : M Expr := visit #[] p e
where visit fvars (p : Pos): Expr → M Expr
| (Expr.forallE n d b c) => do
let d ← f p.pushBindingDomain <| d.instantiateRev fvars
withLocalDecl n c d fun x =>
visit (fvars.push x) p.pushBindingBody b
| e => do
let body ← f p <| e.instantiateRev fvars
mkForallFVars fvars body
/-- Similar to `traverseLet` but with an additional pos argument to track position. -/
def traverseLetWithPos
(f : Pos → Expr → M Expr) (p : Pos) (e : Expr) : M Expr := visit #[] p e
where visit fvars (p : Pos)
| Expr.letE n t v b _ => do
let type ← f p.pushLetVarType <| t.instantiateRev fvars
let value ← f p.pushLetValue <| v.instantiateRev fvars
withLetDecl n type value fun x =>
visit (fvars.push x) p.pushLetBody b
| e => do
let body ← f p <| e.instantiateRev fvars
-- if usedLetOnly = true then let binders will be eliminated
-- if their var doesn't appear in the body.
mkLetFVars (usedLetOnly := false) fvars body
/-- Similar to `Lean.Meta.traverseChildren` except that `visit` also includes a `Pos` argument so you can
track the subexpression position. -/
def traverseChildrenWithPos (visit : Pos → Expr → M Expr) (p : Pos) (e: Expr) : M Expr :=
match e with
| Expr.forallE .. => traverseForallWithPos visit p e
| Expr.lam .. => traverseLambdaWithPos visit p e
| Expr.letE .. => traverseLetWithPos visit p e
| Expr.app .. => Expr.traverseAppWithPos visit p e
| Expr.mdata _ b => e.updateMData! <$> visit p b
| Expr.proj _ _ b => e.updateProj! <$> visit p.pushProj b
| _ => pure e
/-- Given an expression `fun (x₁ : α₁) ... (xₙ : αₙ) => b`, will run
`f` on each of the variable types `αᵢ` and `b` with the correct MetaM context,
replacing each expression with the output of `f` and creating a new lambda.
(that is, correctly instantiating bound variables and repackaging them after) -/
def traverseLambda (visit : Expr → M Expr) := forgetPos traverseLambdaWithPos visit
/-- Given an expression ` (x₁ : α₁) → ... → (xₙ : αₙ) → b`, will run
`f` on each of the variable types `αᵢ` and `b` with the correct MetaM context,
replacing the expression with the output of `f` and creating a new forall expression.
(that is, correctly instantiating bound variables and repackaging them after) -/
def traverseForall (visit : Expr → M Expr) := forgetPos traverseForallWithPos visit
/-- Similar to `traverseLambda` and `traverseForall` but with let binders. -/
def traverseLet (visit : Expr → M Expr) := forgetPos traverseLetWithPos visit
/-- Maps `visit` on each child of the given expression.
Applications, foralls, lambdas and let binders are bundled (as they are bundled in `Expr.traverseApp`, `traverseForall`, ...).
So `traverseChildren f e` where ``e = `(fn a₁ ... aₙ)`` will return
``(← f `(fn)) (← f `(a₁)) ... (← f `(aₙ))`` rather than ``(← f `(fn a₁ ... aₙ₋₁)) (← f `(aₙ))``
See also `Lean.Core.traverseChildren`.
-/
def traverseChildren (visit : Expr → M Expr) := forgetPos traverseChildrenWithPos visit
end Lean.Meta