@@ -18,6 +18,7 @@ to entries in external mathematical databases:
1818* `@[kerodon TAG]` — [ Kerodon ] (https://kerodon.net/tag/)
1919* `@[wikidata QID]` — [ Wikidata ] (https://www.wikidata.org)
2020* `@[lmfdb ID]` — [ LMFDB ] (https://www.lmfdb.org)
21+ * `@[pibase ID]` — [ π-Base ] (https://topology.pi-base.org/)
2122
2223 Each attribute records the cross-reference in an environment extension and appends
2324a link to the declaration's docstring.
@@ -37,30 +38,41 @@ namespace Mathlib.CrossRef
3738inductive Database where
3839 | kerodon
3940 | lmfdb
41+ | pibase
4042 | stacks
4143 | wikidata
4244 deriving BEq, Hashable, Ord
4345
4446namespace Database
4547
46- /-- The base URL for an external database's tag pages. Always ends with `/`. -/
47- def url : Database → String
48- | .kerodon => "https://kerodon.net/tag/"
49- | .lmfdb => "https://www.lmfdb.org/knowledge/show/"
50- | .stacks => "https://stacks.math.columbia.edu/tag/"
51- | .wikidata => "https://www.wikidata.org/wiki/"
48+ /-- The URL for an external database entry. -/
49+ def url (db : Database) (id : String) : String :=
50+ match db with
51+ | .kerodon => "https://kerodon.net/tag/" ++ id
52+ | .lmfdb => "https://www.lmfdb.org/knowledge/show/" ++ id
53+ | .pibase =>
54+ let path := match id.toList with
55+ | 'P' :: _ => "properties/"
56+ | 'S' :: _ => "spaces/"
57+ | 'T' :: _ => "theorems/"
58+ | _ => ""
59+ "https://topology.pi-base.org/" ++ path ++ id
60+ | .stacks => "https://stacks.math.columbia.edu/tag/" ++ id
61+ | .wikidata => "https://www.wikidata.org/wiki/" ++ id
5262
5363/-- The display label used in docstring links and trace output. -/
5464def label : Database → String
5565 | .kerodon => "Kerodon Tag"
5666 | .lmfdb => "LMFDB"
67+ | .pibase => "π-Base"
5768 | .stacks => "Stacks Tag"
5869 | .wikidata => "Wikidata"
5970
6071/-- A lowercase short name for the given database. Useful when exporting to JSON. -/
6172def shortName : Database → String
6273 | .kerodon => "kerodon"
6374 | .lmfdb => "lmfdb"
75+ | .pibase => "pibase"
6476 | .stacks => "stacks"
6577 | .wikidata => "wikidata"
6678
@@ -97,7 +109,7 @@ This is the database-agnostic core of every cross-reference attribute's `add` ha
97109def addCrossRefDoc (db : Database) (decl : Name) (idStr comment : String) : CoreM Unit := do
98110 let oldDoc := (← findDocString? (← getEnv) decl).getD ""
99111 let commentInDoc := if comment.isEmpty then "" else s! " ({ comment} )"
100- let link := s! "[{ db.label} { idStr} ]({ db.url}{ idStr} ){ commentInDoc} "
112+ let link := s! "[{ db.label} { idStr} ]({ db.url idStr} ){ commentInDoc} "
101113 addDocStringCore decl <| "\n\n " .intercalate ([oldDoc, link].filter (· != "" ))
102114 addTagEntry decl db idStr comment
103115
@@ -207,6 +219,49 @@ def lmfdbIdNoAntiquot : Parser := {
207219def lmfdbIdParser : Parser :=
208220 withAntiquot (mkAntiquot "lmfdbId" lmfdbIdKind) lmfdbIdNoAntiquot
209221
222+ /-! ### π-Base parser -/
223+
224+ /-- `pibaseId` is the node kind of π-Base identifiers: one of the letters `P`, `S`, or `T`,
225+ followed by exactly six digits. -/
226+ abbrev pibaseIdKind : SyntaxNodeKind := `pibaseId
227+
228+ /-- The main parser for π-Base identifiers: it accepts canonical property, space, and theorem
229+ identifiers, such as `P000001`, `S000023`, and `T000010`. -/
230+ def pibaseIdFn : ParserFn := fun c s =>
231+ let i := s.pos
232+ let s := takeWhileFn (fun c => c.isAlphanum) c s
233+ if s.hasError then
234+ s
235+ else if s.pos == i then
236+ ParserState.mkError s "π-Base id"
237+ else
238+ let id := c.extract i s.pos
239+ match id.toList with
240+ | kind :: rest =>
241+ if kind != 'P' && kind != 'S' && kind != 'T' then
242+ ParserState.mkUnexpectedError s
243+ "π-Base ids must start with P, S, or T."
244+ else if rest.length != 6 then
245+ ParserState.mkUnexpectedError s "π-Base ids must have exactly six digits after P/S/T."
246+ else if !rest.all Char.isDigit then
247+ ParserState.mkUnexpectedError s
248+ "π-Base ids must consist of P, S, or T followed by six digits."
249+ else
250+ mkNodeToken pibaseIdKind i true c s
251+ | _ =>
252+ ParserState.mkUnexpectedError s
253+ "π-Base ids must consist of P, S, or T followed by six digits."
254+
255+ @ [inherit_doc pibaseIdFn]
256+ def pibaseIdNoAntiquot : Parser := {
257+ fn := pibaseIdFn
258+ info := mkAtomicInfo "pibaseId"
259+ }
260+
261+ @ [inherit_doc pibaseIdFn]
262+ def pibaseIdParser : Parser :=
263+ withAntiquot (mkAntiquot "pibaseId" pibaseIdKind) pibaseIdNoAntiquot
264+
210265end Mathlib.CrossRef
211266
212267open Mathlib.CrossRef
@@ -226,6 +281,11 @@ def Lean.TSyntax.getLmfdbId (stx : TSyntax lmfdbIdKind) : CoreM String := do
226281 let some val := Syntax.isLit? lmfdbIdKind stx | throwError "Malformed LMFDB id"
227282 return val
228283
284+ /-- Extract the underlying identifier as a string from a `pibaseId` node. -/
285+ def Lean.TSyntax.getPibaseId (stx : TSyntax pibaseIdKind) : CoreM String := do
286+ let some val := Syntax.isLit? pibaseIdKind stx | throwError "Malformed π-Base id"
287+ return val
288+
229289namespace Lean.PrettyPrinter
230290
231291namespace Formatter
@@ -242,6 +302,10 @@ namespace Formatter
242302@ [combinator_formatter lmfdbIdNoAntiquot] def lmfdbIdNoAntiquot.formatter :=
243303 visitAtom lmfdbIdKind
244304
305+ /-- The formatter for π-Base identifier syntax. -/
306+ @ [combinator_formatter pibaseIdNoAntiquot] def pibaseIdNoAntiquot.formatter :=
307+ visitAtom pibaseIdKind
308+
245309end Formatter
246310
247311namespace Parenthesizer
@@ -255,6 +319,9 @@ namespace Parenthesizer
255319/-- The parenthesizer for LMFDB identifier syntax. -/
256320@ [combinator_parenthesizer lmfdbIdNoAntiquot] def lmfdbIdAntiquot.parenthesizer := visitToken
257321
322+ /-- The parenthesizer for π-Base identifier syntax. -/
323+ @ [combinator_parenthesizer pibaseIdNoAntiquot] def pibaseIdAntiquot.parenthesizer := visitToken
324+
258325end Lean.PrettyPrinter.Parenthesizer
259326
260327namespace Mathlib.CrossRef
@@ -335,6 +402,28 @@ initialize Lean.registerBuiltinAttribute {
335402 applicationTime := .beforeElaboration
336403}
337404
405+ /-! ### π-Base attribute -/
406+
407+ /-- The `pibase` attribute.
408+ Use it as `@[pibase P000001 "Optional comment"]` to associate a Mathlib declaration with
409+ the corresponding [ π-Base ] (https://topology.pi-base.org/) property, space, or theorem.
410+
411+ The identifier must start with `P`, `S`, or `T`, followed by exactly six digits.
412+ -/
413+ syntax (name := pibaseTag) "pibase" pibaseIdParser (ppSpace str)? : attr
414+
415+ initialize Lean.registerBuiltinAttribute {
416+ name := `pibaseTag
417+ descr := "Apply a π-Base identifier to a declaration."
418+ add := fun decl stx _attrKind => do
419+ let (id, comment) ← match stx with
420+ | `(attr| pibase $id $[$comment]?) => pure (id, comment)
421+ | _ => throwUnsupportedSyntax
422+ addCrossRefDoc .pibase decl (← id.getPibaseId) ((comment.map (·.getString)).getD "" )
423+ -- docstrings are immutable once an asynchronous elaboration task has been started
424+ applicationTime := .beforeElaboration
425+ }
426+
338427end Mathlib.CrossRef
339428
340429/-- Returns the array of `Tag`s in the environment, sorted alphabetically by tag. -/
@@ -361,7 +450,7 @@ def traceCrossRefs (db : Database) (verbose : Bool := false) :
361450 let (parL, parR) := if d.comment.isEmpty then ("" , "" ) else (" (" , ")" )
362451 let cmt := parL ++ d.comment ++ parR
363452 msgs := msgs.push
364- m! "[{ db.label} { d.tag} ]({ db.url ++ d.tag} ) \
453+ m! "[{ db.label} { d.tag} ]({ db.url d.tag} ) \
365454 corresponds to declaration '{ .ofConstName d.declName} '.{ cmt} "
366455 if verbose then
367456 let dType := ((env.find? d.declName).getD default).type
@@ -418,4 +507,16 @@ or declaration type (for definitions, structures, instances, etc.) after each su
418507elab (name := lmfdbTags) "#lmfdb_tags" tk:("!" )? : command =>
419508 traceCrossRefs .lmfdb (tk.isSome)
420509
510+ /-- The `#pibase_tags` command retrieves all declarations that have the `pibase` attribute.
511+
512+ For each found declaration, it prints a line
513+ ```
514+ 'declaration_name' corresponds to tag 'declaration_tag'.
515+ ```
516+ The variant `#pibase_tags!` also adds the theorem statement (for theorems)
517+ or declaration type (for definitions, structures, instances, etc.) after each summary line.
518+ -/
519+ elab (name := pibaseTags) "#pibase_tags" tk:("!" )? : command =>
520+ traceCrossRefs .pibase (tk.isSome)
521+
421522end Mathlib.CrossRef
0 commit comments