/-
# Macros

## What is a macro
Macros in Lean are `Syntax → MacroM Syntax` functions. `MacroM` is the macro
monad which allows macros to have some static guarantees we will discuss in the
next section, you can mostly ignore it for now.

Macros are registered as handlers for a specific syntax declaration using the
`macro` attribute. The compiler will take care of applying these function
to the syntax for us before performing actual analysis of the input. This
means that the only thing we have to do is declare our syntax with a specific
name and bind a function of type `Lean.Macro` to it. Let's try to reproduce
the `LXOR` notation from the `Syntax` chapter:
-/

import Lean

open Lean

syntax:10 (name := lxor) term:10 " LXOR " term:11 : term

@[macro lxor] def lxorImpl : Macro
  | `($l:term LXOR $r:term) => `(!$l && $r) -- we can use the quotation mechanism to create `Syntax` in macros
  | _ => Macro.throwUnsupported

#eval true LXOR true -- false
#eval true LXOR false -- false
#eval false LXOR true -- true
#eval false LXOR false -- false

/-
That was quite easy! The `Macro.throwUnsupported` function can be used by a macro
to indicate that "it doesn't feel responsible for this syntax". In this case
it's merely used to fill a wildcard pattern that should never be reached anyways.

However we can in fact register multiple macros for the same syntax this way
if we desire, they will be tried one after another (the later registered ones have
higher priority)  -- is "higher" correct?
until one throws either a real error using `Macro.throwError` or succeeds, that
is it does not `Macro.throwUnsupported`. Let's see this in action:
-/

@[macro lxor] def lxorImpl2 : Macro
  -- special case that changes behaviour of the case where the left and
  -- right hand side are these specific identifiers
  | `(true LXOR true) => `(true)
  | _ => Macro.throwUnsupported

#eval true LXOR true -- true, handled by new macro
#eval true LXOR false -- false, still handled by the old

/-
This capability is obviously *very* powerful! It should not be used
lightly and without careful thinking since it can introduce weird
behaviour while writing code later on. The following example illustrates
this weird behaviour:
-/

#eval true LXOR true -- true, handled by new macro

def foo := true
#eval foo LXOR foo -- false, handled by old macro, after all the identifiers have a different name

/-
Without knowing exactly how this macro is implemented this behaviour
will be very confusing to whoever might be debugging an issue based on this.
The rule of thumb for when to use a macro vs. other mechanisms like
elaboration is that as soon as you are building real logic like in the 2nd
macro above, it should most likely not be a macro but an elaborator
(explained in the elaboration chapter). This means ideally we want to
use macros for simple syntax to syntax translations, that a human could
easily write out themselves as well but is too lazy to.

## Simplifying macro declaration
Now that we know the basics of what a macro is and how to register it
we can take a look at slightly more automated ways to do this (in fact
all of the ways about to be presented are implemented as macros themselves).

First things first there is `macro_rules` which basically desugars to
functions like the ones we wrote above, for example:
-/

syntax:10 term:10 " RXOR " term:11 : term

macro_rules
  | `($l:term RXOR $r:term) => `($l && !$r)

/-
As you can see, it figures out lot's of things on its own for us:
- the name of the syntax declaration
- the `macro` attribute registration
- the `throwUnsupported` wildcard

apart from this it just works like a function that is using pattern
matching syntax, we can in theory encode arbitrarily complex macro
functions on the right hand side.

If this is still not short enough for you, there is a next step using the
`macro` macro:
-/

macro l:term:10 " ⊕ " r:term:11 : term => `((!$l && $r) || ($l && !$r))

#eval true ⊕ true -- false
#eval true ⊕ false -- true
#eval false ⊕ true -- true
#eval false ⊕ false -- false

/-
As you can see, `macro` is quite close to `notation` already:
- it performed syntax declaration for us
- it automatically wrote a `macro_rules` style function to match on it

The are of course differences as well:
- `notation` is limited to the `term` syntax category
- `notation` cannot have arbitrary macro code on the right hand side

## `Syntax` Quotations
### The basics
So far we've handwaved the `` `(foo $bar) `` syntax to both create and
match on `Syntax` objects but it's time for a full explanation since
it will be essential to all non trivial things that are syntax related.

First things first we call the `` `() `` syntax a `Syntax` quotation.
When we plug variables into a syntax quotation like this: `` `($x) ``
we call the `$x` part an anti-quotation. When we insert `x` like this
it is required that `x` is of type `TSyntax x` where `x` is some `Name`
of a syntax category. The Lean compiler is actually smart enough to figure
the syntax categories that are allowed in this place out. Hence you might
sometimes see errors of the form:
```
application type mismatch
  x.raw
argument
  x
has type
  TSyntax `a : Type
but is expected to have type
  TSyntax `b : Type
```
If you are sure that your thing from the `a` syntax category can be
used as a `b` here you can declare a coercion of the form:
-/

instance : Coe (TSyntax `a) (TSyntax `b) where
  coe s := ⟨s.raw⟩

/-!
Which will allow Lean to perform the type cast automatically. If you
notice that your `a` can not be used in place of the `b` here congrats,
you just discovered a bug in your `Syntax` function. Similar to the Lean
compiler you could can also declare functions that are specific to certain
`TSynax` variants. For example as we have seen in the syntax chapter
there exists the function:
-/
#check TSyntax.getNat -- TSyntax.getNat : TSyntax numLitKind → Nat
/-!
Which is guaranteed to not panic because we know that the `Syntax` that
the function is receiving is a numeric literal and can thus naturally
be converted to a `Nat`.

If we use the antiquotation syntax in pattern matching it will, as discussed
in the syntax chapter, give us a a variable `x` of type `` TSyntax y `` where
`y` is the `Name` of the syntax category that fits in the spot where we pattern matched.
If we wish to insert a literal `$x` into the `Syntax` for some reason,
for example macro creating macros, we can escape the anti quotation using: `` `($$x) ``.

If we want to specify the syntax kind we wish `x` to be interpreted as
we can make this explicit using: `` `($x:term) `` where `term` can be
replaced with any other valid syntax category (e.g. `command`) or parser
(e.g. `ident`).

So far this is only a more formal explanation of the intuitive things
we've already seen in the syntax chapter and up to now in this chapter,
next we'll discuss some more advanced anti-quotations.

### Advanced anti-quotations
For convenince we can also use anti-quotations in a way similar to
format strings: `` `($(mkIdent `c)) `` is the same as: `` let x := mkIdent `c; `($x) ``.

Furthermore there are sometimes situations in which we are not working
with basic `Syntax` but `Syntax` wrapped in more complex datastructures,
most notably `Array (TSyntax c)` or `TSepArray c s`. Where `TSepArray c s`, is a
`Syntax` specific type, it is what we get if we pattern match on some
`Syntax` that users a separator `s` to separate things from the category `c`.
For example if we match using: `$xs,*`, `xs` will have type `TSepArray c ","`,.
With the special case of matching on no specific separator (i.e. whitespace):
`$xs*` in which we will receive an `Array (TSyntax c)`.

If we are dealing with `xs : Array (TSyntax c)` and want to insert it into
a quotation we have two main ways to achieve this:
1. Insert it using a separator, most commonly `,`: `` `($xs,*) ``.
  This is also the way to insert a `TSepArray c ",""`
2. Insert it point blank without a separator (TODO): `` `() ``

For example:
-/

-- syntactically cut away the first element of a tuple if possible
syntax "cut_tuple " "(" term ", " term,+ ")" : term

macro_rules
  -- cutting away one element of a pair isn't possible, it would not result in a tuple
  | `(cut_tuple ($x, $y)) => `(($x, $y))
  | `(cut_tuple ($x, $y, $xs,*)) => `(($y, $xs,*))

#check cut_tuple (1, 2) -- (1, 2) : Nat × Nat
#check cut_tuple (1, 2, 3) -- (2, 3) : Nat × Nat

/-!
The last thing for this section will be so called "anti-quotation splices".
There are two kinds of anti quotation splices, first the so called optional
ones. For example we might declare a syntax with an optional argument,
say our own `let` (in real projects this would most likely be a `let`
in some functional language we are writing a theory about):
-/

syntax "mylet " ident (" : " term)? " := " term " in " term : term

/-!
There is this optional `(" : " term)?` argument involved which can let
the user define the type of the term to the left of it. With the methods
we know so far we'd have to write two `macro_rules` now, one for the case
with, one for the case without the optional argument. However the rest
of the syntactic translation works exactly the same with and without
the optional argument so what we can do using a splice here is to essentially
define both cases at once:
-/

macro_rules
  | `(mylet $x $[: $ty]? := $val in $body) => `(let $x $[: $ty]? := $val; $body)

/-!
The `$[...]?` part is the splice here, it basically says "if this part of
the syntax isn't there, just ignore the parts on the right hand side that
involve anti quotation variables involved here". So now we can run
this syntax both with and without type ascription:
-/

#eval mylet x := 5 in x - 10 -- 0, due to subtraction behaviour of `Nat`
#eval mylet x : Int := 5 in x - 10 -- -5, after all it is an `Int` now

/-!
The second and last splice might remind readers of list comprehension
as seen for example in Python. We will demonstrate it using an implementation
of `map` as a macro:
-/

-- run the function given at the end for each element of the list
syntax "foreach " "[" term,* "]" term : term

macro_rules
  | `(foreach [ $[$x:term],* ] $func:term) => `(let f := $func; [ $[f $x],* ])

#eval foreach [1,2,3,4] (Nat.add 2) -- [3, 4, 5, 6]

/-!
In this case the `$[...],*` part is the splice. On the match side it tries
to match the pattern we define inside of it repetetively (given the seperator
we tell it to). However unlike regular separator matching it does not
give us an `Array` or `SepArray`, instead it allows us to write another
splice on the right hand side that gets evaluated for each time the
pattern we specified matched, with the specific values from the match
per iteration.
-/

/-!
## Hygiene issues and how to solve them
If you are familiar with macro systems in other languages like C you
probably know about so called macro hygiene issues already.
A hygiene issue is when a macro introduces an identifier that collides with an
identifier from some syntax that it is including. For example:
-/

-- Applying this macro produces a function that binds a new identifier `x`.
macro "const" e:term : term => `(fun x => $e)

-- But `x` can also be defined by a user
def x : Nat := 42

-- Which `x` should be used by the compiler in place of `$e`?
#eval (const x) 10 -- 42

/-
Given the fact that macros perform only syntactic translations one might
expect the above `eval` to return 10 instead of 42: after all, the resulting
syntax should be `(fun x => x) 10`. While this was of course not the intention
of the author, this is what would happen in more primitive macro systems like
the one of C. So how does Lean avoid these hygiene issues? You can read
about this in detail in the excellent [Beyond Notations](https://lmcs.episciences.org/9362/pdf)
paper which discusses the idea and implementation in Lean in detail.
We will merely give an overview of the topic, since the details are not
that interesting for practical uses. The idea described in Beyond Notations
comes down to a concept called "macro scopes". Whenever a new macro
is invoked, a new macro scope (basically a unique number) is added to
a list of all the macro scopes that are active right now. When the current
macro introduces a new identifier what is actually getting added is an
identifier of the form:
```
<actual name>._@.(<module_name>.<scopes>)*.<module_name>._hyg.<scopes>
```
For example, if the module name is `Init.Data.List.Basic`, the name is
`foo.bla`, and macros scopes are [2, 5] we get:
```
foo.bla._@.Init.Data.List.Basic._hyg.2.5
```
Since macro scopes are unique numbers the list of macro scopes appended in the end
of the name will always be unique across all macro invocations, hence macro hygiene
issues like the ones above are not possible.

If you are wondering why there is more than just the macro scopes to this
name generation, that is because we may have to combine scopes from different files/modules.
The main module being processed is always the right most one.
This situation may happen when we execute a macro generated in a file
imported in the current file.
```
foo.bla._@.Init.Data.List.Basic.2.1.Init.Lean.Expr_hyg.4
```
The delimiter `_hyg` at the end is used just to improve performance of
the function `Lean.Name.hasMacroScopes` -- the format could also work without it.

This was a lot of technical details. You do not have to understand them
in order to use macros, if you want you can just keep in mind that Lean
will not allow name clashes like the one in the `const` example.

Note that this extends to *all* names that are introduced using syntax
quotations, that is if you write a macro that produces:
`` `(def foo := 1) ``, the user will not be able to access `foo`
because the name will subject to hygienie. Luckily there is a way to
circumvent this. You can use `mkIdent` to generate a raw identifier,
for example: `` `(def $(mkIdent `foo) := 1) ``. In this case it won't
be subject to hygiene and accessible to the user.

## `MonadQuotation` and `MonadRef`
Based on this description of the hygiene mechanism one interesting
question pops up, how do we know what the current list of macro scopes
actually is? After all in the macro functions that were defined above
there is never any explicit passing around of the scopes happening.
As is quite common in functional programming, as soon as we start
having some additional state that we need to bookkeep (like the macro scopes)
this is done with a monad, this is the case here as well with a slight twist.

Instead of implementing this for only a single monad `MacroM` the general
concept of keeping track of macro scopes in monadic way is abstracted
away using a type class called `MonadQuotation`. This allows any other
monad to also easily provide this hygienic `Syntax` creation mechanism
by simply implementing this type class.

This is also the reason that while we are able to use pattern matching on syntax
with `` `(syntax) `` we cannot just create `Syntax` with the same
syntax in pure functions: there is no `Monad` implementing `MonadQuotation`
involved in order to keep track of the macro scopes.

Now let's take a brief look at the `MonadQuotation` type class:
-/

namespace Playground

class MonadRef (m : Type → Type) where
  getRef      : m Syntax
  withRef {α} : Syntax → m α → m α

class MonadQuotation (m : Type → Type) extends MonadRef m where
  getCurrMacroScope : m MacroScope
  getMainModule     : m Name
  withFreshMacroScope {α : Type} : m α → m α

end Playground

/-
Since `MonadQuotation` is based on `MonadRef`, let's take a look at `MonadRef`
first. The idea here is quite simple: `MonadRef` is meant to be seen as an extension
to the `Monad` typeclass which
- gives us a reference to a `Syntax` value with `getRef`
- can evaluate a certain monadic action `m α` with a new reference to a `Syntax`
  using `withRef`

On it's own `MonadRef` isn't exactly interesting, but once it is combined with
`MonadQuotation` it makes sense.

As you can see `MonadQuotation` extends `MonadRef` and adds 3 new functions:
- `getCurrMacroScope` which obtains the latest `MacroScope` that was created
- `getMainModule` which (obviously) obtains the name of the main module,
  both of these are used to create these hygienic identifiers explained above
- `withFreshMacroScope` which will compute the next macro scope and run
  some computation `m α` that performs syntax quotation with this new
  macro scope in order to avoid name clashes. While this is mostly meant
  to be used internally whenever a new macro invocation happens, it can sometimes
  make sense to use this in our own macros, for example when we are generating
  some syntax block repeatedly and want to avoid name clashes.

How `MonadRef` comes into play here is that Lean requires a way to indicate
errors at certain positions to the user. One thing that wasn't introduced
in the `Syntax` chapter is that values of type `Syntax` actually carry their
position in the file around as well. When an error is detected, it is usually
bound to a `Syntax` value which tells Lean where to indicate the error in the file.
What Lean will do when using `withFreshMacroScope` is to apply the position of
the result of `getRef` to each introduced symbol, which then results in better
error positions than not applying any position.

To see error positioning in action, we can write a little macro that makes use of it:
-/

syntax "error_position" ident : term

macro_rules
  | `(error_position all) => Macro.throwError "Ahhh"
  -- the `%$tk` syntax gives us the Syntax of the thing before the %,
  -- in this case `error_position`, giving it the name `tk`
  | `(error_position%$tk first) => withRef tk (Macro.throwError "Ahhh")

#eval error_position all -- the error is indicated at `error_position all`
#eval error_position first -- the error is only indicated at `error_position`

/-
Obviously controlling the positions of errors in this way is quite important
for a good user experience.

## Mini project
As a final mini project for this section we will re-build the arithmetic
DSL from the syntax chapter in a slightly more advanced way, using a macro
this time so we can actually fully integrate it into the Lean syntax.
-/
declare_syntax_cat arith

syntax num : arith
syntax arith "-" arith : arith
syntax arith "+" arith : arith
syntax "(" arith ")" : arith
syntax "[Arith|" arith "]" : term

macro_rules
  | `([Arith| $x:num]) => `($x)
  | `([Arith| $x:arith + $y:arith]) => `([Arith| $x] + [Arith| $y]) -- recursive macros are possible
  | `([Arith| $x:arith - $y:arith]) => `([Arith| $x] - [Arith| $y])
  | `([Arith| ($x:arith)]) => `([Arith| $x])

#eval [Arith| (12 + 3) - 4] -- 11

/-! Again feel free to play around with it. If you want to build more complex
things, like expressions with variables, maybe consider building an inductive type
using macros instead. Once you got your arithmetic expression term
as an inductive, you could then write a function that takes some form of
variable assignment and evaluates the given expression for this
assignment. You could also try to embed arbitrary `term`s into your
arith language using some special syntax or whatever else comes to your mind.
-/

/-!
## More elaborate examples
### Binders 2.0
As promised in the syntax chapter here is Binders 2.0. We'll start by
reintroducing our theory of sets:
-/
def Set (α : Type u) := α → Prop
def Set.mem (x : α) (X : Set α) : Prop := X x

-- Integrate into the already existing typeclass for membership notation
instance : Membership α (Set α) where
  mem := Set.mem

def Set.empty : Set α := λ _ => False

-- the basic "all elements such that" function for the notation
def setOf {α : Type} (p : α → Prop) : Set α := p

/-!
The goal for this section will be to allow for both `{x : X | p x}`
and `{x ∈ X, p x}` notations. In principle there are two ways to do this:
1. Define a syntax and macro for each way to bind a variable we might think of
2. Define a syntax cateogry of binders that we could reuse across other
   binder constructs such as `Σ` or `Π` as well and implement macros for
   the `{ | }` case

In this section we will use approach 2 because it is more easily reusable.
-/

declare_syntax_cat binder_construct
syntax "{" binder_construct "|" term "}" : term

/-!
Now let's define the two binders constructs we are interested in:
-/
syntax ident " : " term : binder_construct
syntax ident " ∈ " term : binder_construct

/-!
And finally the macros to expand our syntax:
-/

macro_rules
  | `({ $var:ident : $ty:term | $body:term }) => `(setOf (fun ($var : $ty) => $body))
  | `({ $var:ident ∈ $s:term | $body:term }) => `(setOf (fun $var => $var ∈ $s ∧ $body))

-- Old examples with better syntax:
#check { x : Nat | x ≤ 1 } -- setOf fun x => x ≤ 1 : Set Nat

example : 1 ∈ { y : Nat | y ≤ 1 } := by simp[Membership.mem, Set.mem, setOf]
example : 2 ∈ { y : Nat | y ≤ 3 ∧ 1 ≤ y } := by simp[Membership.mem, Set.mem, setOf]

-- New examples:
def oneSet : Set Nat := λ x => x = 1
#check { x ∈ oneSet | 10 ≤ x } -- setOf fun x => x ∈ oneSet ∧ 10 ≤ x : Set Nat

example : ∀ x, ¬(x ∈ { y ∈ oneSet | y ≠ 1 }) := by
  intro x h
  -- h : x ∈ setOf fun y => y ∈ oneSet ∧ y ≠ 1
  -- ⊢ False
  cases h
  -- : x ∈ oneSet
  -- : x ≠ 1
  contradiction

/-!
## Reading further
If you want to know more about macros you can read:
- the API docs: TODO link
- the source code: the lower parts of [Init.Prelude](https://github.com/leanprover/lean4/blob/master/src/Init/Prelude.lean)
  as you can see they are declared quite early in Lean because of their importance
  to building up syntax
- the aforementioned [Beyond Notations](https://lmcs.episciences.org/9362/pdf) paper
-/


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Simon Hudon, Sébastien Gouëzel, Scott Morrison

! This file was ported from Lean 3 source module tactic.interactive
! leanprover-community/mathlib commit f89fa08bb6455c96c58e860902fadc8eb2c854ed
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Logic.Nonempty
import Mathbin.Tactic.Lint.Default
import Mathbin.Tactic.Dependencies

/- ./././Mathport/Syntax/Translate/Tactic/Mathlib/Core.lean:38:34: unsupported: setup_tactic_parser -/
namespace Tactic

namespace Interactive

open Interactive Interactive.Types Expr

/-- Similar to `constructor`, but does not reorder goals. -/
unsafe def fconstructor : tactic Unit :=
  concat_tags tactic.fconstructor
#align tactic.interactive.fconstructor tactic.interactive.fconstructor

add_tactic_doc
  { Name := "fconstructor"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.fconstructor]
    tags := ["logic", "goal management"] }

/-- `try_for n { tac }` executes `tac` for `n` ticks, otherwise uses `sorry` to close the goal.
Never fails. Useful for debugging. -/
unsafe def try_for (max : parse parser.pexpr) (tac : itactic) : tactic Unit := do
  let max ← i_to_expr_strict max >>= tactic.eval_expr Nat
  fun s =>
    match _root_.try_for max (tac s) with
    | some r => r
    | none => (tactic.trace "try_for timeout, using sorry" >> tactic.admit) s
#align tactic.interactive.try_for tactic.interactive.try_for

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.many -/
/-- Multiple `subst`. `substs x y z` is the same as `subst x, subst y, subst z`. -/
unsafe def substs (l : parse (parser.many ident)) : tactic Unit :=
  propagate_tags <|
    (l.mapM' fun h => get_local h >>= tactic.subst) >> try (tactic.reflexivity reducible)
#align tactic.interactive.substs tactic.interactive.substs

add_tactic_doc
  { Name := "substs"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.substs]
    tags := ["rewriting"] }

/-- Unfold coercion-related definitions -/
unsafe def unfold_coes (loc : parse location) : tactic Unit :=
  unfold
    [`` coe, `` coeT, `` CoeTC.coe, `` coeB, `` Coe.coe, `` lift, `` HasLift.lift, `` liftT,
      `` HasLiftT.lift, `` coeFn, `` CoeFun.coe, `` coeSort, `` CoeSort.coe]
    loc
#align tactic.interactive.unfold_coes tactic.interactive.unfold_coes

add_tactic_doc
  { Name := "unfold_coes"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.unfold_coes]
    tags := ["simplification"] }

/-- Unfold `has_well_founded.r`, `sizeof` and other such definitions. -/
unsafe def unfold_wf :=
  propagate_tags (andthen well_founded_tactics.unfold_wf_rel well_founded_tactics.unfold_sizeof)
#align tactic.interactive.unfold_wf tactic.interactive.unfold_wf

/-- Unfold auxiliary definitions associated with the current declaration. -/
unsafe def unfold_aux : tactic Unit := do
  let tgt ← target
  let name ← decl_name
  let to_unfold := tgt.list_names_with_prefix Name
  guard ¬to_unfold
  -- should we be using simp_lemmas.mk_default?
        simp_lemmas.mk
        to_unfold tgt >>=
      tactic.change
#align tactic.interactive.unfold_aux tactic.interactive.unfold_aux

/-- For debugging only. This tactic checks the current state for any
missing dropped goals and restores them. Useful when there are no
goals to solve but "result contains meta-variables". -/
unsafe def recover : tactic Unit :=
  metavariables >>= tactic.set_goals
#align tactic.interactive.recover tactic.interactive.recover

/-- Like `try { tac }`, but in the case of failure it continues
from the failure state instead of reverting to the original state. -/
unsafe def continue (tac : itactic) : tactic Unit := fun s =>
  result.cases_on (tac s) (fun a => result.success ()) fun e ref => result.success ()
#align tactic.interactive.continue tactic.interactive.continue

/-- `id { tac }` is the same as `tac`, but it is useful for creating a block scope without
requiring the goal to be solved at the end like `{ tac }`. It can also be used to enclose a
non-interactive tactic for patterns like `tac1; id {tac2}` where `tac2` is non-interactive. -/
@[inline]
protected unsafe def id (tac : itactic) : tactic Unit :=
  tac
#align tactic.interactive.id tactic.interactive.id

/-- `work_on_goal n { tac }` creates a block scope for the `n`-goal,
and does not require that the goal be solved at the end
(any remaining subgoals are inserted back into the list of goals).

Typically usage might look like:
````
intros,
simp,
apply lemma_1,
work_on_goal 3
{ dsimp,
  simp },
refl
````

See also `id { tac }`, which is equivalent to `work_on_goal 1 { tac }`.
-/
unsafe def work_on_goal : parse small_nat → itactic → tactic Unit
  | 0, t => fail "work_on_goal failed: goals are 1-indexed"
  | n + 1, t => do
    let goals ← get_goals
    let earlier_goals := goals.take n
    let later_goals := goals.drop (n + 1)
    set_goals (goals n).toList
    t
    let new_goals ← get_goals
    set_goals (earlier_goals ++ new_goals ++ later_goals)
#align tactic.interactive.work_on_goal tactic.interactive.work_on_goal

/-- `swap n` will move the `n`th goal to the front.
`swap` defaults to `swap 2`, and so interchanges the first and second goals.

See also `tactic.interactive.rotate`, which moves the first `n` goals to the back.
-/
unsafe def swap (n := 2) : tactic Unit := do
  let gs ← get_goals
  match gs (n - 1) with
    | some g => set_goals (g :: gs (n - 1))
    | _ => skip
#align tactic.interactive.swap tactic.interactive.swap

add_tactic_doc
  { Name := "swap"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.swap]
    tags := ["goal management"] }

/-- `rotate` moves the first goal to the back. `rotate n` will do this `n` times.

See also `tactic.interactive.swap`, which moves the `n`th goal to the front.
-/
unsafe def rotate (n := 1) : tactic Unit :=
  tactic.rotate n
#align tactic.interactive.rotate tactic.interactive.rotate

add_tactic_doc
  { Name := "rotate"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.rotate]
    tags := ["goal management"] }

/-- Clear all hypotheses starting with `_`, like `_match` and `_let_match`. -/
unsafe def clear_ : tactic Unit :=
  tactic.repeat do
    let l ← local_context
    l fun h => do
        let Name.mk_string s p ← return <| local_pp_name h
        guard (s = '_')
        let cl ← infer_type h >>= is_class
        guard ¬cl
        tactic.clear h
#align tactic.interactive.clear_ tactic.interactive.clear_

add_tactic_doc
  { Name := "clear_"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.clear_]
    tags := ["context management"] }

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/-- Acts like `have`, but removes a hypothesis with the same name as
this one. For example if the state is `h : p ⊢ goal` and `f : p → q`,
then after `replace h := f h` the goal will be `h : q ⊢ goal`,
where `have h := f h` would result in the state `h : p, h : q ⊢ goal`.
This can be used to simulate the `specialize` and `apply at` tactics
of Coq. -/
unsafe def replace (h : parse (parser.optional ident))
    (q₁ : parse (parser.optional (tk ":" *> texpr)))
    (q₂ : parse <| parser.optional (tk ":=" *> texpr)) : tactic Unit := do
  let h := h.getD `this
  let old ← try_core (get_local h)
  have h q₁ q₂
  match old, q₂ with
    | none, _ => skip
    | some o, some _ => tactic.clear o
    | some o, none => (swap >> tactic.clear o) >> swap
#align tactic.interactive.replace tactic.interactive.replace

add_tactic_doc
  { Name := "replace"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.replace]
    tags := ["context management"] }

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/-- Make every proposition in the context decidable.

`classical!` does this more aggressively, such that even if a decidable instance is already
available for a specific proposition, the noncomputable one will be used instead. -/
unsafe def classical (bang : parse <| parser.optional (tk "!")) :=
  tactic.classical bang.isSome
#align tactic.interactive.classical tactic.interactive.classical

add_tactic_doc
  { Name := "classical"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.classical]
    tags := ["classical logic", "type class"] }

private unsafe def generalize_arg_p_aux : pexpr → parser (pexpr × Name)
  | app (app (macro _ [const `eq _]) h) (local_const x _ _ _) => pure (h, x)
  | _ => fail "parse error"
#align tactic.interactive.generalize_arg_p_aux tactic.interactive.generalize_arg_p_aux

private unsafe def generalize_arg_p : parser (pexpr × Name) :=
  with_desc "expr = id" <| parser.pexpr 0 >>= generalize_arg_p_aux
#align tactic.interactive.generalize_arg_p tactic.interactive.generalize_arg_p

@[nolint def_lemma]
noncomputable theorem generalizeAAux.{u} {α : Sort u} (h : ∀ x : Sort u, (α → x) → x) : α :=
  h α id
#align tactic.interactive.generalize_a_aux Tactic.Interactive.generalizeAAux

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/-- Like `generalize` but also considers assumptions
specified by the user. The user can also specify to
omit the goal.
-/
unsafe def generalize_hyp (h : parse (parser.optional ident)) (_ : parse <| tk ":")
    (p : parse generalize_arg_p) (l : parse location) : tactic Unit := do
  let h' ← get_unused_name `h
  let x' ← get_unused_name `x
  let g ←
    if ¬l.include_goal then do
        refine ``(generalizeAAux _)
        some <$> (Prod.mk <$> tactic.intro x' <*> tactic.intro h')
      else pure none
  let n ← l.get_locals >>= tactic.revert_lst
  generalize h () p
  intron n
  match g with
    | some (x', h') => do
      tactic.apply h'
      tactic.clear h'
      tactic.clear x'
    | none => return ()
#align tactic.interactive.generalize_hyp tactic.interactive.generalize_hyp

add_tactic_doc
  { Name := "generalize_hyp"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.generalize_hyp]
    tags := ["context management"] }

unsafe def compact_decl_aux :
    List Name → BinderInfo → expr → List expr → tactic (List (List Name × BinderInfo × expr))
  | ns, bi, t, [] => pure [(ns.reverse, bi, t)]
  | ns, bi, t, v'@(local_const n pp bi' t') :: xs => do
    let t' ← infer_type v'
    if bi = bi' ∧ t = t' then compact_decl_aux (pp :: ns) bi t xs
      else do
        let vs ← compact_decl_aux [pp] bi' t' xs
        pure <| (ns, bi, t) :: vs
  | ns, bi, t, _ :: xs => compact_decl_aux ns bi t xs
#align tactic.interactive.compact_decl_aux tactic.interactive.compact_decl_aux

/-- go from (x₀ : t₀) (x₁ : t₀) (x₂ : t₀) to (x₀ x₁ x₂ : t₀) -/
unsafe def compact_decl : List expr → tactic (List (List Name × BinderInfo × expr))
  | [] => pure []
  | v@(local_const n pp bi t) :: xs => do
    let t ← infer_type v
    compact_decl_aux [pp] bi t xs
  | _ :: xs => compact_decl xs
#align tactic.interactive.compact_decl tactic.interactive.compact_decl

/-- Remove identity functions from a term. These are normally
automatically generated with terms like `show t, from p` or
`(p : t)` which translate to some variant on `@id t p` in
order to retain the type.
-/
unsafe def clean (q : parse texpr) : tactic Unit := do
  let tgt : expr ← target
  let e ← i_to_expr_strict ``(($(q) : $(tgt)))
  tactic.exact <| e
#align tactic.interactive.clean tactic.interactive.clean

unsafe def source_fields (missing : List Name) (e : pexpr) : tactic (List (Name × pexpr)) := do
  let e ← to_expr e
  let t ← infer_type e
  let struct_n : Name := t.get_app_fn.const_name
  let fields ← expanded_field_list struct_n
  let exp_fields := fields.filterₓ fun x => x.2 ∈ missing
  exp_fields fun ⟨p, n⟩ => (Prod.mk n ∘ to_pexpr) <$> mk_mapp (n p) [none, some e]
#align tactic.interactive.source_fields tactic.interactive.source_fields

unsafe def collect_struct' : pexpr → StateT (List <| expr × structure_instance_info) tactic pexpr
  | e => do
    let some str ← pure e.get_structure_instance_info |
      e.traverse collect_struct'
    let v ← monadLift mk_mvar
    modify (List.cons (v, str))
    pure <| to_pexpr v
#align tactic.interactive.collect_struct' tactic.interactive.collect_struct'

unsafe def collect_struct (e : pexpr) : tactic <| pexpr × List (expr × structure_instance_info) :=
  Prod.map id List.reverse <$> (collect_struct' e).run []
#align tactic.interactive.collect_struct tactic.interactive.collect_struct

unsafe def refine_one (str : structure_instance_info) :
    tactic <| List (expr × structure_instance_info) := do
  let tgt ← target >>= whnf
  let struct_n : Name := tgt.get_app_fn.const_name
  let exp_fields ← expanded_field_list struct_n
  let missing_f := exp_fields.filterₓ fun f => (f.2 : Name) ∉ str.field_names
  let (src_field_names, src_field_vals) ←
    (@List.unzip Name _ ∘ List.join) <$> str.sources.mapM (source_fields <| missing_f.map Prod.snd)
  let provided := exp_fields.filterₓ fun f => (f.2 : Name) ∈ str.field_names
  let missing_f' := missing_f.filterₓ fun x => x.2 ∉ src_field_names
  let vs ← mk_mvar_list missing_f'.length
  let (field_values, new_goals) ← List.unzip <$> (str.field_values.mapM collect_struct : tactic _)
  let e' ←
    to_expr <|
        pexpr.mk_structure_instance
          { struct := some struct_n
            field_names := str.field_names ++ missing_f'.map Prod.snd ++ src_field_names
            field_values := field_values ++ vs.map to_pexpr ++ src_field_vals }
  tactic.exact e'
  let gs ←
    with_enable_tags
        (zipWithM
          (fun (n : Name × Name) v => do
            set_goals [v]
            try (dsimp_target simp_lemmas.mk)
            apply_auto_param <|> apply_opt_param <|> set_main_tag [`_field, n.2, n.1]
            get_goals)
          missing_f' vs)
  set_goals gs
  return new_goals
#align tactic.interactive.refine_one tactic.interactive.refine_one

unsafe def refine_recursively : expr × structure_instance_info → tactic (List expr)
  | (e, str) => do
    set_goals [e]
    let rs ← refine_one str
    let gs ← get_goals
    let gs' ← rs.mapM refine_recursively
    return <| gs' ++ gs
#align tactic.interactive.refine_recursively tactic.interactive.refine_recursively

/-- `refine_struct { .. }` acts like `refine` but works only with structure instance
literals. It creates a goal for each missing field and tags it with the name of the
field so that `have_field` can be used to generically refer to the field currently
being refined.

As an example, we can use `refine_struct` to automate the construction of semigroup
instances:

```lean
refine_struct ( { .. } : semigroup α ),
-- case semigroup, mul
-- α : Type u,
-- ⊢ α → α → α

-- case semigroup, mul_assoc
-- α : Type u,
-- ⊢ ∀ (a b c : α), a * b * c = a * (b * c)
```

`have_field`, used after `refine_struct _`, poses `field` as a local constant
with the type of the field of the current goal:

```lean
refine_struct ({ .. } : semigroup α),
{ have_field, ... },
{ have_field, ... },
```
behaves like
```lean
refine_struct ({ .. } : semigroup α),
{ have field := @semigroup.mul, ... },
{ have field := @semigroup.mul_assoc, ... },
```
-/
unsafe def refine_struct : parse texpr → tactic Unit
  | e => do
    let (x, xs) ← collect_struct e
    refine x
    let gs ← get_goals
    let xs' ← xs.mapM refine_recursively
    set_goals (xs' ++ gs)
#align tactic.interactive.refine_struct tactic.interactive.refine_struct

/-- `guard_hyp' h : t` fails if the hypothesis `h` does not have type `t`.
We use this tactic for writing tests.
Fixes `guard_hyp` by instantiating meta variables
-/
unsafe def guard_hyp' (n : parse ident) (p : parse <| tk ":" *> texpr) : tactic Unit := do
  let h ← get_local n >>= infer_type >>= instantiate_mvars
  guard_expr_eq h p
#align tactic.interactive.guard_hyp' tactic.interactive.guard_hyp'

/--
`match_hyp h : t` fails if the hypothesis `h` does not match the type `t` (which may be a pattern).
We use this tactic for writing tests.
-/
unsafe def match_hyp (n : parse ident) (p : parse <| tk ":" *> texpr) (m := reducible) :
    tactic (List expr) := do
  let h ← get_local n >>= infer_type >>= instantiate_mvars
  match_expr p h m
#align tactic.interactive.match_hyp tactic.interactive.match_hyp

/-- `guard_expr_strict t := e` fails if the expr `t` is not equal to `e`. By contrast
to `guard_expr`, this tests strict (syntactic) equality.
We use this tactic for writing tests.
-/
unsafe def guard_expr_strict (t : expr) (p : parse <| tk ":=" *> texpr) : tactic Unit := do
  let e ← to_expr p
  guard (t = e)
#align tactic.interactive.guard_expr_strict tactic.interactive.guard_expr_strict

/-- `guard_target_strict t` fails if the target of the main goal is not syntactically `t`.
We use this tactic for writing tests.
-/
unsafe def guard_target_strict (p : parse texpr) : tactic Unit := do
  let t ← target
  guard_expr_strict t p
#align tactic.interactive.guard_target_strict tactic.interactive.guard_target_strict

/-- `guard_hyp_strict h : t` fails if the hypothesis `h` does not have type syntactically equal
to `t`.
We use this tactic for writing tests.
-/
unsafe def guard_hyp_strict (n : parse ident) (p : parse <| tk ":" *> texpr) : tactic Unit := do
  let h ← get_local n >>= infer_type >>= instantiate_mvars
  guard_expr_strict h p
#align tactic.interactive.guard_hyp_strict tactic.interactive.guard_hyp_strict

/-- Tests that there are `n` hypotheses in the current context. -/
unsafe def guard_hyp_nums (n : ℕ) : tactic Unit := do
  let k ← local_context
  guard (n = k) <|> fail f! "{k} hypotheses found"
#align tactic.interactive.guard_hyp_nums tactic.interactive.guard_hyp_nums

/-- `guard_hyp_mod_implicit h : t` fails if the type of the hypothesis `h`
is not definitionally equal to `t` modulo none transparency
(i.e., unifying the implicit arguments modulo semireducible transparency).
We use this tactic for writing tests.
-/
unsafe def guard_hyp_mod_implicit (n : parse ident) (p : parse <| tk ":" *> texpr) : tactic Unit :=
  do
  let h ← get_local n >>= infer_type >>= instantiate_mvars
  let e ← to_expr p
  is_def_eq h e transparency.none
#align tactic.interactive.guard_hyp_mod_implicit tactic.interactive.guard_hyp_mod_implicit

/-- `guard_target_mod_implicit t` fails if the target of the main goal
is not definitionally equal to `t` modulo none transparency
(i.e., unifying the implicit arguments modulo semireducible transparency).
We use this tactic for writing tests.
-/
unsafe def guard_target_mod_implicit (p : parse texpr) : tactic Unit := do
  let tgt ← target
  let e ← to_expr p
  is_def_eq tgt e transparency.none
#align tactic.interactive.guard_target_mod_implicit tactic.interactive.guard_target_mod_implicit

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.many -/
/-- Test that `t` is the tag of the main goal. -/
unsafe def guard_tags (tags : parse (parser.many ident)) : tactic Unit := do
  let (t : List Name) ← get_main_tag
  guard (t = tags)
#align tactic.interactive.guard_tags tactic.interactive.guard_tags

/-- `guard_proof_term { t } e` applies tactic `t` and tests whether the resulting proof term
  unifies with `p`. -/
unsafe def guard_proof_term (t : itactic) (p : parse texpr) : itactic := do
  let g :: _ ← get_goals
  let e ← to_expr p
  t
  let g ← instantiate_mvars g
  unify e g
#align tactic.interactive.guard_proof_term tactic.interactive.guard_proof_term

/-- `success_if_fail_with_msg { tac } msg` succeeds if the interactive tactic `tac` fails with
error message `msg` (for test writing purposes). -/
unsafe def success_if_fail_with_msg (tac : tactic.interactive.itactic) :=
  tactic.success_if_fail_with_msg tac
#align tactic.interactive.success_if_fail_with_msg tactic.interactive.success_if_fail_with_msg

/-- Get the field of the current goal. -/
unsafe def get_current_field : tactic Name := do
  let [_, Field, str] ← get_main_tag
  expr.const_name <$> resolve_name (Field str)
#align tactic.interactive.get_current_field tactic.interactive.get_current_field

unsafe def field (n : parse ident) (tac : itactic) : tactic Unit := do
  let gs ← get_goals
  let ts ← gs.mapM get_tag
  let ([g], gs') ← pure <| (List.zip gs ts).partitionₓ fun x => x.snd.get? 1 = some n
  set_goals [g.1]
  tac
  done
  set_goals <| gs' Prod.fst
#align tactic.interactive.field tactic.interactive.field

/-- `have_field`, used after `refine_struct _` poses `field` as a local constant
with the type of the field of the current goal:

```lean
refine_struct ({ .. } : semigroup α),
{ have_field, ... },
{ have_field, ... },
```
behaves like
```lean
refine_struct ({ .. } : semigroup α),
{ have field := @semigroup.mul, ... },
{ have field := @semigroup.mul_assoc, ... },
```
-/
unsafe def have_field : tactic Unit :=
  propagate_tags <| (get_current_field >>= mk_const >>= note `field none) >> return ()
#align tactic.interactive.have_field tactic.interactive.have_field

/-- `apply_field` functions as `have_field, apply field, clear field` -/
unsafe def apply_field : tactic Unit :=
  propagate_tags <| get_current_field >>= applyc
#align tactic.interactive.apply_field tactic.interactive.apply_field

add_tactic_doc
  { Name := "refine_struct"
    category := DocCategory.tactic
    declNames :=
      [`tactic.interactive.refine_struct, `tactic.interactive.apply_field,
        `tactic.interactive.have_field]
    tags := ["structures"]
    inheritDescriptionFrom := `tactic.interactive.refine_struct }

/-- `apply_rules hs with attrs n` applies the list of lemmas `hs` and all lemmas tagged with an
attribute from the list `attrs`, as well as the `assumption` tactic on the
first goal and the resulting subgoals, iteratively, at most `n` times.
`n` is optional, equal to 50 by default.
You can pass an `apply_cfg` option argument as `apply_rules hs n opt`.
(A typical usage would be with `apply_rules hs n { md := reducible }`,
which asks `apply_rules` to not unfold `semireducible` definitions (i.e. most)
when checking if a lemma matches the goal.)

For instance:

```lean
@[user_attribute]
meta def mono_rules : user_attribute :=
{ name := `mono_rules,
  descr := "lemmas usable to prove monotonicity" }

attribute [mono_rules] add_le_add mul_le_mul_of_nonneg_right

lemma my_test {a b c d e : real} (h1 : a ≤ b) (h2 : c ≤ d) (h3 : 0 ≤ e) :
a + c * e + a + c + 0 ≤ b + d * e + b + d + e :=
-- any of the following lines solve the goal:
add_le_add (add_le_add (add_le_add (add_le_add h1 (mul_le_mul_of_nonneg_right h2 h3)) h1 ) h2) h3
by apply_rules [add_le_add, mul_le_mul_of_nonneg_right]
by apply_rules with mono_rules
by apply_rules [add_le_add] with mono_rules
```
-/
unsafe def apply_rules (args : parse opt_pexpr_list) (attrs : parse with_ident_list) (n : Nat := 50)
    (opt : ApplyCfg := { }) : tactic Unit :=
  tactic.apply_rules args attrs n opt
#align tactic.interactive.apply_rules tactic.interactive.apply_rules

add_tactic_doc
  { Name := "apply_rules"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.apply_rules]
    tags := ["lemma application"] }

unsafe def return_cast (f : Option expr) (t : Option (expr × expr)) (es : List (expr × expr × expr))
    (e x x' eq_h : expr) : tactic (Option (expr × expr) × List (expr × expr × expr)) :=
  (do
      guard ¬e
      unify x x'
      let u ← mk_meta_univ
      let f ← f <|> mk_mapp `` _root_.id [(expr.sort u : expr)]
      let t' ← infer_type e
      let some (f', t) ← pure t |
        return (some (f, t'), (e, x', eq_h) :: es)
      infer_type e >>= is_def_eq t
      unify f f'
      return (some (f, t), (e, x', eq_h) :: es)) <|>
    return (t, es)
#align tactic.interactive.return_cast tactic.interactive.return_cast

unsafe def list_cast_of_aux (x : expr) (t : Option (expr × expr)) (es : List (expr × expr × expr)) :
    expr → tactic (Option (expr × expr) × List (expr × expr × expr))
  | e@q(cast $(eq_h) $(x')) => return_cast none t es e x x' eq_h
  | e@q(Eq.mp $(eq_h) $(x')) => return_cast none t es e x x' eq_h
  | e@q(Eq.mpr $(eq_h) $(x')) => mk_eq_symm eq_h >>= return_cast none t es e x x'
  | e@q(@Eq.subst $(α) $(p) $(a) $(b) $(eq_h) $(x')) => return_cast p t es e x x' eq_h
  | e@q(@Eq.substr $(α) $(p) $(a) $(b) $(eq_h) $(x')) =>
    mk_eq_symm eq_h >>= return_cast p t es e x x'
  | e@q(@Eq.ndrec $(α) $(a) $(f) $(x') _ $(eq_h)) => return_cast f t es e x x' eq_h
  | e@q(@Eq.recOn $(α) $(a) $(f) $(b) $(eq_h) $(x')) => return_cast f t es e x x' eq_h
  | e => return (t, es)
#align tactic.interactive.list_cast_of_aux tactic.interactive.list_cast_of_aux

unsafe def list_cast_of (x tgt : expr) : tactic (List (expr × expr × expr)) :=
  (List.reverse ∘ Prod.snd) <$> tgt.mfold (none, []) fun e i es => list_cast_of_aux x es.1 es.2 e
#align tactic.interactive.list_cast_of tactic.interactive.list_cast_of

private unsafe def h_generalize_arg_p_aux : pexpr → parser (pexpr × Name)
  | app (app (macro _ [const `heq _]) h) (local_const x _ _ _) => pure (h, x)
  | _ => fail "parse error"
#align tactic.interactive.h_generalize_arg_p_aux tactic.interactive.h_generalize_arg_p_aux

private unsafe def h_generalize_arg_p : parser (pexpr × Name) :=
  with_desc "expr == id" <| parser.pexpr 0 >>= h_generalize_arg_p_aux
#align tactic.interactive.h_generalize_arg_p tactic.interactive.h_generalize_arg_p

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/-- `h_generalize Hx : e == x` matches on `cast _ e` in the goal and replaces it with
`x`. It also adds `Hx : e == x` as an assumption. If `cast _ e` appears multiple
times (not necessarily with the same proof), they are all replaced by `x`. `cast`
`eq.mp`, `eq.mpr`, `eq.subst`, `eq.substr`, `eq.rec` and `eq.rec_on` are all treated
as casts.

- `h_generalize Hx : e == x with h` adds hypothesis `α = β` with `e : α, x : β`;
- `h_generalize Hx : e == x with _` chooses automatically chooses the name of
  assumption `α = β`;
- `h_generalize! Hx : e == x` reverts `Hx`;
- when `Hx` is omitted, assumption `Hx : e == x` is not added.
-/
unsafe def h_generalize (rev : parse (parser.optional (tk "!")))
    (h : parse (parser.optional ident_)) (_ : parse (tk ":")) (arg : parse h_generalize_arg_p)
    (eqs_h : parse (tk "with" *> pure <$> ident_ <|> pure [])) : tactic Unit := do
  let (e, n) := arg
  let h' := if h = `_ then none else h
  let h' ← (h' : tactic Name) <|> get_unused_name ("h" ++ n.toString : String)
  let e ← to_expr e
  let tgt ← target
  let (e, x, eq_h) :: es ← list_cast_of e tgt |
    fail "no cast found"
  interactive.generalize h' () (to_pexpr e, n)
  let asm ← get_local h'
  let v ← get_local n
  let hs ← es.mapM fun ⟨e, _⟩ => mk_app `eq [e, v]
  (eqs_h [e]).mapM' fun ⟨h, e⟩ => do
      let h ← if h ≠ `_ then pure h else get_unused_name `h
      () <$ note h none eq_h
  hs fun h => do
      let h' ← assert `h h
      tactic.exact asm
      try (rewrite_target h')
      tactic.clear h'
  when h do
      ((to_expr ``(hEq_of_eq_rec_left $(eq_h) $(asm)) <|>
              to_expr ``(heq_of_cast_eq $(eq_h) $(asm))) >>=
            note h' none) >>
          pure ()
  tactic.clear asm
  when rev (interactive.revert [n])
#align tactic.interactive.h_generalize tactic.interactive.h_generalize

add_tactic_doc
  { Name := "h_generalize"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.h_generalize]
    tags := ["context management"] }

/-- Tests whether `t` is definitionally equal to `p`. The difference with `guard_expr_eq` is that
  this uses definitional equality instead of alpha-equivalence. -/
unsafe def guard_expr_eq' (t : expr) (p : parse <| tk ":=" *> texpr) : tactic Unit := do
  let e ← to_expr p
  is_def_eq t e
#align tactic.interactive.guard_expr_eq' tactic.interactive.guard_expr_eq'

/-- `guard_target' t` fails if the target of the main goal is not definitionally equal to `t`.
We use this tactic for writing tests.
The difference with `guard_target` is that this uses definitional equality instead of
alpha-equivalence.
-/
unsafe def guard_target' (p : parse texpr) : tactic Unit := do
  let t ← target
  guard_expr_eq' t p
#align tactic.interactive.guard_target' tactic.interactive.guard_target'

add_tactic_doc
  { Name := "guard_target'"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.guard_target']
    tags := ["testing"] }

/-- Tries to solve the goal using a canonical proof of `true` or the `reflexivity` tactic.
Unlike `trivial` or `trivial'`, does not the `contradiction` tactic.
-/
unsafe def triv : tactic Unit :=
  tactic.triv <|> tactic.reflexivity <|> fail "triv tactic failed"
#align tactic.interactive.triv tactic.interactive.triv

add_tactic_doc
  { Name := "triv"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.triv]
    tags := ["finishing"] }

/--
A weaker version of `trivial` that tries to solve the goal using a canonical proof of `true` or the
`reflexivity` tactic (unfolding only `reducible` constants, so can fail faster than `trivial`),
and otherwise tries the `contradiction` tactic. -/
unsafe def trivial' : tactic Unit :=
  tactic.triv' <|>
    tactic.reflexivity reducible <|> tactic.contradiction <|> fail "trivial' tactic failed"
#align tactic.interactive.trivial' tactic.interactive.trivial'

add_tactic_doc
  { Name := "trivial'"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.trivial']
    tags := ["finishing"] }

/-- Similar to `existsi`. `use x` will instantiate the first term of an `∃` or `Σ` goal with `x`. It
will then try to close the new goal using `trivial'`, or try to simplify it by applying
`exists_prop`. Unlike `existsi`, `x` is elaborated with respect to the expected type.
`use` will alternatively take a list of terms `[x0, ..., xn]`.

`use` will work with constructors of arbitrary inductive types.

Examples:
```lean
example (α : Type) : ∃ S : set α, S = S :=
by use ∅

example : ∃ x : ℤ, x = x :=
by use 42

example : ∃ n > 0, n = n :=
begin
  use 1,
  -- goal is now 1 > 0 ∧ 1 = 1, whereas it would be ∃ (H : 1 > 0), 1 = 1 after existsi 1.
  exact ⟨zero_lt_one, rfl⟩,
end

example : ∃ a b c : ℤ, a + b + c = 6 :=
by use [1, 2, 3]

example : ∃ p : ℤ × ℤ, p.1 = 1 :=
by use ⟨1, 42⟩

example : Σ x y : ℤ, (ℤ × ℤ) × ℤ :=
by use [1, 2, 3, 4, 5]

inductive foo
| mk : ℕ → bool × ℕ → ℕ → foo

example : foo :=
by use [100, tt, 4, 3]
```
-/
unsafe def use (l : parse pexpr_list_or_texpr) : tactic Unit :=
  focus1 <|
    andthen (tactic.use l)
      (try
        (trivial' <|> do
          let q(Exists $(p)) ← target
          (to_expr ``(exists_prop.mpr) >>= tactic.apply) >> skip))
#align tactic.interactive.use tactic.interactive.use

add_tactic_doc
  { Name := "use"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.use, `tactic.interactive.existsi]
    tags := ["logic"]
    inheritDescriptionFrom := `tactic.interactive.use }

/-- `clear_aux_decl` clears every `aux_decl` in the local context for the current goal.
This includes the induction hypothesis when using the equation compiler and
`_let_match` and `_fun_match`.

It is useful when using a tactic such as `finish`, `simp *` or `subst` that may use these
auxiliary declarations, and produce an error saying the recursion is not well founded.

```lean
example (n m : ℕ) (h₁ : n = m) (h₂ : ∃ a : ℕ, a = n ∧ a = m) : 2 * m = 2 * n :=
let ⟨a, ha⟩ := h₂ in
begin
  clear_aux_decl, -- subst will fail without this line
  subst h₁
end

example (x y : ℕ) (h₁ : ∃ n : ℕ, n * 1 = 2) (h₂ : 1 + 1 = 2 → x * 1 = y) : x = y :=
let ⟨n, hn⟩ := h₁ in
begin
  clear_aux_decl, -- finish produces an error without this line
  finish
end
```
-/
unsafe def clear_aux_decl : tactic Unit :=
  tactic.clear_aux_decl
#align tactic.interactive.clear_aux_decl tactic.interactive.clear_aux_decl

add_tactic_doc
  { Name := "clear_aux_decl"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.clear_aux_decl, `tactic.clear_aux_decl]
    tags := ["context management"]
    inheritDescriptionFrom := `tactic.interactive.clear_aux_decl }

unsafe def loc.get_local_pp_names : Loc → tactic (List Name)
  | loc.wildcard => List.map expr.local_pp_name <$> local_context
  | loc.ns l => return l.reduceOption
#align tactic.interactive.loc.get_local_pp_names tactic.interactive.loc.get_local_pp_names

unsafe def loc.get_local_uniq_names (l : Loc) : tactic (List Name) :=
  List.map expr.local_uniq_name <$> l.get_locals
#align tactic.interactive.loc.get_local_uniq_names tactic.interactive.loc.get_local_uniq_names

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/-- The logic of `change x with y at l` fails when there are dependencies.
`change'` mimics the behavior of `change`, except in the case of `change x with y at l`.
In this case, it will correctly replace occurences of `x` with `y` at all possible hypotheses
in `l`. As long as `x` and `y` are defeq, it should never fail.
-/
unsafe def change' (q : parse texpr) :
    parse (parser.optional (tk "with" *> texpr)) → parse location → tactic Unit
  | none, loc.ns [none] => do
    let e ← i_to_expr q
    change_core e none
  | none, loc.ns [some h] => do
    let eq ← i_to_expr q
    let eh ← get_local h
    change_core Eq (some eh)
  | none, _ => fail "change-at does not support multiple locations"
  | some w, l => do
    let l' ← loc.get_local_pp_names l
    l' fun e => try (change_with_at q w e)
    when l <| change q w (loc.ns [none])
#align tactic.interactive.change' tactic.interactive.change'

add_tactic_doc
  { Name := "change'"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.change', `tactic.interactive.change]
    tags := ["renaming"]
    inheritDescriptionFrom := `tactic.interactive.change' }

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
private unsafe def opt_dir_with : parser (Option (Bool × Name)) :=
  parser.optional
    (tk "with" *>
      ((fun arrow h => (Option.isSome arrow, h)) <$> parser.optional (tk "<-") <*> ident))
#align tactic.interactive.opt_dir_with tactic.interactive.opt_dir_with

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/-- `set a := t with h` is a variant of `let a := t`. It adds the hypothesis `h : a = t` to
the local context and replaces `t` with `a` everywhere it can.

`set a := t with ←h` will add `h : t = a` instead.

`set! a := t with h` does not do any replacing.

```lean
example (x : ℕ) (h : x = 3)  : x + x + x = 9 :=
begin
  set y := x with ←h_xy,
/-
x : ℕ,
y : ℕ := x,
h_xy : x = y,
h : y = 3
⊢ y + y + y = 9
-/
end
```
-/
unsafe def set (h_simp : parse (parser.optional (tk "!"))) (a : parse ident)
    (tp : parse (parser.optional (tk ":" *> texpr))) (_ : parse (tk ":=")) (pv : parse texpr)
    (rev_name : parse opt_dir_with) := do
  let tp ←
    i_to_expr <|
        let t := tp.getD pexpr.mk_placeholder
        ``(($(t) : Sort _))
  let pv ← to_expr ``(($(pv) : $(tp)))
  let tp ← instantiate_mvars tp
  definev a tp pv
  when h_simp <| change' ``($(pv)) (some (expr.const a [])) <| Interactive.Loc.wildcard
  match rev_name with
    | some (flip, id) => do
      let nv ← get_local a
      mk_app `eq (cond flip [pv, nv] [nv, pv]) >>= assert id
      reflexivity
    | none => skip
#align tactic.interactive.set tactic.interactive.set

add_tactic_doc
  { Name := "set"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.set]
    tags := ["context management"] }

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.many -/
/-- `clear_except h₀ h₁` deletes all the assumptions it can except for `h₀` and `h₁`.
-/
unsafe def clear_except (xs : parse (parser.many ident)) : tactic Unit := do
  let n ← xs.mapM (try_core ∘ get_local) >>= revert_lst ∘ List.filterMap id
  let ls ← local_context
  ls <| try ∘ tactic.clear
  intron_no_renames n
#align tactic.interactive.clear_except tactic.interactive.clear_except

add_tactic_doc
  { Name := "clear_except"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.clear_except]
    tags := ["context management"] }

unsafe def format_names (ns : List Name) : format :=
  format.join <| List.intersperse " " (ns.map to_fmt)
#align tactic.interactive.format_names tactic.interactive.format_names

private unsafe def indent_bindents (l r : String) : Option (List Name) → expr → tactic format
  | none, e => do
    let e ← pp e
    f!"{(← l)}{(← format.nest l e)}{← r}"
  | some ns, e => do
    let e ← pp e
    let ns := format_names ns
    let margin := l.length + ns.toString.length + " : ".length
    f!"{(← l)}{(← ns)} : {(← format.nest margin e)}{← r}"
#align tactic.interactive.indent_bindents tactic.interactive.indent_bindents

private unsafe def format_binders : List Name × BinderInfo × expr → tactic format
  | (ns, BinderInfo.default, t) => indent_bindents "(" ")" ns t
  | (ns, BinderInfo.implicit, t) => indent_bindents "{" "}" ns t
  | (ns, BinderInfo.strict_implicit, t) => indent_bindents "⦃" "⦄" ns t
  | ([n], BinderInfo.inst_implicit, t) =>
    if "_".isPrefixOfₓ n.toString then indent_bindents "[" "]" none t
    else indent_bindents "[" "]" [n] t
  | (ns, BinderInfo.inst_implicit, t) => indent_bindents "[" "]" ns t
  | (ns, BinderInfo.aux_decl, t) => indent_bindents "(" ")" ns t
#align tactic.interactive.format_binders tactic.interactive.format_binders

private unsafe def partition_vars' (s : name_set) :
    List expr → List expr → List expr → tactic (List expr × List expr)
  | [], as, bs => pure (as.reverse, bs.reverse)
  | x :: xs, as, bs => do
    let t ← infer_type x
    if t s then partition_vars' xs as (x :: bs) else partition_vars' xs (x :: as) bs
#align tactic.interactive.partition_vars' tactic.interactive.partition_vars'

private unsafe def partition_vars : tactic (List expr × List expr) := do
  let ls ← local_context
  partition_vars' (name_set.of_list <| ls expr.local_uniq_name) ls [] []
#align tactic.interactive.partition_vars tactic.interactive.partition_vars

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.many -/
/-- Format the current goal as a stand-alone example. Useful for testing tactics
or creating [minimal working examples](https://leanprover-community.github.io/mwe.html).

* `extract_goal`: formats the statement as an `example` declaration
* `extract_goal my_decl`: formats the statement as a `lemma` or `def` declaration
  called `my_decl`
* `extract_goal with i j k:` only use local constants `i`, `j`, `k` in the declaration

Examples:

```lean
example (i j k : ℕ) (h₀ : i ≤ j) (h₁ : j ≤ k) : i ≤ k :=
begin
  extract_goal,
     -- prints:
     -- example (i j k : ℕ) (h₀ : i ≤ j) (h₁ : j ≤ k) : i ≤ k :=
     -- begin
     --   admit,
     -- end
  extract_goal my_lemma
     -- prints:
     -- lemma my_lemma (i j k : ℕ) (h₀ : i ≤ j) (h₁ : j ≤ k) : i ≤ k :=
     -- begin
     --   admit,
     -- end
end

example {i j k x y z w p q r m n : ℕ} (h₀ : i ≤ j) (h₁ : j ≤ k) (h₁ : k ≤ p) (h₁ : p ≤ q) : i ≤ k :=
begin
  extract_goal my_lemma,
    -- prints:
    -- lemma my_lemma {i j k x y z w p q r m n : ℕ}
    --   (h₀ : i ≤ j)
    --   (h₁ : j ≤ k)
    --   (h₁ : k ≤ p)
    --   (h₁ : p ≤ q) :
    --   i ≤ k :=
    -- begin
    --   admit,
    -- end

  extract_goal my_lemma with i j k
    -- prints:
    -- lemma my_lemma {p i j k : ℕ}
    --   (h₀ : i ≤ j)
    --   (h₁ : j ≤ k)
    --   (h₁ : k ≤ p) :
    --   i ≤ k :=
    -- begin
    --   admit,
    -- end
end

example : true :=
begin
  let n := 0,
  have m : ℕ, admit,
  have k : fin n, admit,
  have : n + m + k.1 = 0, extract_goal,
    -- prints:
    -- example (m : ℕ)  : let n : ℕ := 0 in ∀ (k : fin n), n + m + k.val = 0 :=
    -- begin
    --   intros n k,
    --   admit,
    -- end
end
```

-/
unsafe def extract_goal (print_use : parse <| tk "!" *> pure true <|> pure false)
    (n : parse (parser.optional ident))
    (vs : parse (parser.optional (tk "with" *> parser.many ident))) : tactic Unit := do
  let tgt ← target
  solve_aux tgt do
      let ((cxt₀, cxt₁, ls, tgt), _) ←
        solve_aux tgt do
            vs clear_except
            let ls ← local_context
            let ls ← ls <| succeeds ∘ is_local_def
            let n ← revert_lst ls
            let (c₀, c₁) ← partition_vars
            let tgt ← target
            let ls ← intron' n
            pure (c₀, c₁, ls, tgt)
      let is_prop ← is_prop tgt
      let title :=
        match n, is_prop with
        | none, _ => to_fmt "example"
        | some n, tt => f! "lemma {n}"
        | some n, ff => f! "def {n}"
      let cxt₀ ← compact_decl cxt₀ >>= List.mapM format_binders
      let cxt₁ ← compact_decl cxt₁ >>= List.mapM format_binders
      let stmt ← f!"{← tgt} :="
      let fmt :=
        format.group <|
          format.nest 2 <|
            title ++ cxt₀ (fun acc x => Acc ++ format.group (format.line ++ x)) "" ++
                    format.join (List.map (fun x => format.line ++ x) cxt₁) ++
                  " :" ++
                format.line ++
              stmt
      trace <| fmt <| options.mk `pp.width 80
      let var_names := format.intercalate " " <| ls (to_fmt ∘ local_pp_name)
      let call_intron :=
        if ls then to_fmt ""
        else
          f! "
              intros {var_names},"
      ← do
          dbg_trace "begin{← call_intron}
              admit,
            end
            "
  skip
#align tactic.interactive.extract_goal tactic.interactive.extract_goal

add_tactic_doc
  { Name := "extract_goal"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.extract_goal]
    tags := ["goal management", "proof extraction", "debugging"] }

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
/-- `inhabit α` tries to derive a `nonempty α` instance and then upgrades this
to an `inhabited α` instance.
If the target is a `Prop`, this is done constructively;
otherwise, it uses `classical.choice`.

```lean
example (α) [nonempty α] : ∃ a : α, true :=
begin
  inhabit α,
  existsi default,
  trivial
end
```
-/
unsafe def inhabit (t : parse parser.pexpr) (inst_name : parse (parser.optional ident)) :
    tactic Unit := do
  let ty ← i_to_expr t
  let nm ← returnopt inst_name <|> get_unused_name `inst
  let tgt ← target
  let tgt_is_prop ← is_prop tgt
  if tgt_is_prop then do
      decorate_error "could not infer nonempty instance:" <|
          mk_mapp `` Nonempty.elim_to_inhabited [ty, none, tgt] >>= tactic.apply
      introI nm
    else do
      decorate_error "could not infer nonempty instance:" <|
          mk_mapp `` Classical.inhabited_of_nonempty' [ty, none] >>= note nm none
      resetI
#align tactic.interactive.inhabit tactic.interactive.inhabit

add_tactic_doc
  { Name := "inhabit"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.inhabit]
    tags := ["context management", "type class"] }

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.many -/
/-- `revert_deps n₁ n₂ ...` reverts all the hypotheses that depend on one of `n₁, n₂, ...`
It does not revert `n₁, n₂, ...` themselves (unless they depend on another `nᵢ`). -/
unsafe def revert_deps (ns : parse (parser.many ident)) : tactic Unit :=
  propagate_tags <| (ns.mapM get_local >>= revert_reverse_dependencies_of_hyps) >> skip
#align tactic.interactive.revert_deps tactic.interactive.revert_deps

add_tactic_doc
  { Name := "revert_deps"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.revert_deps]
    tags := ["context management", "goal management"] }

/-- `revert_after n` reverts all the hypotheses after `n`. -/
unsafe def revert_after (n : parse ident) : tactic Unit :=
  propagate_tags <| (get_local n >>= tactic.revert_after) >> skip
#align tactic.interactive.revert_after tactic.interactive.revert_after

add_tactic_doc
  { Name := "revert_after"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.revert_after]
    tags := ["context management", "goal management"] }

/-- Reverts all local constants on which the target depends (recursively). -/
unsafe def revert_target_deps : tactic Unit :=
  propagate_tags <| tactic.revert_target_deps >> skip
#align tactic.interactive.revert_target_deps tactic.interactive.revert_target_deps

add_tactic_doc
  { Name := "revert_target_deps"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.revert_target_deps]
    tags := ["context management", "goal management"] }

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.many -/
/-- `clear_value n₁ n₂ ...` clears the bodies of the local definitions `n₁, n₂ ...`, changing them
into regular hypotheses. A hypothesis `n : α := t` is changed to `n : α`. -/
unsafe def clear_value (ns : parse (parser.many ident)) : tactic Unit :=
  propagate_tags <| ns.reverse.mapM get_local >>= tactic.clear_value
#align tactic.interactive.clear_value tactic.interactive.clear_value

add_tactic_doc
  { Name := "clear_value"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.clear_value]
    tags := ["context management"] }

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `parser.optional -/
-- failed to format: unknown constant 'term.pseudo.antiquot'
/--
      `generalize' : e = x` replaces all occurrences of `e` in the target with a new hypothesis `x` of
      the same type.

      `generalize' h : e = x` in addition registers the hypothesis `h : e = x`.

      `generalize'` is similar to `generalize`. The difference is that `generalize' : e = x` also
      succeeds when `e` does not occur in the goal. It is similar to `set`, but the resulting hypothesis
      `x` is not a local definition.
      -/
    unsafe
  def
    generalize'
    ( h : parse ( parser.optional ident ) ) ( _ : parse <| tk ":" ) ( p : parse generalize_arg_p )
      : tactic Unit
    :=
      propagate_tags
        do
          let ( p , x ) := p
            let e ← i_to_expr p
            let some h ← pure h | tactic.generalize' e x >> skip
            let tgt ← target
            let
              tgt'
                ←
                (
                    do
                      let ⟨ tgt' , _ ⟩ ← solve_aux tgt ( tactic.generalize e x >> target )
                        to_expr ` `( ∀ x , $ ( e ) = x → $ ( tgt' 0 1 ) )
                    )
                  <|>
                  to_expr ` `( ∀ x , $ ( e ) = x → $ ( tgt ) )
            let t ← assert h tgt'
            swap
            exact ` `( $ ( t ) $ ( e ) rfl )
            intro x
            intro h
#align tactic.interactive.generalize' tactic.interactive.generalize'

add_tactic_doc
  { Name := "generalize'"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.generalize']
    tags := ["context management"] }

/-- If the expression `q` is a local variable with type `x = t` or `t = x`, where `x` is a local
constant, `tactic.interactive.subst' q` substitutes `x` by `t` everywhere in the main goal and
then clears `q`.
If `q` is another local variable, then we find a local constant with type `q = t` or `t = q` and
substitute `t` for `q`.

Like `tactic.interactive.subst`, but fails with a nicer error message if the substituted variable is
a local definition. It is trickier to fix this in core, since `tactic.is_local_def` is in mathlib.
-/
unsafe def subst' (q : parse texpr) : tactic Unit := do
  (i_to_expr q >>= tactic.subst') >> try (tactic.reflexivity reducible)
#align tactic.interactive.subst' tactic.interactive.subst'

add_tactic_doc
  { Name := "subst'"
    category := DocCategory.tactic
    declNames := [`tactic.interactive.subst']
    tags := ["context management"] }

end Interactive

end Tactic