This PR ensures whitespace is printed before `+opt` and `-opt` configuration options when pretty printing, improving the experience of tactics such as `simp?`. Reported [on Zulip](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Minor.20simp.3F.20annoyances/near/483736310)
253 lines
5.3 KiB
Text
253 lines
5.3 KiB
Text
import Lean
|
|
/-!
|
|
# Tests for tactic configuration elaboration
|
|
-/
|
|
|
|
open Lean
|
|
|
|
/-!
|
|
Simple tactic configuration
|
|
-/
|
|
structure MyTacticConfig where
|
|
x : Nat := 0
|
|
y : Bool := false
|
|
deriving Repr
|
|
|
|
declare_config_elab elabMyTacticConfig MyTacticConfig
|
|
|
|
elab "my_tactic" cfg:Parser.Tactic.optConfig : tactic => do
|
|
let config ← elabMyTacticConfig cfg
|
|
logInfo m!"config is {repr config}"
|
|
|
|
/--
|
|
info: config is { x := 0, y := false }
|
|
---
|
|
info: config is { x := 0, y := true }
|
|
---
|
|
info: config is { x := 1, y := false }
|
|
---
|
|
info: config is { x := 2, y := false }
|
|
---
|
|
info: config is { x := 1, y := true }
|
|
---
|
|
info: config is { x := 0, y := false }
|
|
-/
|
|
#guard_msgs in
|
|
example : True := by
|
|
my_tactic
|
|
my_tactic +y
|
|
my_tactic (x := 1)
|
|
my_tactic -y (x := 2)
|
|
my_tactic (config := {x := 1, y := true})
|
|
my_tactic +y (config := {y := false})
|
|
trivial
|
|
|
|
/-!
|
|
Basic errors
|
|
-/
|
|
|
|
/--
|
|
error: option is not boolean-valued, so '(x := ...)' syntax must be used
|
|
---
|
|
info: config is { x := 0, y := false }
|
|
---
|
|
error: unsolved goals
|
|
⊢ True
|
|
-/
|
|
#guard_msgs in example : True := by my_tactic +x
|
|
|
|
/--
|
|
error: structure 'MyTacticConfig' does not have a field named 'w'
|
|
---
|
|
info: config is { x := 0, y := false }
|
|
---
|
|
error: unsolved goals
|
|
⊢ True
|
|
-/
|
|
#guard_msgs in example : True := by my_tactic +w
|
|
|
|
/--
|
|
error: field 'x' of structure 'MyTacticConfig' is not a structure
|
|
---
|
|
info: config is { x := 0, y := false }
|
|
---
|
|
error: unsolved goals
|
|
⊢ True
|
|
-/
|
|
#guard_msgs in example : True := by my_tactic +x.a
|
|
|
|
/-!
|
|
A tactic configuration extending another with different default values.
|
|
-/
|
|
structure MyTacticConfig' extends MyTacticConfig where
|
|
x := 22
|
|
y := true
|
|
deriving Repr
|
|
|
|
declare_config_elab elabMyTacticConfig' MyTacticConfig'
|
|
|
|
elab "my_tactic'" cfg:Parser.Tactic.optConfig : tactic => do
|
|
let config ← elabMyTacticConfig' cfg
|
|
logInfo m!"config is {repr config}"
|
|
|
|
/--
|
|
info: config is { toMyTacticConfig := { x := 22, y := true } }
|
|
---
|
|
info: config is { toMyTacticConfig := { x := 22, y := true } }
|
|
---
|
|
info: config is { toMyTacticConfig := { x := 1, y := true } }
|
|
---
|
|
info: config is { toMyTacticConfig := { x := 2, y := false } }
|
|
---
|
|
info: config is { toMyTacticConfig := { x := 1, y := true } }
|
|
---
|
|
info: config is { toMyTacticConfig := { x := 22, y := false } }
|
|
-/
|
|
#guard_msgs in
|
|
example : True := by
|
|
my_tactic'
|
|
my_tactic' +y
|
|
my_tactic' (x := 1)
|
|
my_tactic' -y (x := 2)
|
|
my_tactic' (config := {x := 1, y := true})
|
|
my_tactic' +y (config := {y := false})
|
|
trivial
|
|
|
|
/-!
|
|
Tactic configurations with hierarchical fields
|
|
-/
|
|
|
|
structure A where
|
|
x : Bool := true
|
|
deriving Repr
|
|
structure B extends A
|
|
deriving Repr
|
|
structure C where
|
|
b : B := {}
|
|
deriving Repr
|
|
declare_config_elab elabC C
|
|
|
|
elab "ctac" cfg:Parser.Tactic.optConfig : tactic => do
|
|
let config ← elabC cfg
|
|
logInfo m!"config is {repr config}"
|
|
|
|
/--
|
|
info: config is { b := { toA := { x := false } } }
|
|
---
|
|
info: config is { b := { toA := { x := false } } }
|
|
-/
|
|
#guard_msgs in
|
|
example : True := by
|
|
ctac -b.x
|
|
ctac -b.toA.x
|
|
trivial
|
|
|
|
/-!
|
|
Responds to recovery mode. In these, `ctac` continues even though configuration elaboration failed.
|
|
-/
|
|
|
|
/--
|
|
error: structure 'C' does not have a field named 'x'
|
|
---
|
|
info: config is { b := { toA := { x := true } } }
|
|
-/
|
|
#guard_msgs in
|
|
example : True := by
|
|
ctac -x
|
|
trivial
|
|
|
|
/--
|
|
error: structure 'C' does not have a field named 'x'
|
|
---
|
|
info: config is { b := { toA := { x := true } } }
|
|
---
|
|
error: unsolved goals
|
|
⊢ True
|
|
-/
|
|
#guard_msgs in
|
|
example : True := by
|
|
ctac -x
|
|
done
|
|
|
|
/-!
|
|
Responds to recovery mode. In this, `ctac` fails, doesn't report anything, and then execution continues to `exact`.
|
|
-/
|
|
|
|
/-- error: unknown identifier 'blah' -/
|
|
#guard_msgs in
|
|
example : True := by
|
|
first | ctac +x | exact blah
|
|
|
|
/-!
|
|
Elaboration errors cause the tactic to use the default configuration.
|
|
-/
|
|
|
|
/--
|
|
error: type mismatch
|
|
false
|
|
has type
|
|
Bool : Type
|
|
but is expected to have type
|
|
B : Type
|
|
---
|
|
info: config is { b := { toA := { x := true } } }
|
|
---
|
|
error: unsolved goals
|
|
⊢ True
|
|
-/
|
|
#guard_msgs in
|
|
example : True := by
|
|
ctac (b := false)
|
|
done
|
|
|
|
|
|
/-!
|
|
Elaboration for command configuration
|
|
-/
|
|
|
|
structure MyCommandConfig where
|
|
x : Nat := 0
|
|
y : Bool := false
|
|
deriving Repr
|
|
|
|
declare_command_config_elab elabMyCommandConfig MyCommandConfig
|
|
|
|
elab "my_command" cfg:Parser.Tactic.optConfig : command => do
|
|
let config ← elabMyCommandConfig cfg
|
|
logInfo m!"config is {repr config}"
|
|
|
|
/-- info: config is { x := 0, y := false } -/
|
|
#guard_msgs in my_command
|
|
/-- info: config is { x := 0, y := true } -/
|
|
#guard_msgs in my_command +y
|
|
/-- info: config is { x := 1, y := true } -/
|
|
#guard_msgs in my_command (x := 1) (y := true)
|
|
/-- info: config is { x := 0, y := false } -/
|
|
#guard_msgs in my_command (x := 1) (y := true) (config := {})
|
|
/--
|
|
error: type mismatch
|
|
true
|
|
has type
|
|
Bool : Type
|
|
but is expected to have type
|
|
Nat : Type
|
|
---
|
|
info: config is { x := 0, y := false }
|
|
-/
|
|
#guard_msgs in my_command (x := true)
|
|
|
|
|
|
/-!
|
|
Pretty printing of configuration, checking whitespace is present.
|
|
-/
|
|
elab "#pp_tac " t:tactic : command => Elab.Command.liftTermElabM do
|
|
logInfo (← PrettyPrinter.ppTactic t)
|
|
|
|
/-- info: simp +contextual -/
|
|
#guard_msgs in #pp_tac simp +contextual
|
|
/-- info: simp +contextual -/
|
|
#guard_msgs in #pp_tac simp+contextual
|
|
/-- info: simp (contextual := true) +zeta -/
|
|
#guard_msgs in #pp_tac simp (contextual := true) +zeta
|
|
/-- info: simp (contextual := true) +zeta -/
|
|
#guard_msgs in #pp_tac simp(contextual := true)+zeta
|