1"""
2 pygments.lexers.minecraft
3 ~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Lexers for Minecraft related languages.
6
7 SNBT. A data communication format used in Minecraft.
8 wiki: https://minecraft.wiki/w/NBT_format
9
10 MCFunction. The Function file for Minecraft Data packs and Add-ons.
11 official: https://learn.microsoft.com/en-us/minecraft/creator/documents/functionsintroduction
12 wiki: https://minecraft.wiki/w/Function
13
14 MCSchema. A kind of data Schema for Minecraft Add-on Development.
15 official: https://learn.microsoft.com/en-us/minecraft/creator/reference/content/schemasreference/
16 community example: https://www.mcbe-dev.net/addons/data-driven/manifest.html
17
18 :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
19 :license: BSD, see LICENSE for details.
20"""
21
22from pygments.lexer import RegexLexer, default, include, bygroups
23from pygments.token import Comment, Keyword, Literal, Name, Number, Operator, \
24 Punctuation, String, Text, Whitespace
25
26__all__ = ['SNBTLexer', 'MCFunctionLexer', 'MCSchemaLexer']
27
28
29class SNBTLexer(RegexLexer):
30 """Lexer for stringified NBT, a data format used in Minecraft
31 """
32
33 name = "SNBT"
34 url = "https://minecraft.wiki/w/NBT_format"
35 aliases = ["snbt"]
36 filenames = ["*.snbt"]
37 mimetypes = ["text/snbt"]
38 version_added = '2.12'
39
40 tokens = {
41 "root": [
42 # We only look for the open bracket here since square bracket
43 # is only valid in NBT pathing (which is a mcfunction idea).
44 (r"\{", Punctuation, "compound"),
45 (r"[^\{]+", Text),
46 ],
47
48 "whitespace": [
49 (r"\s+", Whitespace),
50 ],
51
52 "operators": [
53 (r"[,:;]", Punctuation),
54 ],
55
56 "literals": [
57 (r"(true|false)", Keyword.Constant),
58 (r"-?\d+[eE]-?\d+", Number.Float),
59 (r"-?\d*\.\d+[fFdD]?", Number.Float),
60 (r"-?\d+[bBsSlLfFdD]?", Number.Integer),
61
62 # Separate states for both types of strings so they don't entangle
63 (r'"', String.Double, "literals.string_double"),
64 (r"'", String.Single, "literals.string_single"),
65 ],
66 "literals.string_double": [
67 (r"\\.", String.Escape),
68 (r'[^\\"\n]+', String.Double),
69 (r'"', String.Double, "#pop"),
70 ],
71 "literals.string_single": [
72 (r"\\.", String.Escape),
73 (r"[^\\'\n]+", String.Single),
74 (r"'", String.Single, "#pop"),
75 ],
76
77 "compound": [
78 # this handles the unquoted snbt keys
79 # note: stringified keys still work
80 (r"[A-Z_a-z]+", Name.Attribute),
81 include("operators"),
82 include("whitespace"),
83 include("literals"),
84 (r"\{", Punctuation, "#push"),
85 (r"\[", Punctuation, "list"),
86 (r"\}", Punctuation, "#pop"),
87 ],
88
89 "list": [
90 (r"[A-Z_a-z]+", Name.Attribute),
91 include("literals"),
92 include("operators"),
93 include("whitespace"),
94 (r"\[", Punctuation, "#push"),
95 (r"\{", Punctuation, "compound"),
96 (r"\]", Punctuation, "#pop"),
97 ],
98 }
99
100
101class MCFunctionLexer(RegexLexer):
102 """Lexer for the mcfunction scripting language used in Minecraft.
103
104 Modelled somewhat after the `GitHub mcfunction grammar <https://github.com/Arcensoth/language-mcfunction>`_.
105 """
106
107 name = "MCFunction"
108 url = "https://minecraft.wiki/w/Commands"
109 aliases = ["mcfunction", "mcf"]
110 filenames = ["*.mcfunction"]
111 mimetypes = ["text/mcfunction"]
112 version_added = '2.12'
113
114 # Used to denotate the start of a block comment, borrowed from Github's mcfunction
115 _block_comment_prefix = "[>!]"
116
117 tokens = {
118 "root": [
119 include("names"),
120 include("comments"),
121 include("literals"),
122 include("whitespace"),
123 include("property"),
124 include("operators"),
125 include("selectors"),
126 ],
127
128 "names": [
129 # The start of a command (either beginning of line OR after the run keyword)
130 # We don't encode a list of keywords since mods, plugins, or even pre-processors
131 # may add new commands, so we have a 'close-enough' regex which catches them.
132 (r"^(\s*)([a-z_]+)", bygroups(Whitespace, Name.Builtin)),
133 (r"(?<=run)\s+[a-z_]+", Name.Builtin),
134
135 # UUID
136 (r"\b[0-9a-fA-F]+(?:-[0-9a-fA-F]+){4}\b", Name.Variable),
137 include("resource-name"),
138 # normal command names and scoreboards
139 # there's no way to know the differences unfortuntely
140 (r"[A-Za-z_][\w.#%$]+", Keyword.Constant),
141 (r"[#%$][\w.#%$]+", Name.Variable.Magic),
142 ],
143
144 "resource-name": [
145 # resource names have to be lowercase
146 (r"#?[a-z_][a-z_.-]*:[a-z0-9_./-]+", Name.Function),
147 # similar to above except optional `:``
148 # a `/` must be present "somewhere"
149 (r"#?[a-z0-9_\.\-]+\/[a-z0-9_\.\-\/]+", Name.Function),
150 ],
151
152 "whitespace": [
153 (r"\s+", Whitespace),
154 ],
155
156 "comments": [
157 (rf"^\s*(#{_block_comment_prefix})", Comment.Multiline,
158 ("comments.block", "comments.block.emphasized")),
159 (r"#.*$", Comment.Single),
160 ],
161 "comments.block": [
162 (rf"^\s*#{_block_comment_prefix}", Comment.Multiline,
163 "comments.block.emphasized"),
164 (r"^\s*#", Comment.Multiline, "comments.block.normal"),
165 default("#pop"),
166 ],
167 "comments.block.normal": [
168 include("comments.block.special"),
169 (r"\S+", Comment.Multiline),
170 (r"\n", Text, "#pop"),
171 include("whitespace"),
172 ],
173 "comments.block.emphasized": [
174 include("comments.block.special"),
175 (r"\S+", String.Doc),
176 (r"\n", Text, "#pop"),
177 include("whitespace"),
178 ],
179 "comments.block.special": [
180 # Params
181 (r"@\S+", Name.Decorator),
182
183 include("resource-name"),
184
185 # Scoreboard player names
186 (r"[#%$][\w.#%$]+", Name.Variable.Magic),
187 ],
188
189 "operators": [
190 (r"[\-~%^?!+*<>\\/|&=.]", Operator),
191 ],
192
193 "literals": [
194 (r"\.\.", Literal),
195 (r"(true|false)", Keyword.Pseudo),
196
197 # these are like unquoted strings and appear in many places
198 (r"[A-Za-z_]+", Name.Variable.Class),
199
200 (r"[0-7]b", Number.Byte),
201 (r"[+-]?\d*\.?\d+([eE]?[+-]?\d+)?[df]?\b", Number.Float),
202 (r"[+-]?\d+\b", Number.Integer),
203 (r'"', String.Double, "literals.string-double"),
204 (r"'", String.Single, "literals.string-single"),
205 ],
206 "literals.string-double": [
207 (r"\\.", String.Escape),
208 (r'[^\\"\n]+', String.Double),
209 (r'"', String.Double, "#pop"),
210 ],
211 "literals.string-single": [
212 (r"\\.", String.Escape),
213 (r"[^\\'\n]+", String.Single),
214 (r"'", String.Single, "#pop"),
215 ],
216
217 "selectors": [
218 (r"@[a-z]", Name.Variable),
219 ],
220
221
222 ## Generic Property Container
223 # There are several, differing instances where the language accepts
224 # specific contained keys or contained key, value pairings.
225 #
226 # Property Maps:
227 # - Starts with either `[` or `{`
228 # - Key separated by `:` or `=`
229 # - Deliminated by `,`
230 #
231 # Property Lists:
232 # - Starts with `[`
233 # - Deliminated by `,`
234 #
235 # For simplicity, these patterns match a generic, nestable structure
236 # which follow a key, value pattern. For normal lists, there's only keys.
237 # This allow some "illegal" structures, but we'll accept those for
238 # sake of simplicity
239 #
240 # Examples:
241 # - `[facing=up, powered=true]` (blockstate)
242 # - `[name="hello world", nbt={key: 1b}]` (selector + nbt)
243 # - `[{"text": "value"}, "literal"]` (json)
244 ##
245 "property": [
246 # This state gets included in root and also several substates
247 # We do this to shortcut the starting of new properties
248 # within other properties. Lists can have sublists and compounds
249 # and values can start a new property (see the `difficult_1.txt`
250 # snippet).
251 (r"\{", Punctuation, ("property.curly", "property.key")),
252 (r"\[", Punctuation, ("property.square", "property.key")),
253 ],
254 "property.curly": [
255 include("whitespace"),
256 include("property"),
257 (r"\}", Punctuation, "#pop"),
258 ],
259 "property.square": [
260 include("whitespace"),
261 include("property"),
262 (r"\]", Punctuation, "#pop"),
263
264 # lists can have sequences of items
265 (r",", Punctuation),
266 ],
267 "property.key": [
268 include("whitespace"),
269
270 # resource names (for advancements)
271 # can omit `:` to default `minecraft:`
272 # must check if there is a future equals sign if `:` is in the name
273 (r"#?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+(?=\s*\=)", Name.Attribute, "property.delimiter"),
274 (r"#?[a-z_][a-z0-9_\.\-/]+", Name.Attribute, "property.delimiter"),
275
276 # unquoted NBT key
277 (r"[A-Za-z_\-\+]+", Name.Attribute, "property.delimiter"),
278
279 # quoted JSON or NBT key
280 (r'"', Name.Attribute, "property.delimiter", "literals.string-double"),
281 (r"'", Name.Attribute, "property.delimiter", "literals.string-single"),
282
283 # index for a list
284 (r"-?\d+", Number.Integer, "property.delimiter"),
285
286 default("#pop"),
287 ],
288 "property.key.string-double": [
289 (r"\\.", String.Escape),
290 (r'[^\\"\n]+', Name.Attribute),
291 (r'"', Name.Attribute, "#pop"),
292 ],
293 "property.key.string-single": [
294 (r"\\.", String.Escape),
295 (r"[^\\'\n]+", Name.Attribute),
296 (r"'", Name.Attribute, "#pop"),
297 ],
298 "property.delimiter": [
299 include("whitespace"),
300
301 (r"[:=]!?", Punctuation, "property.value"),
302 (r",", Punctuation),
303
304 default("#pop"),
305 ],
306 "property.value": [
307 include("whitespace"),
308
309 # unquoted resource names are valid literals here
310 (r"#?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+", Name.Tag),
311 (r"#?[a-z_][a-z0-9_\.\-/]+", Name.Tag),
312
313 include("literals"),
314 include("property"),
315
316 default("#pop"),
317 ],
318 }
319
320
321class MCSchemaLexer(RegexLexer):
322 """Lexer for Minecraft Add-ons data Schemas, an interface structure standard used in Minecraft
323 """
324
325 name = 'MCSchema'
326 url = 'https://learn.microsoft.com/en-us/minecraft/creator/reference/content/schemasreference/'
327 aliases = ['mcschema']
328 filenames = ['*.mcschema']
329 mimetypes = ['text/mcschema']
330 version_added = '2.14'
331
332 tokens = {
333 'commentsandwhitespace': [
334 (r'\s+', Whitespace),
335 (r'//.*?$', Comment.Single),
336 (r'/\*.*?\*/', Comment.Multiline)
337 ],
338 'slashstartsregex': [
339 include('commentsandwhitespace'),
340 (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/'
341 r'([gimuysd]+\b|\B)', String.Regex, '#pop'),
342 (r'(?=/)', Text, ('#pop', 'badregex')),
343 default('#pop')
344 ],
345 'badregex': [
346 (r'\n', Whitespace, '#pop')
347 ],
348 'singlestring': [
349 (r'\\.', String.Escape),
350 (r"'", String.Single, '#pop'),
351 (r"[^\\']+", String.Single),
352 ],
353 'doublestring': [
354 (r'\\.', String.Escape),
355 (r'"', String.Double, '#pop'),
356 (r'[^\\"]+', String.Double),
357 ],
358 'root': [
359 (r'^(?=\s|/|<!--)', Text, 'slashstartsregex'),
360 include('commentsandwhitespace'),
361
362 # keywords for optional word and field types
363 (r'(?<=: )opt', Operator.Word),
364 (r'(?<=\s)[\w-]*(?=(\s+"|\n))', Keyword.Declaration),
365
366 # numeric literals
367 (r'0[bB][01]+', Number.Bin),
368 (r'0[oO]?[0-7]+', Number.Oct),
369 (r'0[xX][0-9a-fA-F]+', Number.Hex),
370 (r'\d+', Number.Integer),
371 (r'(\.\d+|\d+\.\d*|\d+)([eE][-+]?\d+)?', Number.Float),
372
373 # possible punctuations
374 (r'\.\.\.|=>', Punctuation),
375 (r'\+\+|--|~|\?\?=?|\?|:|\\(?=\n)|'
376 r'(<<|>>>?|==?|!=?|(?:\*\*|\|\||&&|[-<>+*%&|^/]))=?', Operator, 'slashstartsregex'),
377 (r'[{(\[;,]', Punctuation, 'slashstartsregex'),
378 (r'[})\].]', Punctuation),
379
380 # strings
381 (r"'", String.Single, 'singlestring'),
382 (r'"', String.Double, 'doublestring'),
383
384 # title line
385 (r'[\w-]*?(?=:\{?\n)', String.Symbol),
386 # title line with a version code, formatted
387 # `major.minor.patch-prerelease+buildmeta`
388 (r'([\w-]*?)(:)(\d+)(?:(\.)(\d+)(?:(\.)(\d+)(?:(\-)((?:[^\W_]|-)*(?:\.(?:[^\W_]|-)*)*))?(?:(\+)((?:[^\W_]|-)+(?:\.(?:[^\W_]|-)+)*))?)?)?(?=:\{?\n)', bygroups(String.Symbol, Operator, Number.Integer, Operator, Number.Integer, Operator, Number.Integer, Operator, String, Operator, String)),
389
390 (r'.*\n', Text),
391 ]
392 }