lean4-htt/tests/lean/run/ExprLens.lean
Kyle Miller fdd5aec172
feat: better #eval command (#5627)
This refactors and improves the `#eval` command, introducing some new
features.
* Now evaluated results can be represented using `ToExpr` and pretty
printing. This means **hoverable output**. If `ToExpr` fails, it then
tries `Repr` and then `ToString`. The `eval.pp` option controls whether
or not to try `ToExpr`.
* There is now **auto-derivation** of `Repr` instances, enabled with the
`pp.derive.repr` option (default to **true**). For example:
  ```lean
  inductive Baz
    | a | b

  #eval Baz.a
  -- Baz.a
  ```
It simply does `deriving instance Repr for Baz` when there's no way to
represent `Baz`. If core Lean gets `ToExpr` derive handlers, they could
be used here as well.
* The option `eval.type` controls whether or not to include the type in
the output. For now the default is false.
* Now things like `#eval do return 2` work. It tries using
`CommandElabM`, `TermElabM`, or `IO` when the monad is unknown.
* Now there is no longer `Lean.Eval` or `Lean.MetaEval`. These each used
to be responsible for both adapting monads and printing results. The
concerns have been split into two. (1) The `MonadEval` class is
responsible for adapting monads for evaluation (it is similar to
`MonadLift`, but instances are allowed to use default data when
initializing state) and (2) finding a way to represent results is
handled separately.
* Error messages about failed instance synthesis are now more precise.
Once it detects that a `MonadEval` class applies, then the error message
will be specific about missing `ToExpr`/`Repr`/`ToString` instances.
* Fixes a bug where `Repr`/`ToString` instances can't be found by
unfolding types "under the monad". For example, this works now:
  ```lean
  def Foo := List Nat
  def Foo.mk (l : List Nat) : Foo := l
  #eval show Lean.CoreM Foo from do return Foo.mk [1,2,3]
  ```
* Elaboration errors now abort evaluation. This eliminates some
not-so-relevant error messages.
* Now evaluating a value of type `m Unit` never prints a blank message.
* Fixes bugs where evaluating `MetaM` and `CoreM` wouldn't collect log
messages.

The `run_cmd`, `run_elab`, and `run_meta` commands are now frontends for
`#eval`.
2024-10-08 20:51:46 +00:00

89 lines
2.8 KiB
Text

import Lean.Meta.ExprLens
import Lean.Meta.ExprTraverse
import Lean
open Lean Meta Elab Term SubExpr
def Lean.LocalContext.subtract (Γ Δ : LocalContext) : Array Expr :=
-- have Δ = Γ ++ E
let Δ := Δ.getFVars
let Γ := Γ.getFVars
let E := Δ[:(Δ.size - Γ.size)]
E.toArray
def ExprTraversal := ∀{M : _} [Monad M] [MonadLiftT MetaM M] [MonadControlT MetaM M] [MonadOptions M], (Pos → Expr → M Expr) → Pos → Expr → M Expr
instance : Inhabited ExprTraversal where
default := traverseChildrenWithPos
partial def traverseAll : ExprTraversal := fun
| visit, p, e => visit p e >>= traverseChildrenWithPos (fun p e => traverseAll visit p e) p
def testTraversal
(traversalWithPos : ExprTraversal)
(expectedLen : Nat): TermElabM Unit := do
-- make a sample expression `e` that has all of the different kinds of expressions.
let s ← `(
∀ x y : Nat,
∀ {zz : Fin x},
∃ (z : {z: Nat // z = x + y}),
let p := z.1
p + x + y = 3
)
let e ← elabTerm s none
let Γ ← getLCtx
-- traverse `e` using the `traversalWithPos` function
-- leave `e` unmodified but at each point accumulate
-- the abstracted subexpression
let (e', subexprs) ← StateT.run (
traversalWithPos (fun p s => do
let a ← get
let Δ ← getLCtx
let E := Lean.LocalContext.subtract Γ Δ
-- check that numBinders works
let nBinders ← Lean.Core.numBinders p e
if E.size != nBinders then
throwError "bad number of binders"
set <| a.push (p, Expr.abstract s E)
return s
) Pos.root e) #[]
-- the traversal output should be equal to the original
-- that is: `traversal pure e ≡ e`
if not (← liftM $ isDefEq e e') then
throwError "\n{e} \nand \n{e'} are different!"
-- check that the number of subexpressions is what we expect
-- and if it isn't then print them out for debugging.
if subexprs.size != expectedLen then
for (p, s) in subexprs do
let ppt ← PrettyPrinter.ppExpr s
dbg_trace s!"{p}, {ppt}\n"
throwError "expected size: {expectedLen}\nactual size: {subexprs.size}"
-- for each subexpression `p`, make sure that viewSubexpr produces the same
-- subexpression as that found in the traversal.
for (p, s) in subexprs do
viewSubexpr (fun fvars t => do
let t := Expr.abstract t fvars
let de ← liftM $ isDefEq t s
if not de then
throwError "\n{t} \nand \n{s} are different!"
return ()
) p e
-- check that replaceSubexpr pure is the identity
let e' ← replaceSubexpr pure p e
if not (← liftM $ isDefEq e e') then
throwError "\n{e} \nand \n{e'} are different!"
#guard_msgs in
#eval ((do
testTraversal traverseLambdaWithPos 1
testTraversal traverseChildrenWithPos 4
testTraversal traverseAll 103
return ())
: TermElabM Unit
)