Zulip Chat Archive
Stream: lean4
Topic: get docstring for tactic
Jon Eugster (May 22 2023 at 20:10):
Using Lean.findDocString? env name
I can get the docstring of a declaration, but that does not seem to work for tactics. How would I retrieve the docstring of a tactic, say simp
?
Mario Carneiro (May 22 2023 at 20:50):
the docstring on a tactic is the docstring on the syntax
declaration for that tactic:
import Mathlib.Tactic.HelpCmd
#help tactic simp
-- syntax "simp"... [Lean.Parser.Tactic.simp]
open Lean Meta Elab
#eval show MetaM _ from do
let some s ← findDocString? (← getEnv) ``Lean.Parser.Tactic.simp | failure
println! s
-- The `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
-- non-dependent hypotheses. It has many variants: ...
Jon Eugster (May 22 2023 at 21:10):
Thanks!
Now I just have to figure out how to get ``Lean.Parser.Tactic.simp
starting from a Name
/Ident
saying just simp
, but that is a problem for another day. Maybe just something along the lines of ``(tactic| ${name})
(not on the computer anymore)
Mario Carneiro (May 22 2023 at 21:28):
that's what the first line was for
Mario Carneiro (May 22 2023 at 21:29):
the #help
command implements exactly this lookup
Mario Carneiro (May 22 2023 at 21:30):
although depending on the context you might want to just parse a syntax instead. What are you actually trying to do?
Jon Eugster (May 23 2023 at 13:30):
Looking quickly at #help
it isnt completely obvious to me how that lookup works, but maybe Ill figure it out when I look at it for another 20min
As for why, it's for the games (NNG, etc). The author can provide documentation for introduced tactics with a command like
TacticDoc simp "some text"
and I would like to use the tactic's docstring in case they don't provide a documentation that way.
Mario Carneiro (May 23 2023 at 17:22):
why not just require the author to do the lookup and refer to the appropriately namespaced constant?
Alexander Bentkamp (May 24 2023 at 07:08):
We simply would like to make it as easy as possible for authors. Game authors are not necessarily good metaprogrammers.
Mario Carneiro (May 24 2023 at 07:11):
they can just use #help
to look up the names, as indicated
Mario Carneiro (May 24 2023 at 07:13):
Note that looking up a tactic by its initial token is a lossy process, which is why #help
acts more like a search engine. If you actually want to identify a particular tactic you need something more precise
Mario Carneiro (May 24 2023 at 07:14):
An alternative would be to have your syntax accept a full (syntactically correct) tactic expression and look up the name that way
Mario Carneiro (May 24 2023 at 07:17):
import Lean
open Lean Meta
elab doc:docComment "tactic_doc" t:tactic : command => do
let doc := doc.getDocString.trim
let kind := t.raw.getKind
logInfo m!"adding doc {repr doc} to {kind}"
/-- some text -/ tactic_doc simp
-- adding doc "some text" to Lean.Parser.Tactic.simp
/-- some text -/ tactic_doc cases ·
-- adding doc "some text" to Lean.Parser.Tactic.cases
/-- some text -/ tactic_doc · skip
-- adding doc "some text" to cdot
/-- some text -/ tactic_doc skip
-- adding doc "some text" to Lean.Parser.Tactic.skip
/-- some text -/ tactic_doc rw [·]
-- adding doc "some text" to Lean.Parser.Tactic.rwSeq
Jon Eugster (May 24 2023 at 11:57):
Thanks Mario for the help! I wasn't quite aware that this tactic lookup is not a bijective process form the first token itself. I just had an hour on the train to compress #help
into something that should hopefully work well enough:
def getTacticDocstring (name: Name) : CommandElabM String := do
let name := name.toString (escape := false)
let mut decls : Lean.RBMap String (Array SyntaxNodeKind) compare := {}
let env ← getEnv
let catName : Name := `tactic
let catStx : Ident := mkIdent catName -- TODO
let some cat := (Parser.parserExtension.getState env).categories.find? catName
| throwErrorAt catStx "{catStx} is not a syntax category"
liftTermElabM <| Term.addCategoryInfo catStx catName
for (k, _) in cat.kinds do
let mut used := false
if let some tk := do getHeadTk (← (← env.find? k).value?) then
let tk := tk.trim
if name ≠ tk then -- was `!name.isPrefixOf tk`
continue
used := true
decls := decls.insert tk ((decls.findD tk #[]).push k)
for (_name, ks) in decls do
for k in ks do
if let some doc ← findDocString? env k then
return doc
logWarning <| m!"Could not find a docstring for this tactic, consider adding one " ++
m!"using `TacticDoc {name} \"some doc\"`"
return ""
Full MWE
I'm not sure if this might fail in some exotic cases, but hopefully it works for most common tactics.
Last updated: Dec 20 2023 at 11:08 UTC