/- 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 -/ prelude 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