Open Seasawher opened 3 months ago
実装が大きくなってしまいましたが、普通に便利すぎるので標準にこんなコマンドが欲しくなりますね…
(欲を言えば、 #expand command ...
のようにシンタックスカテゴリを指定できるようにできたら最強になると思うんですが、...
の部分で期待するカテゴリが固定できなくなるので流石に無理そう)
import Lean
open Lean Elab Command
/-- syntax category for `#expand` -/
declare_syntax_cat macro_stx
syntax (name := macro_stx.command) command : macro_stx
syntax (name := macro_stx.tactic) tactic : macro_stx
syntax (name := macro_stx.term) term : macro_stx
elab "#print_stx" t:macro_stx : command => do
logInfo m!"{t.raw.formatStx}"
-- `rfl` は `tactic` だけでなく `term` としても解釈可能
#check rfl
-- こういう場合、`choice`で可能性が表現されている
/-- info: (choice (macro_stx.term `rfl) (macro_stx.tactic (Tactic.tacticRfl "rfl"))) -/
#guard_msgs in #print_stx rfl
partial def getStx (stx : TSyntax `macro_stx) : (Syntax × SyntaxNodeKind) :=
match stx.raw with
| Syntax.node _ `choice #[_t1, t2] =>
-- t1は多分`term`としての解釈で興味のない方なので、t2を選ぶ(ちゃんとした根拠はない)
getStx (TSyntax.mk t2)
| Syntax.node _ ``macro_stx.command #[t] => (t, `command)
| Syntax.node _ ``macro_stx.tactic #[t] => (t, `tactic)
| Syntax.node _ ``macro_stx.term #[t] => (t, `term)
| _ => panic! "想定していないパターン"
def findElab (stx : TSyntax `macro_stx) : CommandElabM Unit := do
let (stx', stxKind) : (Syntax × SyntaxNodeKind) := getStx stx
let macroRes ← liftMacroM <| expandMacroImpl? (←getEnv) (@TSyntax.mk stxKind stx')
match macroRes with
| some (name, _) => logInfo s!"Next step is a macro: {name.toString}"
| none =>
let kind := stx'.getKind
let env ← getEnv
let elabs : List Name := match stxKind with
| `command =>
commandElabAttribute.getEntries env kind
|>.map (fun el => el.declName)
| `tactic =>
Tactic.tacticElabAttribute.getEntries env kind
|>.map (fun el => el.declName)
| `term =>
Term.termElabAttribute.getEntries env kind
|>.map (fun el => el.declName)
| _ => panic! "想定していないパターン"
match elabs with
| [] => logInfo s!"There is no elaborators for your syntax, looks like its bad :("
| _ => logInfo s!"Your syntax may be elaborated by: {elabs}"
-- new command for both tactic and command
elab "#expand " stx:macro_stx : command => do
let (stx', _) : (Syntax × SyntaxNodeKind) := getStx stx
match ← Elab.liftMacroM <| Lean.Macro.expandMacro? stx' with
| none => findElab stx
| some t => logInfo m!"{t}"
#expand 1
#expand 1 + 1
#expand trivial
#expand apply hoge
#expand #check hoge
パースできるかチェックするコマンドは、構文カテゴリを引数に取っていたと思います https://lean-ja.github.io/lean-by-example/Command/Declarative/Syntax.html
String を入力に取って、コマンド内でパースすればできるかも
メタプロ本に載っている例: https://leanprover-community.github.io/lean4-metaprogramming-book/main/07_elaboration.html#mini-project