1"""
2 pygments.lexers.int_fiction
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Lexers for interactive fiction languages.
6
7 :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9"""
10
11import re
12
13from pygments.lexer import RegexLexer, include, bygroups, using, \
14 this, default, words
15from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
16 Number, Punctuation, Error, Generic
17
18__all__ = ['Inform6Lexer', 'Inform6TemplateLexer', 'Inform7Lexer',
19 'Tads3Lexer']
20
21
22class Inform6Lexer(RegexLexer):
23 """
24 For Inform 6 source code.
25 """
26
27 name = 'Inform 6'
28 url = 'http://inform-fiction.org/'
29 aliases = ['inform6', 'i6']
30 filenames = ['*.inf']
31 version_added = '2.0'
32
33 flags = re.MULTILINE | re.DOTALL
34
35 _name = r'[a-zA-Z_]\w*'
36
37 # Inform 7 maps these four character classes to their ASCII
38 # equivalents. To support Inform 6 inclusions within Inform 7,
39 # Inform6Lexer maps them too.
40 _dash = '\\-\u2010-\u2014'
41 _dquote = '"\u201c\u201d'
42 _squote = "'\u2018\u2019"
43 _newline = '\\n\u0085\u2028\u2029'
44
45 tokens = {
46 'root': [
47 (rf'\A(!%[^{_newline}]*[{_newline}])+', Comment.Preproc,
48 'directive'),
49 default('directive')
50 ],
51 '_whitespace': [
52 (r'\s+', Text),
53 (rf'![^{_newline}]*', Comment.Single)
54 ],
55 'default': [
56 include('_whitespace'),
57 (r'\[', Punctuation, 'many-values'), # Array initialization
58 (r':|(?=;)', Punctuation, '#pop'),
59 (r'<', Punctuation), # Second angle bracket in an action statement
60 default(('expression', '_expression'))
61 ],
62
63 # Expressions
64 '_expression': [
65 include('_whitespace'),
66 (r'(?=sp\b)', Text, '#pop'),
67 (rf'(?=[{_dquote}{_squote}$0-9#a-zA-Z_])', Text,
68 ('#pop', 'value')),
69 (rf'\+\+|[{_dash}]{{1,2}}(?!>)|~~?', Operator),
70 (rf'(?=[()\[{_dash},?@{{:;])', Text, '#pop')
71 ],
72 'expression': [
73 include('_whitespace'),
74 (r'\(', Punctuation, ('expression', '_expression')),
75 (r'\)', Punctuation, '#pop'),
76 (r'\[', Punctuation, ('#pop', 'statements', 'locals')),
77 (rf'>(?=(\s+|(![^{_newline}]*))*[>;])', Punctuation),
78 (rf'\+\+|[{_dash}]{{2}}(?!>)', Operator),
79 (r',', Punctuation, '_expression'),
80 (rf'&&?|\|\|?|[=~><]?=|[{_dash}]{{1,2}}>?|\.\.?[&#]?|::|[<>+*/%]',
81 Operator, '_expression'),
82 (r'(has|hasnt|in|notin|ofclass|or|provides)\b', Operator.Word,
83 '_expression'),
84 (r'sp\b', Name),
85 (r'\?~?', Name.Label, 'label?'),
86 (r'[@{]', Error),
87 default('#pop')
88 ],
89 '_assembly-expression': [
90 (r'\(', Punctuation, ('#push', '_expression')),
91 (r'[\[\]]', Punctuation),
92 (rf'[{_dash}]>', Punctuation, '_expression'),
93 (r'sp\b', Keyword.Pseudo),
94 (r';', Punctuation, '#pop:3'),
95 include('expression')
96 ],
97 '_for-expression': [
98 (r'\)', Punctuation, '#pop:2'),
99 (r':', Punctuation, '#pop'),
100 include('expression')
101 ],
102 '_keyword-expression': [
103 (r'(from|near|to)\b', Keyword, '_expression'),
104 include('expression')
105 ],
106 '_list-expression': [
107 (r',', Punctuation, '#pop'),
108 include('expression')
109 ],
110 '_object-expression': [
111 (r'has\b', Keyword.Declaration, '#pop'),
112 include('_list-expression')
113 ],
114
115 # Values
116 'value': [
117 include('_whitespace'),
118 # Strings
119 (rf'[{_squote}][^@][{_squote}]', String.Char, '#pop'),
120 (rf'([{_squote}])(@\{{[0-9a-fA-F]*\}})([{_squote}])',
121 bygroups(String.Char, String.Escape, String.Char), '#pop'),
122 (rf'([{_squote}])(@.{{2}})([{_squote}])',
123 bygroups(String.Char, String.Escape, String.Char), '#pop'),
124 (rf'[{_squote}]', String.Single, ('#pop', 'dictionary-word')),
125 (rf'[{_dquote}]', String.Double, ('#pop', 'string')),
126 # Numbers
127 (rf'\$[<>]?[+{_dash}][0-9]*\.?[0-9]*([eE][+{_dash}]?[0-9]+)?',
128 Number.Float, '#pop'),
129 (r'\$[0-9a-fA-F]+', Number.Hex, '#pop'),
130 (r'\$\$[01]+', Number.Bin, '#pop'),
131 (r'[0-9]+', Number.Integer, '#pop'),
132 # Values prefixed by hashes
133 (rf'(##|#a\$)({_name})', bygroups(Operator, Name), '#pop'),
134 (rf'(#g\$)({_name})',
135 bygroups(Operator, Name.Variable.Global), '#pop'),
136 (r'#[nw]\$', Operator, ('#pop', 'obsolete-dictionary-word')),
137 (rf'(#r\$)({_name})', bygroups(Operator, Name.Function), '#pop'),
138 (r'#', Name.Builtin, ('#pop', 'system-constant')),
139 # System functions
140 (words((
141 'child', 'children', 'elder', 'eldest', 'glk', 'indirect', 'metaclass',
142 'parent', 'random', 'sibling', 'younger', 'youngest'), suffix=r'\b'),
143 Name.Builtin, '#pop'),
144 # Metaclasses
145 (r'(?i)(Class|Object|Routine|String)\b', Name.Builtin, '#pop'),
146 # Veneer routines
147 (words((
148 'Box__Routine', 'CA__Pr', 'CDefArt', 'CInDefArt', 'Cl__Ms',
149 'Copy__Primitive', 'CP__Tab', 'DA__Pr', 'DB__Pr', 'DefArt', 'Dynam__String',
150 'EnglishNumber', 'Glk__Wrap', 'IA__Pr', 'IB__Pr', 'InDefArt', 'Main__',
151 'Meta__class', 'OB__Move', 'OB__Remove', 'OC__Cl', 'OP__Pr', 'Print__Addr',
152 'Print__PName', 'PrintShortName', 'RA__Pr', 'RA__Sc', 'RL__Pr', 'R_Process',
153 'RT__ChG', 'RT__ChGt', 'RT__ChLDB', 'RT__ChLDW', 'RT__ChPR', 'RT__ChPrintA',
154 'RT__ChPrintC', 'RT__ChPrintO', 'RT__ChPrintS', 'RT__ChPS', 'RT__ChR',
155 'RT__ChSTB', 'RT__ChSTW', 'RT__ChT', 'RT__Err', 'RT__TrPS', 'RV__Pr',
156 'Symb__Tab', 'Unsigned__Compare', 'WV__Pr', 'Z__Region'),
157 prefix='(?i)', suffix=r'\b'),
158 Name.Builtin, '#pop'),
159 # Other built-in symbols
160 (words((
161 'call', 'copy', 'create', 'DEBUG', 'destroy', 'DICT_CHAR_SIZE',
162 'DICT_ENTRY_BYTES', 'DICT_IS_UNICODE', 'DICT_WORD_SIZE', 'DOUBLE_HI_INFINITY',
163 'DOUBLE_HI_NAN', 'DOUBLE_HI_NINFINITY', 'DOUBLE_LO_INFINITY', 'DOUBLE_LO_NAN',
164 'DOUBLE_LO_NINFINITY', 'false', 'FLOAT_INFINITY', 'FLOAT_NAN', 'FLOAT_NINFINITY',
165 'GOBJFIELD_CHAIN', 'GOBJFIELD_CHILD', 'GOBJFIELD_NAME', 'GOBJFIELD_PARENT',
166 'GOBJFIELD_PROPTAB', 'GOBJFIELD_SIBLING', 'GOBJ_EXT_START',
167 'GOBJ_TOTAL_LENGTH', 'Grammar__Version', 'INDIV_PROP_START', 'INFIX',
168 'infix__watching', 'MODULE_MODE', 'name', 'nothing', 'NUM_ATTR_BYTES', 'print',
169 'print_to_array', 'recreate', 'remaining', 'self', 'sender', 'STRICT_MODE',
170 'sw__var', 'sys__glob0', 'sys__glob1', 'sys__glob2', 'sys_statusline_flag',
171 'TARGET_GLULX', 'TARGET_ZCODE', 'temp__global2', 'temp__global3',
172 'temp__global4', 'temp_global', 'true', 'USE_MODULES', 'WORDSIZE'),
173 prefix='(?i)', suffix=r'\b'),
174 Name.Builtin, '#pop'),
175 # Other values
176 (_name, Name, '#pop')
177 ],
178 'value?': [
179 include('value'),
180 default('#pop')
181 ],
182 # Strings
183 'dictionary-word': [
184 (rf'[~^]+|//[^{_squote}]*', String.Escape),
185 (rf'[^~^/\\@({{{_squote}]+', String.Single),
186 (r'[/({]', String.Single),
187 (r'@\{[0-9a-fA-F]*\}', String.Escape),
188 (r'@.{2}', String.Escape),
189 (rf'[{_squote}]', String.Single, '#pop')
190 ],
191 'string': [
192 (r'[~^]+', String.Escape),
193 (rf'[^~^\\@({{{_dquote}]+', String.Double),
194 (r'[({]', String.Double),
195 (r'\\', String.Escape),
196 (rf'@(\\\s*[{_newline}]\s*)*@((\\\s*[{_newline}]\s*)*[0-9])*', String.Escape),
197 (rf'@(\\\s*[{_newline}]\s*)*[({{]((\\\s*[{_newline}]\s*)*[0-9a-zA-Z_])*'
198 rf'(\\\s*[{_newline}]\s*)*[)}}]',
199 String.Escape),
200 (rf'@(\\\s*[{_newline}]\s*)*.(\\\s*[{_newline}]\s*)*.',
201 String.Escape),
202 (rf'[{_dquote}]', String.Double, '#pop')
203 ],
204 'plain-string': [
205 (rf'[^~^\\({{\[\]{_dquote}]+', String.Double),
206 (r'[~^({\[\]]', String.Double),
207 (r'\\', String.Escape),
208 (rf'[{_dquote}]', String.Double, '#pop')
209 ],
210 # Names
211 '_constant': [
212 include('_whitespace'),
213 (_name, Name.Constant, '#pop'),
214 include('value')
215 ],
216 'constant*': [
217 include('_whitespace'),
218 (r',', Punctuation),
219 (r'=', Punctuation, 'value?'),
220 (_name, Name.Constant, 'value?'),
221 default('#pop')
222 ],
223 '_global': [
224 include('_whitespace'),
225 (_name, Name.Variable.Global, '#pop'),
226 include('value')
227 ],
228 'label?': [
229 include('_whitespace'),
230 (_name, Name.Label, '#pop'),
231 default('#pop')
232 ],
233 'variable?': [
234 include('_whitespace'),
235 (_name, Name.Variable, '#pop'),
236 default('#pop')
237 ],
238 # Values after hashes
239 'obsolete-dictionary-word': [
240 (r'\S\w*', String.Other, '#pop')
241 ],
242 'system-constant': [
243 include('_whitespace'),
244 (_name, Name.Builtin, '#pop')
245 ],
246
247 # Directives
248 'directive': [
249 include('_whitespace'),
250 (r'#', Punctuation),
251 (r';', Punctuation, '#pop'),
252 (r'\[', Punctuation,
253 ('default', 'statements', 'locals', 'routine-name?')),
254 (words((
255 'abbreviate', 'endif', 'dictionary', 'ifdef', 'iffalse', 'ifndef', 'ifnot',
256 'iftrue', 'ifv3', 'ifv5', 'release', 'serial', 'switches', 'system_file',
257 'version'), prefix='(?i)', suffix=r'\b'),
258 Keyword, 'default'),
259 (r'(?i)(array|global)\b', Keyword,
260 ('default', 'directive-keyword?', '_global')),
261 (r'(?i)attribute\b', Keyword, ('default', 'alias?', '_constant')),
262 (r'(?i)class\b', Keyword,
263 ('object-body', 'duplicates', 'class-name')),
264 (r'(?i)(constant|default)\b', Keyword,
265 ('default', 'constant*')),
266 (r'(?i)(end\b)(.*)', bygroups(Keyword, Text)),
267 (r'(?i)(extend|verb)\b', Keyword, 'grammar'),
268 (r'(?i)fake_action\b', Keyword, ('default', '_constant')),
269 (r'(?i)import\b', Keyword, 'manifest'),
270 (r'(?i)(include|link|origsource)\b', Keyword,
271 ('default', 'before-plain-string?')),
272 (r'(?i)(lowstring|undef)\b', Keyword, ('default', '_constant')),
273 (r'(?i)message\b', Keyword, ('default', 'diagnostic')),
274 (r'(?i)(nearby|object)\b', Keyword,
275 ('object-body', '_object-head')),
276 (r'(?i)property\b', Keyword,
277 ('default', 'alias?', '_constant', 'property-keyword*')),
278 (r'(?i)replace\b', Keyword,
279 ('default', 'routine-name?', 'routine-name?')),
280 (r'(?i)statusline\b', Keyword, ('default', 'directive-keyword?')),
281 (r'(?i)stub\b', Keyword, ('default', 'routine-name?')),
282 (r'(?i)trace\b', Keyword,
283 ('default', 'trace-keyword?', 'trace-keyword?')),
284 (r'(?i)zcharacter\b', Keyword,
285 ('default', 'directive-keyword?', 'directive-keyword?')),
286 (_name, Name.Class, ('object-body', '_object-head'))
287 ],
288 # [, Replace, Stub
289 'routine-name?': [
290 include('_whitespace'),
291 (_name, Name.Function, '#pop'),
292 default('#pop')
293 ],
294 'locals': [
295 include('_whitespace'),
296 (r';', Punctuation, '#pop'),
297 (r'\*', Punctuation),
298 (r'"', String.Double, 'plain-string'),
299 (_name, Name.Variable)
300 ],
301 # Array
302 'many-values': [
303 include('_whitespace'),
304 (r';', Punctuation),
305 (r'\]', Punctuation, '#pop'),
306 (r':', Error),
307 default(('expression', '_expression'))
308 ],
309 # Attribute, Property
310 'alias?': [
311 include('_whitespace'),
312 (r'alias\b', Keyword, ('#pop', '_constant')),
313 default('#pop')
314 ],
315 # Class, Object, Nearby
316 'class-name': [
317 include('_whitespace'),
318 (r'(?=[,;]|(class|has|private|with)\b)', Text, '#pop'),
319 (_name, Name.Class, '#pop')
320 ],
321 'duplicates': [
322 include('_whitespace'),
323 (r'\(', Punctuation, ('#pop', 'expression', '_expression')),
324 default('#pop')
325 ],
326 '_object-head': [
327 (rf'[{_dash}]>', Punctuation),
328 (r'(class|has|private|with)\b', Keyword.Declaration, '#pop'),
329 include('_global')
330 ],
331 'object-body': [
332 include('_whitespace'),
333 (r';', Punctuation, '#pop:2'),
334 (r',', Punctuation),
335 (r'class\b', Keyword.Declaration, 'class-segment'),
336 (r'(has|private|with)\b', Keyword.Declaration),
337 (r':', Error),
338 default(('_object-expression', '_expression'))
339 ],
340 'class-segment': [
341 include('_whitespace'),
342 (r'(?=[,;]|(class|has|private|with)\b)', Text, '#pop'),
343 (_name, Name.Class),
344 default('value')
345 ],
346 # Extend, Verb
347 'grammar': [
348 include('_whitespace'),
349 (r'=', Punctuation, ('#pop', 'default')),
350 (r'\*', Punctuation, ('#pop', 'grammar-line')),
351 default('_directive-keyword')
352 ],
353 'grammar-line': [
354 include('_whitespace'),
355 (r';', Punctuation, '#pop'),
356 (r'[/*]', Punctuation),
357 (rf'[{_dash}]>', Punctuation, 'value'),
358 (r'(noun|scope)\b', Keyword, '=routine'),
359 default('_directive-keyword')
360 ],
361 '=routine': [
362 include('_whitespace'),
363 (r'=', Punctuation, 'routine-name?'),
364 default('#pop')
365 ],
366 # Import
367 'manifest': [
368 include('_whitespace'),
369 (r';', Punctuation, '#pop'),
370 (r',', Punctuation),
371 (r'(?i)global\b', Keyword, '_global'),
372 default('_global')
373 ],
374 # Include, Link, Message
375 'diagnostic': [
376 include('_whitespace'),
377 (rf'[{_dquote}]', String.Double, ('#pop', 'message-string')),
378 default(('#pop', 'before-plain-string?', 'directive-keyword?'))
379 ],
380 'before-plain-string?': [
381 include('_whitespace'),
382 (rf'[{_dquote}]', String.Double, ('#pop', 'plain-string')),
383 default('#pop')
384 ],
385 'message-string': [
386 (r'[~^]+', String.Escape),
387 include('plain-string')
388 ],
389
390 # Keywords used in directives
391 '_directive-keyword!': [
392 include('_whitespace'),
393 (words((
394 'additive', 'alias', 'buffer', 'class', 'creature', 'data', 'error', 'fatalerror',
395 'first', 'has', 'held', 'individual', 'initial', 'initstr', 'last', 'long', 'meta',
396 'multi', 'multiexcept', 'multiheld', 'multiinside', 'noun', 'number', 'only',
397 'private', 'replace', 'reverse', 'scope', 'score', 'special', 'string', 'table',
398 'terminating', 'time', 'topic', 'warning', 'with'), suffix=r'\b'),
399 Keyword, '#pop'),
400 (r'static\b', Keyword),
401 (rf'[{_dash}]{{1,2}}>|[+=]', Punctuation, '#pop')
402 ],
403 '_directive-keyword': [
404 include('_directive-keyword!'),
405 include('value')
406 ],
407 'directive-keyword?': [
408 include('_directive-keyword!'),
409 default('#pop')
410 ],
411 'property-keyword*': [
412 include('_whitespace'),
413 (words(('additive', 'individual', 'long'),
414 suffix=rf'\b(?=(\s*|(![^{_newline}]*[{_newline}]))*[_a-zA-Z])'),
415 Keyword),
416 default('#pop')
417 ],
418 'trace-keyword?': [
419 include('_whitespace'),
420 (words((
421 'assembly', 'dictionary', 'expressions', 'lines', 'linker',
422 'objects', 'off', 'on', 'symbols', 'tokens', 'verbs'), suffix=r'\b'),
423 Keyword, '#pop'),
424 default('#pop')
425 ],
426
427 # Statements
428 'statements': [
429 include('_whitespace'),
430 (r'\]', Punctuation, '#pop'),
431 (r'[;{}]', Punctuation),
432 (words((
433 'box', 'break', 'continue', 'default', 'give', 'inversion',
434 'new_line', 'quit', 'read', 'remove', 'return', 'rfalse', 'rtrue',
435 'spaces', 'string', 'until'), suffix=r'\b'),
436 Keyword, 'default'),
437 (r'(do|else)\b', Keyword),
438 (r'(font|style)\b', Keyword,
439 ('default', 'miscellaneous-keyword?')),
440 (r'for\b', Keyword, ('for', '(?')),
441 (r'(if|switch|while)', Keyword,
442 ('expression', '_expression', '(?')),
443 (r'(jump|save|restore)\b', Keyword, ('default', 'label?')),
444 (r'objectloop\b', Keyword,
445 ('_keyword-expression', 'variable?', '(?')),
446 (rf'print(_ret)?\b|(?=[{_dquote}])', Keyword, 'print-list'),
447 (r'\.', Name.Label, 'label?'),
448 (r'@', Keyword, 'opcode'),
449 (r'#(?![agrnw]\$|#)', Punctuation, 'directive'),
450 (r'<', Punctuation, 'default'),
451 (r'move\b', Keyword,
452 ('default', '_keyword-expression', '_expression')),
453 default(('default', '_keyword-expression', '_expression'))
454 ],
455 'miscellaneous-keyword?': [
456 include('_whitespace'),
457 (r'(bold|fixed|from|near|off|on|reverse|roman|to|underline)\b',
458 Keyword, '#pop'),
459 (r'(a|A|an|address|char|name|number|object|property|string|the|'
460 rf'The)\b(?=(\s+|(![^{_newline}]*))*\))', Keyword.Pseudo,
461 '#pop'),
462 (rf'{_name}(?=(\s+|(![^{_newline}]*))*\))', Name.Function,
463 '#pop'),
464 default('#pop')
465 ],
466 '(?': [
467 include('_whitespace'),
468 (r'\(', Punctuation, '#pop'),
469 default('#pop')
470 ],
471 'for': [
472 include('_whitespace'),
473 (r';', Punctuation, ('_for-expression', '_expression')),
474 default(('_for-expression', '_expression'))
475 ],
476 'print-list': [
477 include('_whitespace'),
478 (r';', Punctuation, '#pop'),
479 (r':', Error),
480 default(('_list-expression', '_expression', '_list-expression', 'form'))
481 ],
482 'form': [
483 include('_whitespace'),
484 (r'\(', Punctuation, ('#pop', 'miscellaneous-keyword?')),
485 default('#pop')
486 ],
487
488 # Assembly
489 'opcode': [
490 include('_whitespace'),
491 (rf'[{_dquote}]', String.Double, ('operands', 'plain-string')),
492 (rf'[{_dash}]{{1,2}}>', Punctuation, 'operands'),
493 (_name, Keyword, 'operands')
494 ],
495 'operands': [
496 (r':', Error),
497 default(('_assembly-expression', '_expression'))
498 ]
499 }
500
501 def get_tokens_unprocessed(self, text):
502 # 'in' is either a keyword or an operator.
503 # If the token two tokens after 'in' is ')', 'in' is a keyword:
504 # objectloop(a in b)
505 # Otherwise, it is an operator:
506 # objectloop(a in b && true)
507 objectloop_queue = []
508 objectloop_token_count = -1
509 previous_token = None
510 for index, token, value in RegexLexer.get_tokens_unprocessed(self,
511 text):
512 if previous_token is Name.Variable and value == 'in':
513 objectloop_queue = [[index, token, value]]
514 objectloop_token_count = 2
515 elif objectloop_token_count > 0:
516 if token not in Comment and token not in Text:
517 objectloop_token_count -= 1
518 objectloop_queue.append((index, token, value))
519 else:
520 if objectloop_token_count == 0:
521 if objectloop_queue[-1][2] == ')':
522 objectloop_queue[0][1] = Keyword
523 while objectloop_queue:
524 yield objectloop_queue.pop(0)
525 objectloop_token_count = -1
526 yield index, token, value
527 if token not in Comment and token not in Text:
528 previous_token = token
529 while objectloop_queue:
530 yield objectloop_queue.pop(0)
531
532 def analyse_text(text):
533 """We try to find a keyword which seem relatively common, unfortunately
534 there is a decent overlap with Smalltalk keywords otherwise here.."""
535 result = 0
536 if re.search('\borigsource\b', text, re.IGNORECASE):
537 result += 0.05
538
539 return result
540
541
542class Inform7Lexer(RegexLexer):
543 """
544 For Inform 7 source code.
545 """
546
547 name = 'Inform 7'
548 url = 'http://inform7.com/'
549 aliases = ['inform7', 'i7']
550 filenames = ['*.ni', '*.i7x']
551 version_added = '2.0'
552
553 flags = re.MULTILINE | re.DOTALL
554
555 _dash = Inform6Lexer._dash
556 _dquote = Inform6Lexer._dquote
557 _newline = Inform6Lexer._newline
558 _start = rf'\A|(?<=[{_newline}])'
559
560 # There are three variants of Inform 7, differing in how to
561 # interpret at signs and braces in I6T. In top-level inclusions, at
562 # signs in the first column are inweb syntax. In phrase definitions
563 # and use options, tokens in braces are treated as I7. Use options
564 # also interpret "{N}".
565 tokens = {}
566 token_variants = ['+i6t-not-inline', '+i6t-inline', '+i6t-use-option']
567
568 for level in token_variants:
569 tokens[level] = {
570 '+i6-root': list(Inform6Lexer.tokens['root']),
571 '+i6t-root': [ # For Inform6TemplateLexer
572 (rf'[^{Inform6Lexer._newline}]*', Comment.Preproc,
573 ('directive', '+p'))
574 ],
575 'root': [
576 (r'(\|?\s)+', Text),
577 (r'\[', Comment.Multiline, '+comment'),
578 (rf'[{_dquote}]', Generic.Heading,
579 ('+main', '+titling', '+titling-string')),
580 default(('+main', '+heading?'))
581 ],
582 '+titling-string': [
583 (rf'[^{_dquote}]+', Generic.Heading),
584 (rf'[{_dquote}]', Generic.Heading, '#pop')
585 ],
586 '+titling': [
587 (r'\[', Comment.Multiline, '+comment'),
588 (rf'[^{_dquote}.;:|{_newline}]+', Generic.Heading),
589 (rf'[{_dquote}]', Generic.Heading, '+titling-string'),
590 (rf'[{_newline}]{{2}}|(?<=[\s{_dquote}])\|[\s{_dquote}]',
591 Text, ('#pop', '+heading?')),
592 (rf'[.;:]|(?<=[\s{_dquote}])\|', Text, '#pop'),
593 (rf'[|{_newline}]', Generic.Heading)
594 ],
595 '+main': [
596 (rf'(?i)[^{_dquote}:a\[(|{_newline}]+', Text),
597 (rf'[{_dquote}]', String.Double, '+text'),
598 (r':', Text, '+phrase-definition'),
599 (r'(?i)\bas\b', Text, '+use-option'),
600 (r'\[', Comment.Multiline, '+comment'),
601 (rf'(\([{_dash}])(.*?)([{_dash}]\))',
602 bygroups(Punctuation,
603 using(this, state=('+i6-root', 'directive'),
604 i6t='+i6t-not-inline'), Punctuation)),
605 (rf'({_start}|(?<=[\s;:.{_dquote}]))\|\s|[{_newline}]{{2,}}', Text, '+heading?'),
606 (rf'(?i)[a(|{_newline}]', Text)
607 ],
608 '+phrase-definition': [
609 (r'\s+', Text),
610 (r'\[', Comment.Multiline, '+comment'),
611 (rf'(\([{_dash}])(.*?)([{_dash}]\))',
612 bygroups(Punctuation,
613 using(this, state=('+i6-root', 'directive',
614 'default', 'statements'),
615 i6t='+i6t-inline'), Punctuation), '#pop'),
616 default('#pop')
617 ],
618 '+use-option': [
619 (r'\s+', Text),
620 (r'\[', Comment.Multiline, '+comment'),
621 (rf'(\([{_dash}])(.*?)([{_dash}]\))',
622 bygroups(Punctuation,
623 using(this, state=('+i6-root', 'directive'),
624 i6t='+i6t-use-option'), Punctuation), '#pop'),
625 default('#pop')
626 ],
627 '+comment': [
628 (r'[^\[\]]+', Comment.Multiline),
629 (r'\[', Comment.Multiline, '#push'),
630 (r'\]', Comment.Multiline, '#pop')
631 ],
632 '+text': [
633 (rf'[^\[{_dquote}]+', String.Double),
634 (r'\[.*?\]', String.Interpol),
635 (rf'[{_dquote}]', String.Double, '#pop')
636 ],
637 '+heading?': [
638 (r'(\|?\s)+', Text),
639 (r'\[', Comment.Multiline, '+comment'),
640 (rf'[{_dash}]{{4}}\s+', Text, '+documentation-heading'),
641 (rf'[{_dash}]{{1,3}}', Text),
642 (rf'(?i)(volume|book|part|chapter|section)\b[^{_newline}]*',
643 Generic.Heading, '#pop'),
644 default('#pop')
645 ],
646 '+documentation-heading': [
647 (r'\s+', Text),
648 (r'\[', Comment.Multiline, '+comment'),
649 (r'(?i)documentation\s+', Text, '+documentation-heading2'),
650 default('#pop')
651 ],
652 '+documentation-heading2': [
653 (r'\s+', Text),
654 (r'\[', Comment.Multiline, '+comment'),
655 (rf'[{_dash}]{{4}}\s', Text, '+documentation'),
656 default('#pop:2')
657 ],
658 '+documentation': [
659 (rf'(?i)({_start})\s*(chapter|example)\s*:[^{_newline}]*', Generic.Heading),
660 (rf'(?i)({_start})\s*section\s*:[^{_newline}]*',
661 Generic.Subheading),
662 (rf'(({_start})\t.*?[{_newline}])+',
663 using(this, state='+main')),
664 (rf'[^{_newline}\[]+|[{_newline}\[]', Text),
665 (r'\[', Comment.Multiline, '+comment'),
666 ],
667 '+i6t-not-inline': [
668 (rf'({_start})@c( .*?)?([{_newline}]|\Z)',
669 Comment.Preproc),
670 (rf'({_start})@([{_dash}]+|Purpose:)[^{_newline}]*',
671 Comment.Preproc),
672 (rf'({_start})@p( .*?)?([{_newline}]|\Z)',
673 Generic.Heading, '+p')
674 ],
675 '+i6t-use-option': [
676 include('+i6t-not-inline'),
677 (r'(\{)(N)(\})', bygroups(Punctuation, Text, Punctuation))
678 ],
679 '+i6t-inline': [
680 (r'(\{)(\S[^}]*)?(\})',
681 bygroups(Punctuation, using(this, state='+main'),
682 Punctuation))
683 ],
684 '+i6t': [
685 (rf'(\{{[{_dash}])(![^}}]*)(\}}?)',
686 bygroups(Punctuation, Comment.Single, Punctuation)),
687 (rf'(\{{[{_dash}])(lines)(:)([^}}]*)(\}}?)',
688 bygroups(Punctuation, Keyword, Punctuation, Text,
689 Punctuation), '+lines'),
690 (rf'(\{{[{_dash}])([^:}}]*)(:?)([^}}]*)(\}}?)',
691 bygroups(Punctuation, Keyword, Punctuation, Text,
692 Punctuation)),
693 (r'(\(\+)(.*?)(\+\)|\Z)',
694 bygroups(Punctuation, using(this, state='+main'),
695 Punctuation))
696 ],
697 '+p': [
698 (r'[^@]+', Comment.Preproc),
699 (rf'({_start})@c( .*?)?([{_newline}]|\Z)',
700 Comment.Preproc, '#pop'),
701 (rf'({_start})@([{_dash}]|Purpose:)', Comment.Preproc),
702 (rf'({_start})@p( .*?)?([{_newline}]|\Z)',
703 Generic.Heading),
704 (r'@', Comment.Preproc)
705 ],
706 '+lines': [
707 (rf'({_start})@c( .*?)?([{_newline}]|\Z)',
708 Comment.Preproc),
709 (rf'({_start})@([{_dash}]|Purpose:)[^{_newline}]*',
710 Comment.Preproc),
711 (rf'({_start})@p( .*?)?([{_newline}]|\Z)',
712 Generic.Heading, '+p'),
713 (rf'({_start})@\w*[ {_newline}]', Keyword),
714 (rf'![^{_newline}]*', Comment.Single),
715 (rf'(\{{)([{_dash}]endlines)(\}})',
716 bygroups(Punctuation, Keyword, Punctuation), '#pop'),
717 (rf'[^@!{{]+?([{_newline}]|\Z)|.', Text)
718 ]
719 }
720 # Inform 7 can include snippets of Inform 6 template language,
721 # so all of Inform6Lexer's states are copied here, with
722 # modifications to account for template syntax. Inform7Lexer's
723 # own states begin with '+' to avoid name conflicts. Some of
724 # Inform6Lexer's states begin with '_': these are not modified.
725 # They deal with template syntax either by including modified
726 # states, or by matching r'' then pushing to modified states.
727 for token in Inform6Lexer.tokens:
728 if token == 'root':
729 continue
730 tokens[level][token] = list(Inform6Lexer.tokens[token])
731 if not token.startswith('_'):
732 tokens[level][token][:0] = [include('+i6t'), include(level)]
733
734 def __init__(self, **options):
735 level = options.get('i6t', '+i6t-not-inline')
736 if level not in self._all_tokens:
737 self._tokens = self.__class__.process_tokendef(level)
738 else:
739 self._tokens = self._all_tokens[level]
740 RegexLexer.__init__(self, **options)
741
742
743class Inform6TemplateLexer(Inform7Lexer):
744 """
745 For Inform 6 template code.
746 """
747
748 name = 'Inform 6 template'
749 aliases = ['i6t']
750 filenames = ['*.i6t']
751 version_added = '2.0'
752
753 def get_tokens_unprocessed(self, text, stack=('+i6t-root',)):
754 return Inform7Lexer.get_tokens_unprocessed(self, text, stack)
755
756
757class Tads3Lexer(RegexLexer):
758 """
759 For TADS 3 source code.
760 """
761
762 name = 'TADS 3'
763 aliases = ['tads3']
764 filenames = ['*.t']
765 url = 'https://www.tads.org'
766 version_added = ''
767
768 flags = re.DOTALL | re.MULTILINE
769
770 _comment_single = r'(?://(?:[^\\\n]|\\+[\w\W])*$)'
771 _comment_multiline = r'(?:/\*(?:[^*]|\*(?!/))*\*/)'
772 _escape = (r'(?:\\(?:[\n\\<>"\'^v bnrt]|u[\da-fA-F]{,4}|x[\da-fA-F]{,2}|'
773 r'[0-3]?[0-7]{1,2}))')
774 _name = r'(?:[_a-zA-Z]\w*)'
775 _no_quote = r'(?=\s|\\?>)'
776 _operator = (r'(?:&&|\|\||\+\+|--|\?\?|::|[.,@\[\]~]|'
777 r'(?:[=+\-*/%!&|^]|<<?|>>?>?)=?)')
778 _ws = rf'(?:\\|\s|{_comment_single}|{_comment_multiline})'
779 _ws_pp = rf'(?:\\\n|[^\S\n]|{_comment_single}|{_comment_multiline})'
780
781 def _make_string_state(triple, double, verbatim=None, _escape=_escape):
782 if verbatim:
783 verbatim = ''.join([f'(?:{re.escape(c.lower())}|{re.escape(c.upper())})'
784 for c in verbatim])
785 char = r'"' if double else r"'"
786 token = String.Double if double else String.Single
787 escaped_quotes = rf'+|{char}(?!{char}{{2}})' if triple else r''
788 prefix = '{}{}'.format('t' if triple else '', 'd' if double else 's')
789 tag_state_name = f'{prefix}qt'
790 state = []
791 if triple:
792 state += [
793 (rf'{char}{{3,}}', token, '#pop'),
794 (rf'\\{char}+', String.Escape),
795 (char, token)
796 ]
797 else:
798 state.append((char, token, '#pop'))
799 state += [
800 include('s/verbatim'),
801 (rf'[^\\<&{{}}{char}]+', token)
802 ]
803 if verbatim:
804 # This regex can't use `(?i)` because escape sequences are
805 # case-sensitive. `<\XMP>` works; `<\xmp>` doesn't.
806 state.append((rf'\\?<(/|\\\\|(?!{_escape})\\){verbatim}(?=[\s=>])',
807 Name.Tag, ('#pop', f'{prefix}qs', tag_state_name)))
808 else:
809 state += [
810 (rf'\\?<!([^><\\{char}]|<(?!<)|\\{char}{escaped_quotes}|{_escape}|\\.)*>?', Comment.Multiline),
811 (r'(?i)\\?<listing(?=[\s=>]|\\>)', Name.Tag,
812 ('#pop', f'{prefix}qs/listing', tag_state_name)),
813 (r'(?i)\\?<xmp(?=[\s=>]|\\>)', Name.Tag,
814 ('#pop', f'{prefix}qs/xmp', tag_state_name)),
815 (rf'\\?<([^\s=><\\{char}]|<(?!<)|\\{char}{escaped_quotes}|{_escape}|\\.)*', Name.Tag,
816 tag_state_name),
817 include('s/entity')
818 ]
819 state += [
820 include('s/escape'),
821 (rf'\{{([^}}<\\{char}]|<(?!<)|\\{char}{escaped_quotes}|{_escape}|\\.)*\}}', String.Interpol),
822 (r'[\\&{}<]', token)
823 ]
824 return state
825
826 def _make_tag_state(triple, double, _escape=_escape):
827 char = r'"' if double else r"'"
828 quantifier = r'{3,}' if triple else r''
829 state_name = '{}{}qt'.format('t' if triple else '', 'd' if double else 's')
830 token = String.Double if double else String.Single
831 escaped_quotes = rf'+|{char}(?!{char}{{2}})' if triple else r''
832 return [
833 (rf'{char}{quantifier}', token, '#pop:2'),
834 (r'(\s|\\\n)+', Text),
835 (r'(=)(\\?")', bygroups(Punctuation, String.Double),
836 f'dqs/{state_name}'),
837 (r"(=)(\\?')", bygroups(Punctuation, String.Single),
838 f'sqs/{state_name}'),
839 (r'=', Punctuation, f'uqs/{state_name}'),
840 (r'\\?>', Name.Tag, '#pop'),
841 (rf'\{{([^}}<\\{char}]|<(?!<)|\\{char}{escaped_quotes}|{_escape}|\\.)*\}}', String.Interpol),
842 (rf'([^\s=><\\{char}]|<(?!<)|\\{char}{escaped_quotes}|{_escape}|\\.)+', Name.Attribute),
843 include('s/escape'),
844 include('s/verbatim'),
845 include('s/entity'),
846 (r'[\\{}&]', Name.Attribute)
847 ]
848
849 def _make_attribute_value_state(terminator, host_triple, host_double,
850 _escape=_escape):
851 token = (String.Double if terminator == r'"' else
852 String.Single if terminator == r"'" else String.Other)
853 host_char = r'"' if host_double else r"'"
854 host_quantifier = r'{3,}' if host_triple else r''
855 host_token = String.Double if host_double else String.Single
856 escaped_quotes = (rf'+|{host_char}(?!{host_char}{{2}})'
857 if host_triple else r'')
858 return [
859 (rf'{host_char}{host_quantifier}', host_token, '#pop:3'),
860 (r'{}{}'.format(r'' if token is String.Other else r'\\?', terminator),
861 token, '#pop'),
862 include('s/verbatim'),
863 include('s/entity'),
864 (rf'\{{([^}}<\\{host_char}]|<(?!<)|\\{host_char}{escaped_quotes}|{_escape}|\\.)*\}}', String.Interpol),
865 (r'([^\s"\'<%s{}\\&])+' % (r'>' if token is String.Other else r''),
866 token),
867 include('s/escape'),
868 (r'["\'\s&{<}\\]', token)
869 ]
870
871 tokens = {
872 'root': [
873 ('\ufeff', Text),
874 (r'\{', Punctuation, 'object-body'),
875 (r';+', Punctuation),
876 (r'(?=(argcount|break|case|catch|continue|default|definingobj|'
877 r'delegated|do|else|for|foreach|finally|goto|if|inherited|'
878 r'invokee|local|nil|new|operator|replaced|return|self|switch|'
879 r'targetobj|targetprop|throw|true|try|while)\b)', Text, 'block'),
880 (rf'({_name})({_ws}*)(\()',
881 bygroups(Name.Function, using(this, state='whitespace'),
882 Punctuation),
883 ('block?/root', 'more/parameters', 'main/parameters')),
884 include('whitespace'),
885 (r'\++', Punctuation),
886 (r'[^\s!"%-(*->@-_a-z{-~]+', Error), # Averts an infinite loop
887 (r'(?!\Z)', Text, 'main/root')
888 ],
889 'main/root': [
890 include('main/basic'),
891 default(('#pop', 'object-body/no-braces', 'classes', 'class'))
892 ],
893 'object-body/no-braces': [
894 (r';', Punctuation, '#pop'),
895 (r'\{', Punctuation, ('#pop', 'object-body')),
896 include('object-body')
897 ],
898 'object-body': [
899 (r';', Punctuation),
900 (r'\{', Punctuation, '#push'),
901 (r'\}', Punctuation, '#pop'),
902 (r':', Punctuation, ('classes', 'class')),
903 (rf'({_name}?)({_ws}*)(\()',
904 bygroups(Name.Function, using(this, state='whitespace'),
905 Punctuation),
906 ('block?', 'more/parameters', 'main/parameters')),
907 (rf'({_name})({_ws}*)(\{{)',
908 bygroups(Name.Function, using(this, state='whitespace'),
909 Punctuation), 'block'),
910 (rf'({_name})({_ws}*)(:)',
911 bygroups(Name.Variable, using(this, state='whitespace'),
912 Punctuation),
913 ('object-body/no-braces', 'classes', 'class')),
914 include('whitespace'),
915 (rf'->|{_operator}', Punctuation, 'main'),
916 default('main/object-body')
917 ],
918 'main/object-body': [
919 include('main/basic'),
920 (rf'({_name})({_ws}*)(=?)',
921 bygroups(Name.Variable, using(this, state='whitespace'),
922 Punctuation), ('#pop', 'more', 'main')),
923 default('#pop:2')
924 ],
925 'block?/root': [
926 (r'\{', Punctuation, ('#pop', 'block')),
927 include('whitespace'),
928 (r'(?=[\[\'"<(:])', Text, # It might be a VerbRule macro.
929 ('#pop', 'object-body/no-braces', 'grammar', 'grammar-rules')),
930 # It might be a macro like DefineAction.
931 default(('#pop', 'object-body/no-braces'))
932 ],
933 'block?': [
934 (r'\{', Punctuation, ('#pop', 'block')),
935 include('whitespace'),
936 default('#pop')
937 ],
938 'block/basic': [
939 (r'[;:]+', Punctuation),
940 (r'\{', Punctuation, '#push'),
941 (r'\}', Punctuation, '#pop'),
942 (r'default\b', Keyword.Reserved),
943 (rf'({_name})({_ws}*)(:)',
944 bygroups(Name.Label, using(this, state='whitespace'),
945 Punctuation)),
946 include('whitespace')
947 ],
948 'block': [
949 include('block/basic'),
950 (r'(?!\Z)', Text, ('more', 'main'))
951 ],
952 'block/embed': [
953 (r'>>', String.Interpol, '#pop'),
954 include('block/basic'),
955 (r'(?!\Z)', Text, ('more/embed', 'main'))
956 ],
957 'main/basic': [
958 include('whitespace'),
959 (r'\(', Punctuation, ('#pop', 'more', 'main')),
960 (r'\[', Punctuation, ('#pop', 'more/list', 'main')),
961 (r'\{', Punctuation, ('#pop', 'more/inner', 'main/inner',
962 'more/parameters', 'main/parameters')),
963 (r'\*|\.{3}', Punctuation, '#pop'),
964 (r'(?i)0x[\da-f]+', Number.Hex, '#pop'),
965 (r'(\d+\.(?!\.)\d*|\.\d+)([eE][-+]?\d+)?|\d+[eE][-+]?\d+',
966 Number.Float, '#pop'),
967 (r'0[0-7]+', Number.Oct, '#pop'),
968 (r'\d+', Number.Integer, '#pop'),
969 (r'"""', String.Double, ('#pop', 'tdqs')),
970 (r"'''", String.Single, ('#pop', 'tsqs')),
971 (r'"', String.Double, ('#pop', 'dqs')),
972 (r"'", String.Single, ('#pop', 'sqs')),
973 (r'R"""', String.Regex, ('#pop', 'tdqr')),
974 (r"R'''", String.Regex, ('#pop', 'tsqr')),
975 (r'R"', String.Regex, ('#pop', 'dqr')),
976 (r"R'", String.Regex, ('#pop', 'sqr')),
977 # Two-token keywords
978 (rf'(extern)({_ws}+)(object\b)',
979 bygroups(Keyword.Reserved, using(this, state='whitespace'),
980 Keyword.Reserved)),
981 (rf'(function|method)({_ws}*)(\()',
982 bygroups(Keyword.Reserved, using(this, state='whitespace'),
983 Punctuation),
984 ('#pop', 'block?', 'more/parameters', 'main/parameters')),
985 (rf'(modify)({_ws}+)(grammar\b)',
986 bygroups(Keyword.Reserved, using(this, state='whitespace'),
987 Keyword.Reserved),
988 ('#pop', 'object-body/no-braces', ':', 'grammar')),
989 (rf'(new)({_ws}+(?=(?:function|method)\b))',
990 bygroups(Keyword.Reserved, using(this, state='whitespace'))),
991 (rf'(object)({_ws}+)(template\b)',
992 bygroups(Keyword.Reserved, using(this, state='whitespace'),
993 Keyword.Reserved), ('#pop', 'template')),
994 (rf'(string)({_ws}+)(template\b)',
995 bygroups(Keyword, using(this, state='whitespace'),
996 Keyword.Reserved), ('#pop', 'function-name')),
997 # Keywords
998 (r'(argcount|definingobj|invokee|replaced|targetobj|targetprop)\b',
999 Name.Builtin, '#pop'),
1000 (r'(break|continue|goto)\b', Keyword.Reserved, ('#pop', 'label')),
1001 (r'(case|extern|if|intrinsic|return|static|while)\b',
1002 Keyword.Reserved),
1003 (r'catch\b', Keyword.Reserved, ('#pop', 'catch')),
1004 (r'class\b', Keyword.Reserved,
1005 ('#pop', 'object-body/no-braces', 'class')),
1006 (r'(default|do|else|finally|try)\b', Keyword.Reserved, '#pop'),
1007 (r'(dictionary|property)\b', Keyword.Reserved,
1008 ('#pop', 'constants')),
1009 (r'enum\b', Keyword.Reserved, ('#pop', 'enum')),
1010 (r'export\b', Keyword.Reserved, ('#pop', 'main')),
1011 (r'(for|foreach)\b', Keyword.Reserved,
1012 ('#pop', 'more/inner', 'main/inner')),
1013 (r'(function|method)\b', Keyword.Reserved,
1014 ('#pop', 'block?', 'function-name')),
1015 (r'grammar\b', Keyword.Reserved,
1016 ('#pop', 'object-body/no-braces', 'grammar')),
1017 (r'inherited\b', Keyword.Reserved, ('#pop', 'inherited')),
1018 (r'local\b', Keyword.Reserved,
1019 ('#pop', 'more/local', 'main/local')),
1020 (r'(modify|replace|switch|throw|transient)\b', Keyword.Reserved,
1021 '#pop'),
1022 (r'new\b', Keyword.Reserved, ('#pop', 'class')),
1023 (r'(nil|true)\b', Keyword.Constant, '#pop'),
1024 (r'object\b', Keyword.Reserved, ('#pop', 'object-body/no-braces')),
1025 (r'operator\b', Keyword.Reserved, ('#pop', 'operator')),
1026 (r'propertyset\b', Keyword.Reserved,
1027 ('#pop', 'propertyset', 'main')),
1028 (r'self\b', Name.Builtin.Pseudo, '#pop'),
1029 (r'template\b', Keyword.Reserved, ('#pop', 'template')),
1030 # Operators
1031 (rf'(__objref|defined)({_ws}*)(\()',
1032 bygroups(Operator.Word, using(this, state='whitespace'),
1033 Operator), ('#pop', 'more/__objref', 'main')),
1034 (r'delegated\b', Operator.Word),
1035 # Compiler-defined macros and built-in properties
1036 (r'(__DATE__|__DEBUG|__LINE__|__FILE__|'
1037 r'__TADS_MACRO_FORMAT_VERSION|__TADS_SYS_\w*|__TADS_SYSTEM_NAME|'
1038 r'__TADS_VERSION_MAJOR|__TADS_VERSION_MINOR|__TADS3|__TIME__|'
1039 r'construct|finalize|grammarInfo|grammarTag|lexicalParent|'
1040 r'miscVocab|sourceTextGroup|sourceTextGroupName|'
1041 r'sourceTextGroupOrder|sourceTextOrder)\b', Name.Builtin, '#pop')
1042 ],
1043 'main': [
1044 include('main/basic'),
1045 (_name, Name, '#pop'),
1046 default('#pop')
1047 ],
1048 'more/basic': [
1049 (r'\(', Punctuation, ('more/list', 'main')),
1050 (r'\[', Punctuation, ('more', 'main')),
1051 (r'\.{3}', Punctuation),
1052 (r'->|\.\.', Punctuation, 'main'),
1053 (r'(?=;)|[:)\]]', Punctuation, '#pop'),
1054 include('whitespace'),
1055 (_operator, Operator, 'main'),
1056 (r'\?', Operator, ('main', 'more/conditional', 'main')),
1057 (rf'(is|not)({_ws}+)(in\b)',
1058 bygroups(Operator.Word, using(this, state='whitespace'),
1059 Operator.Word)),
1060 (r'[^\s!"%-_a-z{-~]+', Error) # Averts an infinite loop
1061 ],
1062 'more': [
1063 include('more/basic'),
1064 default('#pop')
1065 ],
1066 # Then expression (conditional operator)
1067 'more/conditional': [
1068 (r':(?!:)', Operator, '#pop'),
1069 include('more')
1070 ],
1071 # Embedded expressions
1072 'more/embed': [
1073 (r'>>', String.Interpol, '#pop:2'),
1074 include('more')
1075 ],
1076 # For/foreach loop initializer or short-form anonymous function
1077 'main/inner': [
1078 (r'\(', Punctuation, ('#pop', 'more/inner', 'main/inner')),
1079 (r'local\b', Keyword.Reserved, ('#pop', 'main/local')),
1080 include('main')
1081 ],
1082 'more/inner': [
1083 (r'\}', Punctuation, '#pop'),
1084 (r',', Punctuation, 'main/inner'),
1085 (r'(in|step)\b', Keyword, 'main/inner'),
1086 include('more')
1087 ],
1088 # Local
1089 'main/local': [
1090 (_name, Name.Variable, '#pop'),
1091 include('whitespace')
1092 ],
1093 'more/local': [
1094 (r',', Punctuation, 'main/local'),
1095 include('more')
1096 ],
1097 # List
1098 'more/list': [
1099 (r'[,:]', Punctuation, 'main'),
1100 include('more')
1101 ],
1102 # Parameter list
1103 'main/parameters': [
1104 (rf'({_name})({_ws}*)(?=:)',
1105 bygroups(Name.Variable, using(this, state='whitespace')), '#pop'),
1106 (rf'({_name})({_ws}+)({_name})',
1107 bygroups(Name.Class, using(this, state='whitespace'),
1108 Name.Variable), '#pop'),
1109 (r'\[+', Punctuation),
1110 include('main/basic'),
1111 (_name, Name.Variable, '#pop'),
1112 default('#pop')
1113 ],
1114 'more/parameters': [
1115 (rf'(:)({_ws}*(?=[?=,:)]))',
1116 bygroups(Punctuation, using(this, state='whitespace'))),
1117 (r'[?\]]+', Punctuation),
1118 (r'[:)]', Punctuation, ('#pop', 'multimethod?')),
1119 (r',', Punctuation, 'main/parameters'),
1120 (r'=', Punctuation, ('more/parameter', 'main')),
1121 include('more')
1122 ],
1123 'more/parameter': [
1124 (r'(?=[,)])', Text, '#pop'),
1125 include('more')
1126 ],
1127 'multimethod?': [
1128 (r'multimethod\b', Keyword, '#pop'),
1129 include('whitespace'),
1130 default('#pop')
1131 ],
1132
1133 # Statements and expressions
1134 'more/__objref': [
1135 (r',', Punctuation, 'mode'),
1136 (r'\)', Operator, '#pop'),
1137 include('more')
1138 ],
1139 'mode': [
1140 (r'(error|warn)\b', Keyword, '#pop'),
1141 include('whitespace')
1142 ],
1143 'catch': [
1144 (r'\(+', Punctuation),
1145 (_name, Name.Exception, ('#pop', 'variables')),
1146 include('whitespace')
1147 ],
1148 'enum': [
1149 include('whitespace'),
1150 (r'token\b', Keyword, ('#pop', 'constants')),
1151 default(('#pop', 'constants'))
1152 ],
1153 'grammar': [
1154 (r'\)+', Punctuation),
1155 (r'\(', Punctuation, 'grammar-tag'),
1156 (r':', Punctuation, 'grammar-rules'),
1157 (_name, Name.Class),
1158 include('whitespace')
1159 ],
1160 'grammar-tag': [
1161 include('whitespace'),
1162 (r'"""([^\\"<]|""?(?!")|\\"+|\\.|<(?!<))+("{3,}|<<)|'
1163 r'R"""([^\\"]|""?(?!")|\\"+|\\.)+"{3,}|'
1164 r"'''([^\\'<]|''?(?!')|\\'+|\\.|<(?!<))+('{3,}|<<)|"
1165 r"R'''([^\\']|''?(?!')|\\'+|\\.)+'{3,}|"
1166 r'"([^\\"<]|\\.|<(?!<))+("|<<)|R"([^\\"]|\\.)+"|'
1167 r"'([^\\'<]|\\.|<(?!<))+('|<<)|R'([^\\']|\\.)+'|"
1168 r"([^)\s\\/]|/(?![/*]))+|\)", String.Other, '#pop')
1169 ],
1170 'grammar-rules': [
1171 include('string'),
1172 include('whitespace'),
1173 (rf'(\[)({_ws}*)(badness)',
1174 bygroups(Punctuation, using(this, state='whitespace'), Keyword),
1175 'main'),
1176 (rf'->|{_operator}|[()]', Punctuation),
1177 (_name, Name.Constant),
1178 default('#pop:2')
1179 ],
1180 ':': [
1181 (r':', Punctuation, '#pop')
1182 ],
1183 'function-name': [
1184 (r'(<<([^>]|>>>|>(?!>))*>>)+', String.Interpol),
1185 (rf'(?={_name}?{_ws}*[({{])', Text, '#pop'),
1186 (_name, Name.Function, '#pop'),
1187 include('whitespace')
1188 ],
1189 'inherited': [
1190 (r'<', Punctuation, ('#pop', 'classes', 'class')),
1191 include('whitespace'),
1192 (_name, Name.Class, '#pop'),
1193 default('#pop')
1194 ],
1195 'operator': [
1196 (r'negate\b', Operator.Word, '#pop'),
1197 include('whitespace'),
1198 (_operator, Operator),
1199 default('#pop')
1200 ],
1201 'propertyset': [
1202 (r'\(', Punctuation, ('more/parameters', 'main/parameters')),
1203 (r'\{', Punctuation, ('#pop', 'object-body')),
1204 include('whitespace')
1205 ],
1206 'template': [
1207 (r'(?=;)', Text, '#pop'),
1208 include('string'),
1209 (r'inherited\b', Keyword.Reserved),
1210 include('whitespace'),
1211 (rf'->|\?|{_operator}', Punctuation),
1212 (_name, Name.Variable)
1213 ],
1214
1215 # Identifiers
1216 'class': [
1217 (r'\*|\.{3}', Punctuation, '#pop'),
1218 (r'object\b', Keyword.Reserved, '#pop'),
1219 (r'transient\b', Keyword.Reserved),
1220 (_name, Name.Class, '#pop'),
1221 include('whitespace'),
1222 default('#pop')
1223 ],
1224 'classes': [
1225 (r'[:,]', Punctuation, 'class'),
1226 include('whitespace'),
1227 (r'>', Punctuation, '#pop'),
1228 default('#pop')
1229 ],
1230 'constants': [
1231 (r',+', Punctuation),
1232 (r';', Punctuation, '#pop'),
1233 (r'property\b', Keyword.Reserved),
1234 (_name, Name.Constant),
1235 include('whitespace')
1236 ],
1237 'label': [
1238 (_name, Name.Label, '#pop'),
1239 include('whitespace'),
1240 default('#pop')
1241 ],
1242 'variables': [
1243 (r',+', Punctuation),
1244 (r'\)', Punctuation, '#pop'),
1245 include('whitespace'),
1246 (_name, Name.Variable)
1247 ],
1248
1249 # Whitespace and comments
1250 'whitespace': [
1251 (rf'^{_ws_pp}*#({_comment_multiline}|[^\n]|(?<=\\)\n)*\n?',
1252 Comment.Preproc),
1253 (_comment_single, Comment.Single),
1254 (_comment_multiline, Comment.Multiline),
1255 (rf'\\+\n+{_ws_pp}*#?|\n+|([^\S\n]|\\)+', Text)
1256 ],
1257
1258 # Strings
1259 'string': [
1260 (r'"""', String.Double, 'tdqs'),
1261 (r"'''", String.Single, 'tsqs'),
1262 (r'"', String.Double, 'dqs'),
1263 (r"'", String.Single, 'sqs')
1264 ],
1265 's/escape': [
1266 (rf'\{{\{{|\}}\}}|{_escape}', String.Escape)
1267 ],
1268 's/verbatim': [
1269 (r'<<\s*(as\s+decreasingly\s+likely\s+outcomes|cycling|else|end|'
1270 r'first\s+time|one\s+of|only|or|otherwise|'
1271 r'(sticky|(then\s+)?(purely\s+)?at)\s+random|stopping|'
1272 r'(then\s+)?(half\s+)?shuffled|\|\|)\s*>>', String.Interpol),
1273 (rf'<<(%(_({_escape}|\\?.)|[\-+ ,#]|\[\d*\]?)*\d*\.?\d*({_escape}|\\?.)|'
1274 r'\s*((else|otherwise)\s+)?(if|unless)\b)?',
1275 String.Interpol, ('block/embed', 'more/embed', 'main'))
1276 ],
1277 's/entity': [
1278 (r'(?i)&(#(x[\da-f]+|\d+)|[a-z][\da-z]*);?', Name.Entity)
1279 ],
1280 'tdqs': _make_string_state(True, True),
1281 'tsqs': _make_string_state(True, False),
1282 'dqs': _make_string_state(False, True),
1283 'sqs': _make_string_state(False, False),
1284 'tdqs/listing': _make_string_state(True, True, 'listing'),
1285 'tsqs/listing': _make_string_state(True, False, 'listing'),
1286 'dqs/listing': _make_string_state(False, True, 'listing'),
1287 'sqs/listing': _make_string_state(False, False, 'listing'),
1288 'tdqs/xmp': _make_string_state(True, True, 'xmp'),
1289 'tsqs/xmp': _make_string_state(True, False, 'xmp'),
1290 'dqs/xmp': _make_string_state(False, True, 'xmp'),
1291 'sqs/xmp': _make_string_state(False, False, 'xmp'),
1292
1293 # Tags
1294 'tdqt': _make_tag_state(True, True),
1295 'tsqt': _make_tag_state(True, False),
1296 'dqt': _make_tag_state(False, True),
1297 'sqt': _make_tag_state(False, False),
1298 'dqs/tdqt': _make_attribute_value_state(r'"', True, True),
1299 'dqs/tsqt': _make_attribute_value_state(r'"', True, False),
1300 'dqs/dqt': _make_attribute_value_state(r'"', False, True),
1301 'dqs/sqt': _make_attribute_value_state(r'"', False, False),
1302 'sqs/tdqt': _make_attribute_value_state(r"'", True, True),
1303 'sqs/tsqt': _make_attribute_value_state(r"'", True, False),
1304 'sqs/dqt': _make_attribute_value_state(r"'", False, True),
1305 'sqs/sqt': _make_attribute_value_state(r"'", False, False),
1306 'uqs/tdqt': _make_attribute_value_state(_no_quote, True, True),
1307 'uqs/tsqt': _make_attribute_value_state(_no_quote, True, False),
1308 'uqs/dqt': _make_attribute_value_state(_no_quote, False, True),
1309 'uqs/sqt': _make_attribute_value_state(_no_quote, False, False),
1310
1311 # Regular expressions
1312 'tdqr': [
1313 (r'[^\\"]+', String.Regex),
1314 (r'\\"*', String.Regex),
1315 (r'"{3,}', String.Regex, '#pop'),
1316 (r'"', String.Regex)
1317 ],
1318 'tsqr': [
1319 (r"[^\\']+", String.Regex),
1320 (r"\\'*", String.Regex),
1321 (r"'{3,}", String.Regex, '#pop'),
1322 (r"'", String.Regex)
1323 ],
1324 'dqr': [
1325 (r'[^\\"]+', String.Regex),
1326 (r'\\"?', String.Regex),
1327 (r'"', String.Regex, '#pop')
1328 ],
1329 'sqr': [
1330 (r"[^\\']+", String.Regex),
1331 (r"\\'?", String.Regex),
1332 (r"'", String.Regex, '#pop')
1333 ]
1334 }
1335
1336 def get_tokens_unprocessed(self, text, **kwargs):
1337 pp = rf'^{self._ws_pp}*#{self._ws_pp}*'
1338 if_false_level = 0
1339 for index, token, value in (
1340 RegexLexer.get_tokens_unprocessed(self, text, **kwargs)):
1341 if if_false_level == 0: # Not in a false #if
1342 if (token is Comment.Preproc and
1343 re.match(rf'{pp}if{self._ws_pp}+(0|nil){self._ws_pp}*$\n?', value)):
1344 if_false_level = 1
1345 else: # In a false #if
1346 if token is Comment.Preproc:
1347 if (if_false_level == 1 and
1348 re.match(rf'{pp}el(if|se)\b', value)):
1349 if_false_level = 0
1350 elif re.match(rf'{pp}if', value):
1351 if_false_level += 1
1352 elif re.match(rf'{pp}endif\b', value):
1353 if_false_level -= 1
1354 else:
1355 token = Comment
1356 yield index, token, value
1357
1358 def analyse_text(text):
1359 """This is a rather generic descriptive language without strong
1360 identifiers. It looks like a 'GameMainDef' has to be present,
1361 and/or a 'versionInfo' with an 'IFID' field."""
1362 result = 0
1363 if '__TADS' in text or 'GameMainDef' in text:
1364 result += 0.2
1365
1366 # This is a fairly unique keyword which is likely used in source as well
1367 if 'versionInfo' in text and 'IFID' in text:
1368 result += 0.1
1369
1370 return result