@@ -16,6 +16,7 @@ to entries in external mathematical databases:
1616
1717* `@[stacks TAG]` — [ Stacks Project ] (https://stacks.math.columbia.edu/tags)
1818* `@[kerodon TAG]` — [ Kerodon ] (https://kerodon.net/tag/)
19+ * `@[wikidata QID]` — [ Wikidata ] (https://www.wikidata.org)
1920
2021 Each attribute records the cross-reference in an environment extension and appends
2122a link to the declaration's docstring.
@@ -35,17 +36,20 @@ namespace Mathlib.CrossRef
3536inductive Database where
3637 | kerodon
3738 | stacks
39+ | wikidata
3840 deriving BEq, Hashable
3941
4042/-- The base URL for an external database's tag pages. Always ends with `/`. -/
4143def databaseURL : Database → String
4244 | .kerodon => "https://kerodon.net/tag/"
4345 | .stacks => "https://stacks.math.columbia.edu/tag/"
46+ | .wikidata => "https://www.wikidata.org/wiki/"
4447
4548/-- The display label used in docstring links and trace output. -/
4649def databaseLabel : Database → String
4750 | .kerodon => "Kerodon Tag"
4851 | .stacks => "Stacks Tag"
52+ | .wikidata => "Wikidata"
4953
5054/-- A cross-reference from a Mathlib declaration to an entry in an external database. -/
5155structure Tag where
@@ -84,6 +88,8 @@ def addCrossRefDoc (db : Database) (decl : Name) (idStr comment : String) : Core
8488
8589open Parser
8690
91+ /-! ### Stacks (and Kerodon) parser -/
92+
8793/-- `stacksTag` is the node kind of Stacks Project Tags: a sequence of digits and
8894uppercase letters. -/
8995abbrev stacksTagKind : SyntaxNodeKind := `stacksTag
@@ -117,6 +123,42 @@ def stacksTagNoAntiquot : Parser := {
117123def stacksTagParser : Parser :=
118124 withAntiquot (mkAntiquot "stacksTag" stacksTagKind) stacksTagNoAntiquot
119125
126+ /-! ### Wikidata parser -/
127+
128+ /-- `wikidataId` is the node kind of Wikidata identifiers: the letter `Q` followed by digits. -/
129+ abbrev wikidataIdKind : SyntaxNodeKind := `wikidataId
130+
131+ /-- The main parser for Wikidata identifiers: it accepts `Q` followed by one or more digits. -/
132+ def wikidataIdFn : ParserFn := fun c s =>
133+ let i := s.pos
134+ let s := takeWhileFn (fun c => c.isAlphanum) c s
135+ if s.hasError then
136+ s
137+ else if s.pos == i then
138+ ParserState.mkError s "wikidata id"
139+ else
140+ let id := c.extract i s.pos
141+ match id.toList with
142+ | 'Q' :: rest@(_ :: _) =>
143+ if rest.all Char.isDigit then
144+ mkNodeToken wikidataIdKind i true c s
145+ else
146+ ParserState.mkUnexpectedError s
147+ "Wikidata ids must consist of the letter Q followed by digits."
148+ | _ =>
149+ ParserState.mkUnexpectedError s
150+ "Wikidata ids must start with the letter Q followed by one or more digits."
151+
152+ @ [inherit_doc wikidataIdFn]
153+ def wikidataIdNoAntiquot : Parser := {
154+ fn := wikidataIdFn
155+ info := mkAtomicInfo "wikidataId"
156+ }
157+
158+ @ [inherit_doc wikidataIdFn]
159+ def wikidataIdParser : Parser :=
160+ withAntiquot (mkAntiquot "wikidataId" wikidataIdKind) wikidataIdNoAntiquot
161+
120162end Mathlib.CrossRef
121163
122164open Mathlib.CrossRef
@@ -126,6 +168,11 @@ def Lean.TSyntax.getStacksTag (stx : TSyntax stacksTagKind) : CoreM String := do
126168 let some val := Syntax.isLit? stacksTagKind stx | throwError "Malformed Stacks tag"
127169 return val
128170
171+ /-- Extract the underlying identifier as a string from a `wikidataId` node. -/
172+ def Lean.TSyntax.getWikidataId (stx : TSyntax wikidataIdKind) : CoreM String := do
173+ let some val := Syntax.isLit? wikidataIdKind stx | throwError "Malformed Wikidata id"
174+ return val
175+
129176namespace Lean.PrettyPrinter
130177
131178namespace Formatter
@@ -134,17 +181,26 @@ namespace Formatter
134181@ [combinator_formatter stacksTagNoAntiquot] def stacksTagNoAntiquot.formatter :=
135182 visitAtom stacksTagKind
136183
184+ /-- The formatter for Wikidata identifier syntax. -/
185+ @ [combinator_formatter wikidataIdNoAntiquot] def wikidataIdNoAntiquot.formatter :=
186+ visitAtom wikidataIdKind
187+
137188end Formatter
138189
139190namespace Parenthesizer
140191
141192/-- The parenthesizer for Stacks Project Tags syntax. -/
142193@ [combinator_parenthesizer stacksTagNoAntiquot] def stacksTagAntiquot.parenthesizer := visitToken
143194
195+ /-- The parenthesizer for Wikidata identifier syntax. -/
196+ @ [combinator_parenthesizer wikidataIdNoAntiquot] def wikidataIdAntiquot.parenthesizer := visitToken
197+
144198end Lean.PrettyPrinter.Parenthesizer
145199
146200namespace Mathlib.CrossRef
147201
202+ /-! ### Stacks / Kerodon attribute -/
203+
148204/-- The syntax category for the database name. -/
149205declare_syntax_cat stacksTagDB
150206
@@ -177,14 +233,36 @@ initialize Lean.registerBuiltinAttribute {
177233 applicationTime := .beforeElaboration
178234}
179235
236+ /-! ### Wikidata attribute -/
237+
238+ /-- The `wikidata` attribute.
239+ Use it as `@[wikidata Q12345 "Optional comment"]` to associate a Mathlib declaration with
240+ the corresponding [ Wikidata ] (https://www.wikidata.org) item.
241+
242+ The identifier must be the letter `Q` followed by one or more digits.
243+ -/
244+ syntax (name := wikidataTag) "wikidata" wikidataIdParser (ppSpace str)? : attr
245+
246+ initialize Lean.registerBuiltinAttribute {
247+ name := `wikidataTag
248+ descr := "Apply a Wikidata identifier to a declaration."
249+ add := fun decl stx _attrKind => do
250+ let (id, comment) := ← match stx with
251+ | `(attr| wikidata $id $[$comment]?) => return (id, comment)
252+ | _ => throwUnsupportedSyntax
253+ addCrossRefDoc .wikidata decl (← id.getWikidataId) ((comment.map (·.getString)).getD "" )
254+ -- docstrings are immutable once an asynchronous elaboration task has been started
255+ applicationTime := .beforeElaboration
256+ }
257+
180258end Mathlib.CrossRef
181259
182- /-- Return the array of `Tag`s in the environment, sorted alphabetically by tag. -/
260+ /-- Returns the array of `Tag`s in the environment, sorted alphabetically by tag. -/
183261private def Lean.Environment.getSortedCrossRefs (env : Environment) : Array Tag :=
184262 let tags := PersistentEnvExtension.getState tagExt env
185263 tags.2 .flatten.appendList tags.1 |>.qsort (·.tag < ·.tag)
186264
187- /-- Return the declaration names of results carrying the cross-reference `tag`. -/
265+ /-- Returns the declaration names of results carrying the cross-reference `tag`. -/
188266private def Lean.Environment.getCrossRefDeclNames (env : Environment) (tag : String) :
189267 Array Name :=
190268 env.getSortedCrossRefs.filterMap fun d => if d.tag == tag then some d.declName else none
@@ -236,4 +314,16 @@ or declaration type (for definitions, structures, instances, etc.) after each su
236314elab (name := kerodonTags) "#kerodon_tags" tk:("!" )? : command =>
237315 traceCrossRefs .kerodon (tk.isSome)
238316
317+ /-- The `#wikidata_tags` command retrieves all declarations that have the `wikidata` attribute.
318+
319+ For each found declaration, it prints a line
320+ ```
321+ 'declaration_name' corresponds to tag 'declaration_tag'.
322+ ```
323+ The variant `#wikidata_tags!` also adds the theorem statement (for theorems)
324+ or declaration type (for definitions, structures, instances, etc.) after each summary line.
325+ -/
326+ elab (name := wikidataTags) "#wikidata_tags" tk:("!" )? : command =>
327+ traceCrossRefs .wikidata (tk.isSome)
328+
239329end Mathlib.CrossRef
0 commit comments