Built with
doc-gen4, running Lean4.
Bubbles (
) indicate interactive fragments: hover for details, tap to reveal contents.
Use
Ctrl+βCtrl+βto navigate,
Ctrl+π±οΈto focus.
On Mac, use
Cmdinstead of
Ctrl.
/-
Copyright (c) 2021 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Arthur Paulino, AurΓ©lien Saue, Mario Carneiro
-/
import Std.Tactic.Simpa
import Mathlib.Lean.Expr
/-!
#
Generally useful tactics.
-/
open Lean.Elab.Tactic
namespace Lean
open Elab
/--
Return the modifiers of declaration `nm` with (optional) docstring `newDoc`.
Currently, recursive or partial definitions are not supported, and no attributes are provided.
-/
def toModifiers (nm : Name) (newDoc : Option String := none) :
CoreM Modifiers := do
let env β getEnv
let d β getConstInfo nm
let mods : Modifiers :=
{ docString? := newDoc
visibility :=
if isPrivateNameExport nm then
Visibility.private
else if isProtected env nm then
Visibility.regular
else
Visibility.protected
isNoncomputable := if (env.find? $ nm.mkStr "_cstage1").isSome then false else true
recKind := RecKind.default -- nonrec only matters for name resolution, so is irrelevant (?)
isUnsafe := d.isUnsafe
attrs := #[] }
return mods
/--
Make a PreDefinition taking some metadata from declaration `nm`.
You can provide a new type, value and (optional) docstring, but the remaining information is taken
from `nm`.
Currently only implemented for definitions and theorems. Also see docstring of `toModifiers`
-/
def toPreDefinition (nm newNm : Name) (newType newValue : Expr) (newDoc : Option String := none) :
CoreM PreDefinition := do
let d β getConstInfo nm
let mods β toModifiers nm newDoc
let predef : PreDefinition :=
{ ref := Syntax.missing
kind := if d.isDef then DefKind.def else DefKind.theorem
levelParams := d.levelParams
modifiers := mods
declName := newNm
type := newType
value := newValue }
return predef
/-- Make `nm` protected. -/
def setProtected {m : Type β Type} [MonadEnv m] (nm : Name) : m Unit :=
modifyEnv (addProtected Β· nm)
namespace Parser.Tactic
syntax withArgs := " with " (colGt ident)+
syntax usingArg := " using " term
/-- Extract the arguments from a `simpArgs` syntax as an array of syntaxes -/
def getSimpArgs : Syntax β TacticM (Array Syntax)
| `(simpArgs| [$args,*]) => pure: {f : Type ?u.2772 β Type ?u.2771} β [self : Pure f] β {Ξ± : Type ?u.2772} β Ξ± β f Ξ±
pure args.getElems
| _ => Elab.throwUnsupportedSyntax
/-- Extract the arguments from a `dsimpArgs` syntax as an array of syntaxes -/
def getDSimpArgs : Syntax β TacticM (Array Syntax)
| `(dsimpArgs| [$args,*]) => pure: {f : Type ?u.3799 β Type ?u.3798} β [self : Pure f] β {Ξ± : Type ?u.3799} β Ξ± β f Ξ±
pure args.getElems
| _ => Elab.throwUnsupportedSyntax
/-- Extract the arguments from a `withArgs` syntax as an array of syntaxes -/
def getWithArgs : Syntax β TacticM (Array Syntax)
| `(withArgs| with $args*) => pure: {f : Type ?u.4816 β Type ?u.4815} β [self : Pure f] β {Ξ± : Type ?u.4816} β Ξ± β f Ξ±
pure args
| _ => Elab.throwUnsupportedSyntax
/-- Extract the argument from a `usingArg` syntax as a syntax term -/
def getUsingArg : Syntax β TacticM Syntax
| `(usingArg| using $e) => pure: {f : Type ?u.5782 β Type ?u.5781} β [self : Pure f] β {Ξ± : Type ?u.5782} β Ξ± β f Ξ±
pure e
| _ => Elab.throwUnsupportedSyntax
/--
`repeat1 tac` applies `tac` to main goal at least once. If the application succeeds,
the tactic is applied recursively to the generated subgoals until it eventually fails.
-/
macro "repeat1 " seq:tacticSeq : tactic => `(tactic| (($seq); repeat $seq))
end Parser.Tactic
end Lean
namespace Lean.Elab.Tactic
/-- Given a local context and an array of `FVarIds` assumed to be in that local context, remove all
implementation details. -/
def filterOutImplementationDetails (lctx : LocalContext) (fvarIds : Array FVarId) : Array FVarId :=
fvarIds.filter (fun fvar => ! (lctx.fvarIdToDecl.find! fvar).isImplementationDetail)
/-- Elaborate syntax for an `FVarId` in the local context of the given goal. -/
def getFVarIdAt (goal : MVarId) (id : Syntax) : TacticM FVarId := withRef id do
-- use apply-like elaboration to suppress insertion of implicit arguments
let e β goal.withContext do
elabTermForApply id (mayPostpone := false)
match e with
| Expr.fvar fvarId => return fvarId
| _ => throwError "unexpected term '{e}'; expected single reference to variable"
/-- Get the array of `FVarId`s in the local context of the given `goal`.
If `ids` is specified, elaborate them in the local context of the given goal to obtain the array of
`FVarId`s.
If `includeImplementationDetails` is `false` (the default), we filter out implementation details
(`implDecl`s and `auxDecl`s) from the resulting list of `FVarId`s. -/
def getFVarIdsAt (goal : MVarId) (ids : Option (Array Syntax) := none)
(includeImplementationDetails : Bool := false) : TacticM (Array FVarId) :=
goal.withContext do
let lctx := (β goal.getDecl): ?m.13101
(β goal(β goal.getDecl): ?m.13101
.getDecl(β goal.getDecl): ?m.13101
).lctx
let fvarIds β match ids with
| none => pure: {f : Type ?u.13161 β Type ?u.13160} β [self : Pure f] β {Ξ± : Type ?u.13161} β Ξ± β f Ξ±
purepure lctx.getFVarIds: TacticM (?m.13111 yβ)
lctxpure lctx.getFVarIds: TacticM (?m.13111 yβ)
.getFVarIds
| some ids => idsids.mapM <| getFVarIdAt goal: TacticM (?m.13111 yβ)
.mapM: {Ξ± : Type ?u.13307} β
{Ξ² : Type ?u.13306} β {m : Type ?u.13306 β Type ?u.13305} β [inst : Monad m] β (Ξ± β m Ξ²) β Array Ξ± β m (Array Ξ²)
mapMids.mapM <| getFVarIdAt goal: TacticM (?m.13111 yβ)
<| getFVarIdAtids.mapM <| getFVarIdAt goal: TacticM (?m.13111 yβ)
goal
if includeImplementationDetails then
return fvarIds
else
return filterOutImplementationDetails lctx fvarIds
/--
Run a tactic on all goals, and always succeeds.
(This is parallel to `Lean.Elab.Tactic.evalAllGoals` in core,
which takes a `Syntax` rather than `TacticM Unit`.
This function could be moved to core and `evalAllGoals` refactored to use it.)
-/
def allGoals (tac : TacticM Unit) : TacticM Unit := do
let mvarIds β getGoals
let mut mvarIdsNew := #[]
for mvarId in mvarIds do
unless (β mvarId.isAssigned): ?m.15935
(β mvarId(β mvarId.isAssigned): ?m.15935
.isAssigned(β mvarId.isAssigned): ?m.15935
) do
setGoals [mvarId]
try
tac
mvarIdsNew := mvarIdsNew ++ (β getUnsolvedGoals)
catch ex =>
if (β read).recover then
logException ex
mvarIdsNew := mvarIdsNew.push mvarId
else
throw ex
setGoals mvarIdsNew.toList
/-- Simulates the `<;>` tactic combinator. First runs `tac1` and then runs
`tac2` on all newly-generated subgoals.
-/
def andThenOnSubgoals (tac1 : TacticM Unit) (tac2 : TacticM Unit) : TacticM Unit :=
focus do tac1; allGoals tac2
variable [Monad: (Type ?u.22993 β Type ?u.22992) β Type (max(?u.22993+1)?u.22992)
Monad m] [MonadExcept: outParam (Type ?u.22285) β (Type ?u.22284 β Type ?u.22283) β Type (max(max?u.22285(?u.22284+1))?u.22283)
MonadExcept Exception m]
/-- Repeats a tactic at most `n` times, stopping sooner if the
tactic fails. Always succeeds. -/
def iterateAtMost : Nat β m Unit β m Unit
| 0, _ => pure: {f : Type ?u.22323 β Type ?u.22322} β [self : Pure f] β {Ξ± : Type ?u.22323} β Ξ± β f Ξ±
pure ()
| n + 1, tac => try tac; iterateAtMost n tac catch _ => pure: {f : Type ?u.22529 β Type ?u.22528} β [self : Pure f] β {Ξ± : Type ?u.22529} β Ξ± β f Ξ±
pure ()
/-- `iterateExactly' n t` executes `t` `n` times. If any iteration fails, the whole tactic fails.
-/
def iterateExactly' : Nat β m Unit β m Unit
| 0, _ => pure: {f : Type ?u.23037 β Type ?u.23036} β [self : Pure f] β {Ξ± : Type ?u.23037} β Ξ± β f Ξ±
pure ()
| n+1, tac => tac *> iterateExactly' n tac
/--
`iterateRange m n t`: Repeat the given tactic at least `m` times and
at most `n` times or until `t` fails. Fails if `t` does not run at least `m` times.
-/
def iterateRange : Nat β Nat β m Unit β m Unit
| 0, 0, _ => pure: {f : Type ?u.23635 β Type ?u.23634} β [self : Pure f] β {Ξ± : Type ?u.23635} β Ξ± β f Ξ±
pure ()
| 0, b, tac => iterateAtMost b tac
| (a+1), n, tac => do tac; iterateRange a (n-1) tac
/-- Repeats a tactic until it fails. Always succeeds. -/
partial def iterateUntilFailure (tac : m Unit) : m Unit :=
try tac; iterateUntilFailure tac catch _ => pure: {f : Type ?u.24672 β Type ?u.24671} β [self : Pure f] β {Ξ± : Type ?u.24672} β Ξ± β f Ξ±
pure ()
/-- `iterateUntilFailureWithResults` is a helper tactic which accumulates the list of results
obtained from iterating `tac` until it fails. Always succeeds.
-/
partial def iterateUntilFailureWithResults {Ξ± : Type} (tac : m Ξ±) : m (List Ξ±) := do
try
let a β tac
let l β iterateUntilFailureWithResults: {Ξ± : Type} β m Ξ± β m (List Ξ±)
iterateUntilFailureWithResults tac
pure: {f : Type ?u.25016 β Type ?u.25015} β [self : Pure f] β {Ξ± : Type ?u.25016} β Ξ± β f Ξ±
pure (a :: l)
catch _ => pure: {f : Type ?u.25077 β Type ?u.25076} β [self : Pure f] β {Ξ± : Type ?u.25077} β Ξ± β f Ξ±
pure []
/-- `iterateUntilFailureCount` is similar to `iterateUntilFailure` except it counts
the number of successful calls to `tac`. Always succeeds.
-/
def iterateUntilFailureCount: {Ξ± : Type} β m Ξ± β m Nat
iterateUntilFailureCount {Ξ± : Type} (tac : m Ξ±) : m Nat := do
let r β iterateUntilFailureWithResults tac
return r.length
end Lean.Elab.Tactic
namespace Mathlib
open Lean
/-- Returns the root directory which contains the package root file, e.g. `Mathlib.lean`. -/
def getPackageDir (pkg : String) : IO System.FilePath := do
let sp β initSrcSearchPath (β findSysroot): ?m.25644
(β findSysroot(β findSysroot): ?m.25644
)
let root? β sp.findM? fun p =>
(p / pkg).isDir <||> ((p / pkg).withExtension "lean").pathExists
if let some root := root? then return root
throw <| IO.userError s!"Could not find {pkg} directory. {
""}Make sure the LEAN_SRC_PATH environment variable is set correctly."
/-- Returns the mathlib root directory. -/
def getMathlibDir := getPackageDir "Mathlib"