-
Notifications
You must be signed in to change notification settings - Fork 259
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
``` example (h : 1 < 0) : False := by hint example {P Q : Prop} (p : P) (f : P → Q) : Q := by hint example {P Q R: Prop} (x : P ∧ Q ∧ R ∧ R) : Q ∧ P ∧ R := by hint example {a b : ℚ} (h : a < b) : ¬ b < a := by hint example : 37^2 - 35^2 = 72 * 2 := by hint example : Nat.Prime 37 := by hint ``` Tries out any tactics registered using `register_hint tac`, and reports which ones succeed using the new "Try these: " multiple suggestion widgets. Tactics that close the goal are highlighted in green. Tactics that succeed but don't close the goal display the new subgoals in the widget. If `tac` produces a "Try this: " message, use that instead of `tac`. I haven't hooked up `aesop` yet, because of leanprover-community/aesop#85. Similarly for `norm_num`. I would like to parallelize this, but I don't think that needs to happen right away. Co-authored-by: Scott Morrison <scott.morrison@gmail.com> Co-authored-by: Komyyy <pol_tta@outlook.jp>
- Loading branch information
1 parent
061dc38
commit 3595903
Showing
10 changed files
with
224 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/- | ||
Copyright (c) 2023 Scott Morrison. All rights reserved. | ||
Released under Apache 2.0 license as described in the file LICENSE. | ||
Authors: Scott Morrison | ||
-/ | ||
import Std.Tactic.TryThis | ||
import Std.Linter.UnreachableTactic | ||
import Mathlib.Data.Nondet.Basic | ||
import Mathlib.Tactic.FailIfNoProgress | ||
import Mathlib.Mathport.Rename | ||
|
||
/-! | ||
# The `hint` tactic. | ||
The `hint` tactic tries the kitchen sink: | ||
it runs every tactic registered via the `register_hint tac` command | ||
on the current goal, and reports which ones succeed. | ||
## Future work | ||
It would be nice to run the tactics in parallel. | ||
-/ | ||
|
||
open Lean Elab Tactic | ||
|
||
open Std.Tactic.TryThis | ||
|
||
namespace Mathlib.Tactic.Hint | ||
|
||
/-- An environment extension for registering hint tactics. -/ | ||
initialize hintExtension : SimplePersistentEnvExtension (TSyntax `tactic) (List (TSyntax `tactic)) ← | ||
registerSimplePersistentEnvExtension { | ||
addEntryFn := (·.cons) | ||
addImportedFn := mkStateFromImportedEntries (·.cons) {} | ||
} | ||
|
||
/-- Register a new hint tactic. -/ | ||
def addHint (stx : TSyntax `tactic) : CoreM Unit := do | ||
modifyEnv fun env => hintExtension.addEntry env stx | ||
|
||
/-- Return the list of registered hint tactics. -/ | ||
def getHints : CoreM (List (TSyntax `tactic)) := return hintExtension.getState (← getEnv) | ||
|
||
open Lean.Elab.Command in | ||
/-- | ||
Register a tactic for use with the `hint` tactic, e.g. `register_hint simp_all`. | ||
-/ | ||
elab (name := registerHintStx) "register_hint" tac:tactic : command => liftTermElabM do | ||
-- remove comments | ||
let tac : TSyntax `tactic := ⟨tac.raw.copyHeadTailInfoFrom .missing⟩ | ||
addHint tac | ||
|
||
initialize | ||
Std.Linter.UnreachableTactic.ignoreTacticKindsRef.modify fun s => s.insert ``registerHintStx | ||
|
||
/-- | ||
Construct a suggestion for a tactic. | ||
* Check the passed `MessageLog` for an info message beginning with "Try this: ". | ||
* If found, use that as the suggestion. | ||
* Otherwise use the provided syntax. | ||
* Also, look for remaining goals and pretty print them after the suggestion. | ||
-/ | ||
def suggestion (tac : TSyntax `tactic) (msgs : MessageLog := {}) : TacticM Suggestion := do | ||
-- TODO `addExactSuggestion` has an option to construct `postInfo?` | ||
-- Factor that out so we can use it here instead of copying and pasting? | ||
let goals ← getGoals | ||
let postInfo? ← if goals.isEmpty then pure none else | ||
let mut str := "\nRemaining subgoals:" | ||
for g in goals do | ||
let e ← PrettyPrinter.ppExpr (← instantiateMVars (← g.getType)) | ||
str := str ++ Format.pretty ("\n⊢ " ++ e) | ||
pure (some str) | ||
let style? := if goals.isEmpty then some .success else none | ||
let msg? ← msgs.toList.findM? fun m => do pure <| | ||
m.severity == MessageSeverity.information && (← m.data.toString).startsWith "Try this: " | ||
let suggestion ← match msg? with | ||
| some m => pure <| SuggestionText.string (((← m.data.toString).drop 10).takeWhile (· != '\n')) | ||
| none => pure <| SuggestionText.tsyntax tac | ||
return { suggestion, postInfo?, style? } | ||
|
||
/-- Run a tactic, returning any new messages rather than adding them to the message log. -/ | ||
def withMessageLog (t : TacticM Unit) : TacticM MessageLog := do | ||
let initMsgs ← modifyGetThe Core.State fun st => (st.messages, { st with messages := {} }) | ||
t | ||
modifyGetThe Core.State fun st => (st.messages, { st with messages := initMsgs }) | ||
|
||
/-- | ||
Run a tactic, but revert any changes to info trees. | ||
We use this to inhibit the creation of widgets by subsidiary tactics. | ||
-/ | ||
def withoutInfoTrees (t : TacticM Unit) : TacticM Unit := do | ||
let trees := (← getInfoState).trees | ||
t | ||
modifyInfoState fun s => { s with trees } | ||
|
||
/-- | ||
Run all tactics registered using `register_hint`. | ||
Print a "Try these:" suggestion for each of the successful tactics. | ||
If one tactic succeeds and closes the goal, we don't look at subsequent tactics. | ||
-/ | ||
-- TODO We could run the tactics in parallel. | ||
-- TODO With widget support, could we run the tactics in parallel | ||
-- and do live updates of the widget as results come in? | ||
def hint (stx : Syntax) : TacticM Unit := do | ||
let tacs := Nondet.ofList (← getHints) | ||
let results := tacs.filterMapM fun t : TSyntax `tactic => do | ||
if let some msgs ← observing? (withMessageLog (withoutInfoTrees (evalTactic t))) then | ||
return some (← getGoals, ← suggestion t msgs) | ||
else | ||
return none | ||
let results ← (results.toMLList.takeUpToFirst fun r => r.1.1.isEmpty).asArray | ||
let results := results.qsort (·.1.1.length < ·.1.1.length) | ||
addSuggestions stx (results.map (·.1.2)) | ||
match results.find? (·.1.1.isEmpty) with | ||
| some r => | ||
-- We don't restore the entire state, as that would delete the suggestion messages. | ||
setMCtx r.2.term.meta.meta.mctx | ||
| none => admitGoal (← getMainGoal) | ||
|
||
/-- | ||
The `hint` tactic tries every tactic registered using `register_hint tac`, | ||
and reports any that succeed. | ||
-/ | ||
syntax (name := hintStx) "hint" : tactic | ||
|
||
@[inherit_doc hintStx] | ||
elab_rules : tactic | ||
| `(tactic| hint%$tk) => hint tk | ||
|
||
end Mathlib.Tactic.Hint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,14 @@ | ||
/- | ||
Copyright (c) 2018 Robert Y. Lewis. All rights reserved. | ||
Released under Apache 2.0 license as described in the file LICENSE. | ||
Authors: Robert Y. Lewis | ||
-/ | ||
import Mathlib.Tactic.Linarith.Frontend | ||
import Mathlib.Tactic.NormNum | ||
import Mathlib.Tactic.Hint | ||
|
||
/-! | ||
We register `linarith` with the `hint` tactic. | ||
-/ | ||
|
||
register_hint linarith |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import Mathlib.Tactic.Common | ||
import Mathlib.Tactic.Linarith | ||
import Mathlib.Data.Nat.Prime | ||
|
||
/-- | ||
info: Try these: | ||
• linarith | ||
-/ | ||
#guard_msgs in | ||
example (h : 1 < 0) : False := by hint | ||
|
||
/-- | ||
info: Try these: | ||
• exact f p | ||
-/ | ||
#guard_msgs in | ||
example {P Q : Prop} (p : P) (f : P → Q) : Q := by hint | ||
|
||
/-- | ||
info: Try these: | ||
• simp_all only [and_self] | ||
-/ | ||
#guard_msgs in | ||
example {P Q R: Prop} (x : P ∧ Q ∧ R ∧ R) : Q ∧ P ∧ R := by hint | ||
|
||
/-- | ||
info: Try these: | ||
• linarith | ||
-/ | ||
#guard_msgs in | ||
example {a b : ℚ} (h : a < b) : ¬ b < a := by hint | ||
|
||
/-- | ||
info: Try these: | ||
• exact rfl | ||
-/ | ||
#guard_msgs in | ||
example : 37^2 - 35^2 = 72 * 2 := by hint | ||
|
||
/-- | ||
info: Try these: | ||
• simp_all only | ||
-/ | ||
#guard_msgs in | ||
example : Nat.Prime 37 := by hint | ||
|
||
/-- | ||
info: Try these: | ||
• aesop | ||
• simp_all only [zero_le, and_true] | ||
-/ | ||
#guard_msgs in | ||
example {P : Nat → Prop} (h : { x // P x }) : ∃ x, P x ∧ 0 ≤ x := by hint |