Skip to content

Commit c76411d

Browse files
authored
feat: compact notation for inspecting grind state (#10828)
This PR implements a compact notation for inspecting the `grind` state in interactive mode. Within a `grind` tactic block, each tactic may optionally have a suffix of the form `| filter?`. Examples: ```lean instantiate | gen > 0 -- Displays terms in the `grind` state after executing `instantiate` with generation greater than zero ``` ```lean instantiate | -- Displays the `grind` state after executing `instantiate` ``` Remark: If the user places the cursor one space before `|`, the state *before* executing `instantiate` is displayed. This PR removes the code that was silently displaying the `grind` state after each tactic step, as it was too noisy. It also updates the notation for the `first` combinator in the `grind` tactic mode to avoid conflicts with the new syntax.
1 parent c221000 commit c76411d

File tree

7 files changed

+79
-58
lines changed

7 files changed

+79
-58
lines changed

src/Init/Grind/Interactive.lean

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,32 @@ when selecting patterns.
1717
syntax grindLemmaMin := ppGroup("!" (Attr.grindMod ppSpace)? ident)
1818

1919
namespace Grind
20+
declare_syntax_cat grind_filter (behavior := both)
21+
22+
syntax:max ident : grind_filter
23+
syntax:max &"gen" " < " num : grind_filter
24+
syntax:max &"gen" " = " num : grind_filter
25+
syntax:max &"gen" " != " num : grind_filter
26+
syntax:max &"gen" "" num : grind_filter
27+
syntax:max &"gen" " <= " num : grind_filter
28+
syntax:max &"gen" " > " num : grind_filter
29+
syntax:max &"gen" "" num : grind_filter
30+
syntax:max &"gen" " >= " num : grind_filter
31+
syntax:max "(" grind_filter ")" : grind_filter
32+
syntax:35 grind_filter:35 " && " grind_filter:36 : grind_filter
33+
syntax:35 grind_filter:35 " || " grind_filter:36 : grind_filter
34+
syntax:max "!" grind_filter:40 : grind_filter
35+
36+
syntax grindFilter := (colGt grind_filter)?
2037

2138
/-- `grind` is the syntax category for a "grind interactive tactic".
2239
A `grind` tactic is a program which receives a `grind` goal. -/
2340
declare_syntax_cat grind (behavior := both)
2441

25-
syntax grindSeq1Indented := sepBy1IndentSemicolon(grind)
26-
syntax grindSeqBracketed := "{" withoutPosition(sepByIndentSemicolon(grind)) "}"
42+
syntax grindStep := grind ("|" (colGt ppSpace grind_filter)?)?
43+
44+
syntax grindSeq1Indented := sepBy1IndentSemicolon(grindStep)
45+
syntax grindSeqBracketed := "{" withoutPosition(sepByIndentSemicolon(grindStep)) "}"
2746
syntax grindSeq := grindSeqBracketed <|> grindSeq1Indented
2847

2948
/-- `(grindSeq)` runs the `grindSeq` in sequence on the current list of targets.
@@ -50,24 +69,6 @@ syntax thm := anchor <|> grindLemma <|> grindLemmaMin
5069
/-- Instantiates theorems using E-matching. -/
5170
syntax (name := instantiate) "instantiate" (colGt thm),* : grind
5271

53-
declare_syntax_cat grind_filter (behavior := both)
54-
55-
syntax:max ident : grind_filter
56-
syntax:max &"gen" " < " num : grind_filter
57-
syntax:max &"gen" " = " num : grind_filter
58-
syntax:max &"gen" " != " num : grind_filter
59-
syntax:max &"gen" " ≤ " num : grind_filter
60-
syntax:max &"gen" " <= " num : grind_filter
61-
syntax:max &"gen" " > " num : grind_filter
62-
syntax:max &"gen" " ≥ " num : grind_filter
63-
syntax:max &"gen" " >= " num : grind_filter
64-
syntax:max "(" grind_filter ")" : grind_filter
65-
syntax:35 grind_filter:35 " && " grind_filter:36 : grind_filter
66-
syntax:35 grind_filter:35 " || " grind_filter:36 : grind_filter
67-
syntax:max "!" grind_filter:40 : grind_filter
68-
69-
syntax grindFilter := (colGt grind_filter)?
70-
7172
-- **Note**: Should we rename the following tactics to `trace_`?
7273
/-- Shows asserted facts. -/
7374
syntax (name := showAsserted) "show_asserted" ppSpace grindFilter : grind
@@ -145,10 +146,10 @@ macro:1 x:grind tk:" <;> " y:grind:2 : grind => `(grind|
145146
all_goals $y:grind)
146147

147148
/-- `first | tac | ...` runs each `tac` until one succeeds, or else fails. -/
148-
syntax (name := first) "first " withPosition((ppDedent(ppLine) colGe "| " grindSeq)+) : grind
149+
syntax (name := first) "first " withPosition((ppDedent(ppLine) colGe "(" grindSeq ")")+) : grind
149150

150151
/-- `try tac` runs `tac` and succeeds even if `tac` failed. -/
151-
macro "try " t:grindSeq : grind => `(grind| first | $t | skip)
152+
macro "try " t:grindSeq : grind => `(grind| first ($t:grindSeq) (skip))
152153

153154
/-- `fail_if_success t` fails if the tactic `t` succeeds. -/
154155
syntax (name := failIfSuccess) "fail_if_success " grindSeq : grind
@@ -168,7 +169,7 @@ The tactic `tac` should eventually fail, otherwise `repeat tac` will run indefin
168169
syntax "repeat " grindSeq : grind
169170

170171
macro_rules
171-
| `(grind| repeat $seq) => `(grind| first | ($seq); repeat $seq | skip)
172+
| `(grind| repeat $seq:grindSeq) => `(grind| first (($seq); repeat $seq:grindSeq) (skip))
172173

173174
/-- `rename_i x_1 ... x_n` renames the last `n` inaccessible names using the given names. -/
174175
syntax (name := renameI) "rename_i" (ppSpace colGt binderIdent)+ : grind

src/Lean/Elab/Tactic/Grind.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ prelude
88
public import Lean.Elab.Tactic.Grind.Main
99
public import Lean.Elab.Tactic.Grind.Basic
1010
public import Lean.Elab.Tactic.Grind.BuiltinTactic
11-
public import Lean.Elab.Tactic.Grind.Show
11+
public import Lean.Elab.Tactic.Grind.ShowState
1212
public import Lean.Elab.Tactic.Grind.Have

src/Lean/Elab/Tactic/Grind/Basic.lean

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,6 @@ def mkTacticInfo (mctxBefore : MetavarContext) (goalsBefore : List MVarId) (stx
8787
def mkInitialTacticInfo (stx : Syntax) : GrindTacticM (GrindTacticM Info) := do
8888
let mctxBefore ← getMCtx
8989
let goalsBefore ← getUnsolvedGoalMVarIds
90-
/-
91-
**Note**: We only display the grind state if there is exactly one goal.
92-
This is a hack because we currently use a silent info to display the grind state, and we cannot attach it after each goal.
93-
We claim this is not a big deal since the user will probably use `next =>` to focus on subgoals.
94-
-/
95-
if let [goal] ← getGoals then goal.withContext do
96-
let config := (← read).params.config
97-
let msg := MessageData.lazy fun ctx => do
98-
let .ok msg ← EIO.toBaseIO <| ctx.runMetaM
99-
<| Grind.goalDiagToMessageData goal config (header := "Grind state") (collapsedMain := false)
100-
| return "Grind state could not be generated"
101-
return msg
102-
logAt (severity := .information) (isSilent := true) stx msg
10390
return mkTacticInfo mctxBefore goalsBefore stx
10491

10592
@[inline] def withTacticInfoContext (stx : Syntax) (x : GrindTacticM α) : GrindTacticM α := do

src/Lean/Elab/Tactic/Grind/BuiltinTactic.lean

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,26 @@ import Lean.Meta.Tactic.ExposeNames
2424
import Lean.Elab.Tactic.Basic
2525
import Lean.Elab.Tactic.RenameInaccessibles
2626
import Lean.Elab.Tactic.Grind.Filter
27+
import Lean.Elab.Tactic.Grind.ShowState
2728
namespace Lean.Elab.Tactic.Grind
2829

30+
def showStateAt (ref : Syntax) (filter : Filter) : GrindTacticM Unit := do
31+
if let goalBefore :: _ := (← getGoals) then
32+
withRef ref <| goalBefore.withContext <| showState filter (isSilent := true)
33+
else
34+
logAt ref (severity := .information) (isSilent := true) "no grind state"
35+
2936
def evalSepTactics (stx : Syntax) : GrindTacticM Unit := do
3037
for arg in stx.getArgs, i in *...stx.getArgs.size do
3138
if i % 2 == 0 then
32-
evalGrindTactic arg
39+
match arg with
40+
| `(Parser.Tactic.Grind.grindStep| $tac:grind) => evalGrindTactic tac
41+
| `(Parser.Tactic.Grind.grindStep| $tac:grind | $[$filter?]?) =>
42+
let filter ← elabFilter filter?
43+
showStateAt arg filter
44+
evalGrindTactic tac
45+
showStateAt arg[1] filter
46+
| _ => throwUnsupportedSyntax
3347
else
3448
saveTacticInfoForToken arg
3549

@@ -371,15 +385,14 @@ public def renameInaccessibles (mvarId : MVarId) (hs : TSyntaxArray ``binderIden
371385
replaceMainGoal []
372386

373387
@[builtin_grind_tactic «first»] partial def evalFirst : GrindTactic := fun stx => do
374-
let tacs := stx[1].getArgs
375-
if tacs.isEmpty then throwUnsupportedSyntax
376-
loop tacs 0
388+
let `(grind| first $[($s:grindSeq)]*) := stx | throwUnsupportedSyntax
389+
loop s 0
377390
where
378-
loop (tacs : Array Syntax) (i : Nat) :=
379-
if i == tacs.size - 1 then
380-
evalGrindTactic tacs[i]![1]
391+
loop (s : Array (TSyntax ``Parser.Tactic.Grind.grindSeq)) (i : Nat) :=
392+
if i == s.size - 1 then
393+
evalGrindTactic s[i]!
381394
else
382-
evalGrindTactic tacs[i]![1] <|> loop tacs (i+1)
395+
evalGrindTactic s[i]! <|> loop s (i+1)
383396

384397
@[builtin_grind_tactic failIfSuccess] def evalFailIfSuccess : GrindTactic := fun stx =>
385398
Term.withoutErrToSorry <| withoutRecover do
Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
66
module
77
prelude
88
public import Lean.Elab.Tactic.Grind.Basic
9-
import Lean.Elab.Tactic.Grind.Filter
9+
public import Lean.Elab.Tactic.Grind.Filter
1010
import Lean.Meta.Tactic.Grind.PP
1111
import Lean.Meta.Tactic.Grind.Anchor
1212
import Lean.Meta.Tactic.Grind.Split
@@ -92,15 +92,18 @@ def ppEqcs? (filter : Filter) (collapsed := false) : GrindTacticM (Option Messag
9292
def pushIfSome (msgs : Array MessageData) (msg? : Option MessageData) : Array MessageData :=
9393
if let some msg := msg? then msgs.push msg else msgs
9494

95-
@[builtin_grind_tactic showState] def evalShowState : GrindTactic := fun stx => withMainContext do
96-
let `(grind| show_state $[$filter?]?) := stx | throwUnsupportedSyntax
97-
let filter ← elabFilter filter?
95+
public def showState (filter : Filter) (isSilent := false) : GrindTacticM Unit := do
9896
let msgs := #[]
9997
let msgs := pushIfSome msgs (← ppAsserted? filter (collapsed := true))
10098
let msgs := pushIfSome msgs (← ppProps? filter true (collapsed := false))
10199
let msgs := pushIfSome msgs (← ppProps? filter false (collapsed := false))
102100
let msgs := pushIfSome msgs (← ppEqcs? filter (collapsed := false))
103-
logInfo <| MessageData.trace { cls := `grind, collapsed := false } "Grind state" msgs
101+
logAt (← getRef) (severity := .information) (isSilent := isSilent) <| MessageData.trace { cls := `grind, collapsed := false } "Grind state" msgs
102+
103+
@[builtin_grind_tactic Parser.Tactic.Grind.showState] def evalShowState : GrindTactic := fun stx => withMainContext do
104+
let `(grind| show_state $[$filter?]?) := stx | throwUnsupportedSyntax
105+
let filter ← elabFilter filter?
106+
showState filter
104107

105108
@[builtin_grind_tactic showCases] def evalShowCases : GrindTactic := fun stx => withMainContext do
106109
let `(grind| show_cases $[$filter?]?) := stx | throwUnsupportedSyntax

src/Lean/Meta/Tactic/Grind/Action.lean

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,15 @@ use `callCC` here.
6868
6969
-/
7070

71+
abbrev TGrind := TSyntax `grind
72+
abbrev TGrindStep := TSyntax ``Parser.Tactic.Grind.grindStep
73+
7174
/-- Result type for a `grind` Action -/
7275
inductive ActionResult where
7376
| /--
7477
The goal has been closed, and you can use `seq` to close the goal efficiently.
7578
-/
76-
closed (seq : List (TSyntax `grind))
79+
closed (seq : List TGrind)
7780
| /--
7881
The action could not make further progress.
7982
`gs` are subgoals that could not be closed. They are used for producing error messages.
@@ -151,8 +154,16 @@ Executes `x`, but behaves like a `skip` if it is not applicable.
151154
def skipIfNA (x : Action) : Action := fun goal _ kp =>
152155
x goal kp kp
153156

154-
def mkGrindSeq (s : List (TSyntax `grind)) : TSyntax ``Parser.Tactic.Grind.grindSeq :=
155-
let s := s.map (·.raw)
157+
/-- ``TSyntax `grind`` => ``TSyntax `Lean.Parser.Tactic.Grind.grindStep`` -/
158+
def mkGrindStep (t : TGrind) : TGrindStep :=
159+
mkNode ``Parser.Tactic.Grind.grindStep #[ t, mkNullNode ]
160+
161+
def TGrindStep.getTactic : TGrindStep → TGrind
162+
| `(Parser.Tactic.Grind.grindStep| $tac:grind $[| $_]?) => tac
163+
| _ => ⟨mkNullNode⟩
164+
165+
def mkGrindSeq (s : List TGrind) : TSyntax ``Parser.Tactic.Grind.grindSeq :=
166+
let s := s.map fun tac => (mkGrindStep tac).raw
156167
let s := s.intersperse (mkNullNode #[])
157168
mkNode ``Parser.Tactic.Grind.grindSeq #[
158169
mkNode ``Parser.Tactic.Grind.grindSeq1Indented #[
@@ -169,7 +180,7 @@ next =>
169180
```
170181
If the list is empty, it returns `next => done`.
171182
-/
172-
def mkGrindNext (s : List (TSyntax `grind)) : CoreM (TSyntax `grind) := do
183+
def mkGrindNext (s : List TGrind) : CoreM TGrind := do
173184
let s ← if s == [] then pure [← `(grind| done)] else pure s
174185
let s := mkGrindSeq s
175186
`(grind| next => $s:grindSeq)
@@ -203,7 +214,7 @@ def ungroup : Action := fun goal _ kp => do
203214
match r with
204215
| .closed [tac] =>
205216
match tac with
206-
| `(grind| next => $seq;*) => return .closed seq.getElems.toList
217+
| `(grind| next => $seq;*) => return .closed <| seq.getElems.toList.map TGrindStep.getTactic
207218
| _ => return r
208219
| _ => return r
209220
else

tests/lean/run/grind_interactive.lean

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,13 +239,13 @@ example : h bs = 1 → h as ≠ 0 := by
239239
skip
240240
try fail
241241
fail_if_success fail
242-
first | fail | done | skip
242+
first (fail) (done) (skip)
243243
fail "Failed here"
244244
done
245245

246246
example : h bs = 1 → h as ≠ 0 := by
247247
grind [h.eq_def] =>
248-
instantiate
248+
instantiate | as
249249
cases #ec88
250250
all_goals instantiate
251251

@@ -268,6 +268,12 @@ example (r p q : Prop) : p ∨ r → p ∨ q → p ∨ ¬q → ¬p ∨ q → ¬p
268268
cases?
269269
sorry
270270

271+
set_option trace.Meta.debug true in
272+
example (r p q : Prop) : p ∨ r → p ∨ q → p ∨ ¬q → ¬p ∨ q → ¬p ∨ ¬q → False := by
273+
grind =>
274+
cases?
275+
sorry
276+
271277
/--
272278
info: Try these:
273279
[apply] cases #7a08 for
@@ -395,7 +401,7 @@ example (as bs cs : Array α) (v₁ v₂ : α)
395401
(h₆ : j < as.size)
396402
: cs[j] = as[j] := by
397403
grind =>
398-
instantiate Array.getElem_set
404+
instantiate Array.getElem_set | gen > 0
399405
instantiate Array.getElem_set
400406

401407
example (as bs cs : Array α) (v₁ v₂ : α)

0 commit comments

Comments
 (0)