1"""
2 pygments.lexers.basic
3 ~~~~~~~~~~~~~~~~~~~~~
4
5 Lexers for BASIC like languages (other than VB.net).
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, bygroups, default, words, include
14from pygments.token import Comment, Error, Keyword, Name, Number, \
15 Punctuation, Operator, String, Text, Whitespace
16from pygments.lexers import _vbscript_builtins
17
18
19__all__ = ['BlitzBasicLexer', 'BlitzMaxLexer', 'MonkeyLexer', 'CbmBasicV2Lexer',
20 'QBasicLexer', 'VBScriptLexer', 'BBCBasicLexer']
21
22
23class BlitzMaxLexer(RegexLexer):
24 """
25 For BlitzMax source code.
26 """
27
28 name = 'BlitzMax'
29 url = 'http://blitzbasic.com'
30 aliases = ['blitzmax', 'bmax']
31 filenames = ['*.bmx']
32 mimetypes = ['text/x-bmx']
33 version_added = '1.4'
34
35 bmax_vopwords = r'\b(Shl|Shr|Sar|Mod)\b'
36 bmax_sktypes = r'@{1,2}|[!#$%]'
37 bmax_lktypes = r'\b(Int|Byte|Short|Float|Double|Long)\b'
38 bmax_name = r'[a-z_]\w*'
39 bmax_var = (rf'({bmax_name})(?:(?:([ \t]*)({bmax_sktypes})|([ \t]*:[ \t]*\b(?:Shl|Shr|Sar|Mod)\b)'
40 rf'|([ \t]*)(:)([ \t]*)(?:{bmax_lktypes}|({bmax_name})))(?:([ \t]*)(Ptr))?)')
41 bmax_func = bmax_var + r'?((?:[ \t]|\.\.\n)*)([(])'
42
43 flags = re.MULTILINE | re.IGNORECASE
44 tokens = {
45 'root': [
46 # Text
47 (r'\s+', Whitespace),
48 (r'(\.\.)(\n)', bygroups(Text, Whitespace)), # Line continuation
49 # Comments
50 (r"'.*?\n", Comment.Single),
51 (r'([ \t]*)\bRem\n(\n|.)*?\s*\bEnd([ \t]*)Rem', Comment.Multiline),
52 # Data types
53 ('"', String.Double, 'string'),
54 # Numbers
55 (r'[0-9]+\.[0-9]*(?!\.)', Number.Float),
56 (r'\.[0-9]*(?!\.)', Number.Float),
57 (r'[0-9]+', Number.Integer),
58 (r'\$[0-9a-f]+', Number.Hex),
59 (r'\%[10]+', Number.Bin),
60 # Other
61 (rf'(?:(?:(:)?([ \t]*)(:?{bmax_vopwords}|([+\-*/&|~]))|Or|And|Not|[=<>^]))', Operator),
62 (r'[(),.:\[\]]', Punctuation),
63 (r'(?:#[\w \t]*)', Name.Label),
64 (r'(?:\?[\w \t]*)', Comment.Preproc),
65 # Identifiers
66 (rf'\b(New)\b([ \t]?)([(]?)({bmax_name})',
67 bygroups(Keyword.Reserved, Whitespace, Punctuation, Name.Class)),
68 (rf'\b(Import|Framework|Module)([ \t]+)({bmax_name}\.{bmax_name})',
69 bygroups(Keyword.Reserved, Whitespace, Keyword.Namespace)),
70 (bmax_func, bygroups(Name.Function, Whitespace, Keyword.Type,
71 Operator, Whitespace, Punctuation, Whitespace,
72 Keyword.Type, Name.Class, Whitespace,
73 Keyword.Type, Whitespace, Punctuation)),
74 (bmax_var, bygroups(Name.Variable, Whitespace, Keyword.Type, Operator,
75 Whitespace, Punctuation, Whitespace, Keyword.Type,
76 Name.Class, Whitespace, Keyword.Type)),
77 (rf'\b(Type|Extends)([ \t]+)({bmax_name})',
78 bygroups(Keyword.Reserved, Whitespace, Name.Class)),
79 # Keywords
80 (r'\b(Ptr)\b', Keyword.Type),
81 (r'\b(Pi|True|False|Null|Self|Super)\b', Keyword.Constant),
82 (r'\b(Local|Global|Const|Field)\b', Keyword.Declaration),
83 (words((
84 'TNullMethodException', 'TNullFunctionException',
85 'TNullObjectException', 'TArrayBoundsException',
86 'TRuntimeException'), prefix=r'\b', suffix=r'\b'), Name.Exception),
87 (words((
88 'Strict', 'SuperStrict', 'Module', 'ModuleInfo',
89 'End', 'Return', 'Continue', 'Exit', 'Public', 'Private',
90 'Var', 'VarPtr', 'Chr', 'Len', 'Asc', 'SizeOf', 'Sgn', 'Abs', 'Min', 'Max',
91 'New', 'Release', 'Delete', 'Incbin', 'IncbinPtr', 'IncbinLen',
92 'Framework', 'Include', 'Import', 'Extern', 'EndExtern',
93 'Function', 'EndFunction', 'Type', 'EndType', 'Extends', 'Method', 'EndMethod',
94 'Abstract', 'Final', 'If', 'Then', 'Else', 'ElseIf', 'EndIf',
95 'For', 'To', 'Next', 'Step', 'EachIn', 'While', 'Wend', 'EndWhile',
96 'Repeat', 'Until', 'Forever', 'Select', 'Case', 'Default', 'EndSelect',
97 'Try', 'Catch', 'EndTry', 'Throw', 'Assert', 'Goto', 'DefData', 'ReadData',
98 'RestoreData'), prefix=r'\b', suffix=r'\b'),
99 Keyword.Reserved),
100 # Final resolve (for variable names and such)
101 (rf'({bmax_name})', Name.Variable),
102 ],
103 'string': [
104 (r'""', String.Double),
105 (r'"C?', String.Double, '#pop'),
106 (r'[^"]+', String.Double),
107 ],
108 }
109
110
111class BlitzBasicLexer(RegexLexer):
112 """
113 For BlitzBasic source code.
114 """
115
116 name = 'BlitzBasic'
117 url = 'http://blitzbasic.com'
118 aliases = ['blitzbasic', 'b3d', 'bplus']
119 filenames = ['*.bb', '*.decls']
120 mimetypes = ['text/x-bb']
121 version_added = '2.0'
122
123 bb_sktypes = r'@{1,2}|[#$%]'
124 bb_name = r'[a-z]\w*'
125 bb_var = (rf'({bb_name})(?:([ \t]*)({bb_sktypes})|([ \t]*)([.])([ \t]*)(?:({bb_name})))?')
126
127 flags = re.MULTILINE | re.IGNORECASE
128 tokens = {
129 'root': [
130 # Text
131 (r'\s+', Whitespace),
132 # Comments
133 (r";.*?\n", Comment.Single),
134 # Data types
135 ('"', String.Double, 'string'),
136 # Numbers
137 (r'[0-9]+\.[0-9]*(?!\.)', Number.Float),
138 (r'\.[0-9]+(?!\.)', Number.Float),
139 (r'[0-9]+', Number.Integer),
140 (r'\$[0-9a-f]+', Number.Hex),
141 (r'\%[10]+', Number.Bin),
142 # Other
143 (words(('Shl', 'Shr', 'Sar', 'Mod', 'Or', 'And', 'Not',
144 'Abs', 'Sgn', 'Handle', 'Int', 'Float', 'Str',
145 'First', 'Last', 'Before', 'After'),
146 prefix=r'\b', suffix=r'\b'),
147 Operator),
148 (r'([+\-*/~=<>^])', Operator),
149 (r'[(),:\[\]\\]', Punctuation),
150 (rf'\.([ \t]*)({bb_name})', Name.Label),
151 # Identifiers
152 (rf'\b(New)\b([ \t]+)({bb_name})',
153 bygroups(Keyword.Reserved, Whitespace, Name.Class)),
154 (rf'\b(Gosub|Goto)\b([ \t]+)({bb_name})',
155 bygroups(Keyword.Reserved, Whitespace, Name.Label)),
156 (rf'\b(Object)\b([ \t]*)([.])([ \t]*)({bb_name})\b',
157 bygroups(Operator, Whitespace, Punctuation, Whitespace, Name.Class)),
158 (rf'\b{bb_var}\b([ \t]*)(\()',
159 bygroups(Name.Function, Whitespace, Keyword.Type, Whitespace, Punctuation,
160 Whitespace, Name.Class, Whitespace, Punctuation)),
161 (rf'\b(Function)\b([ \t]+){bb_var}',
162 bygroups(Keyword.Reserved, Whitespace, Name.Function, Whitespace, Keyword.Type,
163 Whitespace, Punctuation, Whitespace, Name.Class)),
164 (rf'\b(Type)([ \t]+)({bb_name})',
165 bygroups(Keyword.Reserved, Whitespace, Name.Class)),
166 # Keywords
167 (r'\b(Pi|True|False|Null)\b', Keyword.Constant),
168 (r'\b(Local|Global|Const|Field|Dim)\b', Keyword.Declaration),
169 (words((
170 'End', 'Return', 'Exit', 'Chr', 'Len', 'Asc', 'New', 'Delete', 'Insert',
171 'Include', 'Function', 'Type', 'If', 'Then', 'Else', 'ElseIf', 'EndIf',
172 'For', 'To', 'Next', 'Step', 'Each', 'While', 'Wend',
173 'Repeat', 'Until', 'Forever', 'Select', 'Case', 'Default',
174 'Goto', 'Gosub', 'Data', 'Read', 'Restore'), prefix=r'\b', suffix=r'\b'),
175 Keyword.Reserved),
176 # Final resolve (for variable names and such)
177 # (r'(%s)' % (bb_name), Name.Variable),
178 (bb_var, bygroups(Name.Variable, Whitespace, Keyword.Type,
179 Whitespace, Punctuation, Whitespace, Name.Class)),
180 ],
181 'string': [
182 (r'""', String.Double),
183 (r'"C?', String.Double, '#pop'),
184 (r'[^"\n]+', String.Double),
185 ],
186 }
187
188
189class MonkeyLexer(RegexLexer):
190 """
191 For Monkey source code.
192 """
193
194 name = 'Monkey'
195 aliases = ['monkey']
196 filenames = ['*.monkey']
197 mimetypes = ['text/x-monkey']
198 url = 'https://blitzresearch.itch.io/monkeyx'
199 version_added = '1.6'
200
201 name_variable = r'[a-z_]\w*'
202 name_function = r'[A-Z]\w*'
203 name_constant = r'[A-Z_][A-Z0-9_]*'
204 name_class = r'[A-Z]\w*'
205 name_module = r'[a-z0-9_]*'
206
207 keyword_type = r'(?:Int|Float|String|Bool|Object|Array|Void)'
208 # ? == Bool // % == Int // # == Float // $ == String
209 keyword_type_special = r'[?%#$]'
210
211 flags = re.MULTILINE
212
213 tokens = {
214 'root': [
215 # Text
216 (r'\s+', Whitespace),
217 # Comments
218 (r"'.*", Comment),
219 (r'(?i)^#rem\b', Comment.Multiline, 'comment'),
220 # preprocessor directives
221 (r'(?i)^(?:#If|#ElseIf|#Else|#EndIf|#End|#Print|#Error)\b', Comment.Preproc),
222 # preprocessor variable (any line starting with '#' that is not a directive)
223 (r'^#', Comment.Preproc, 'variables'),
224 # String
225 ('"', String.Double, 'string'),
226 # Numbers
227 (r'[0-9]+\.[0-9]*(?!\.)', Number.Float),
228 (r'\.[0-9]+(?!\.)', Number.Float),
229 (r'[0-9]+', Number.Integer),
230 (r'\$[0-9a-fA-Z]+', Number.Hex),
231 (r'\%[10]+', Number.Bin),
232 # Native data types
233 (rf'\b{keyword_type}\b', Keyword.Type),
234 # Exception handling
235 (r'(?i)\b(?:Try|Catch|Throw)\b', Keyword.Reserved),
236 (r'Throwable', Name.Exception),
237 # Builtins
238 (r'(?i)\b(?:Null|True|False)\b', Name.Builtin),
239 (r'(?i)\b(?:Self|Super)\b', Name.Builtin.Pseudo),
240 (r'\b(?:HOST|LANG|TARGET|CONFIG)\b', Name.Constant),
241 # Keywords
242 (r'(?i)^(Import)(\s+)(.*)(\n)',
243 bygroups(Keyword.Namespace, Whitespace, Name.Namespace, Whitespace)),
244 (r'(?i)^Strict\b.*\n', Keyword.Reserved),
245 (r'(?i)(Const|Local|Global|Field)(\s+)',
246 bygroups(Keyword.Declaration, Whitespace), 'variables'),
247 (r'(?i)(New|Class|Interface|Extends|Implements)(\s+)',
248 bygroups(Keyword.Reserved, Whitespace), 'classname'),
249 (r'(?i)(Function|Method)(\s+)',
250 bygroups(Keyword.Reserved, Whitespace), 'funcname'),
251 (r'(?i)(?:End|Return|Public|Private|Extern|Property|'
252 r'Final|Abstract)\b', Keyword.Reserved),
253 # Flow Control stuff
254 (r'(?i)(?:If|Then|Else|ElseIf|EndIf|'
255 r'Select|Case|Default|'
256 r'While|Wend|'
257 r'Repeat|Until|Forever|'
258 r'For|To|Until|Step|EachIn|Next|'
259 r'Exit|Continue)(?=\s)', Keyword.Reserved),
260 # not used yet
261 (r'(?i)\b(?:Module|Inline)\b', Keyword.Reserved),
262 # Array
263 (r'[\[\]]', Punctuation),
264 # Other
265 (r'<=|>=|<>|\*=|/=|\+=|-=|&=|~=|\|=|[-&*/^+=<>|~]', Operator),
266 (r'(?i)(?:Not|Mod|Shl|Shr|And|Or)', Operator.Word),
267 (r'[(){}!#,.:]', Punctuation),
268 # catch the rest
269 (rf'{name_constant}\b', Name.Constant),
270 (rf'{name_function}\b', Name.Function),
271 (rf'{name_variable}\b', Name.Variable),
272 ],
273 'funcname': [
274 (rf'(?i){name_function}\b', Name.Function),
275 (r':', Punctuation, 'classname'),
276 (r'\s+', Whitespace),
277 (r'\(', Punctuation, 'variables'),
278 (r'\)', Punctuation, '#pop')
279 ],
280 'classname': [
281 (rf'{name_module}\.', Name.Namespace),
282 (rf'{keyword_type}\b', Keyword.Type),
283 (rf'{name_class}\b', Name.Class),
284 # array (of given size)
285 (r'(\[)(\s*)(\d*)(\s*)(\])',
286 bygroups(Punctuation, Whitespace, Number.Integer, Whitespace, Punctuation)),
287 # generics
288 (r'\s+(?!<)', Whitespace, '#pop'),
289 (r'<', Punctuation, '#push'),
290 (r'>', Punctuation, '#pop'),
291 (r'\n', Whitespace, '#pop'),
292 default('#pop')
293 ],
294 'variables': [
295 (rf'{name_constant}\b', Name.Constant),
296 (rf'{name_variable}\b', Name.Variable),
297 (rf'{keyword_type_special}', Keyword.Type),
298 (r'\s+', Whitespace),
299 (r':', Punctuation, 'classname'),
300 (r',', Punctuation, '#push'),
301 default('#pop')
302 ],
303 'string': [
304 (r'[^"~]+', String.Double),
305 (r'~q|~n|~r|~t|~z|~~', String.Escape),
306 (r'"', String.Double, '#pop'),
307 ],
308 'comment': [
309 (r'(?i)^#rem.*?', Comment.Multiline, "#push"),
310 (r'(?i)^#end.*?', Comment.Multiline, "#pop"),
311 (r'\n', Comment.Multiline),
312 (r'.+', Comment.Multiline),
313 ],
314 }
315
316
317class CbmBasicV2Lexer(RegexLexer):
318 """
319 For CBM BASIC V2 sources.
320 """
321 name = 'CBM BASIC V2'
322 aliases = ['cbmbas']
323 filenames = ['*.bas']
324 url = 'https://en.wikipedia.org/wiki/Commodore_BASIC'
325 version_added = '1.6'
326
327 flags = re.IGNORECASE
328
329 tokens = {
330 'root': [
331 (r'rem.*\n', Comment.Single),
332 (r'\s+', Whitespace),
333 (r'new|run|end|for|to|next|step|go(to|sub)?|on|return|stop|cont'
334 r'|if|then|input#?|read|wait|load|save|verify|poke|sys|print#?'
335 r'|list|clr|cmd|open|close|get#?', Keyword.Reserved),
336 (r'data|restore|dim|let|def|fn', Keyword.Declaration),
337 (r'tab|spc|sgn|int|abs|usr|fre|pos|sqr|rnd|log|exp|cos|sin|tan|atn'
338 r'|peek|len|val|asc|(str|chr|left|right|mid)\$', Name.Builtin),
339 (r'[-+*/^<>=]', Operator),
340 (r'not|and|or', Operator.Word),
341 (r'"[^"\n]*.', String),
342 (r'\d+|[-+]?\d*\.\d*(e[-+]?\d+)?', Number.Float),
343 (r'[(),:;]', Punctuation),
344 (r'\w+[$%]?', Name),
345 ]
346 }
347
348 def analyse_text(text):
349 # if it starts with a line number, it shouldn't be a "modern" Basic
350 # like VB.net
351 if re.match(r'^\d+', text):
352 return 0.2
353
354
355class QBasicLexer(RegexLexer):
356 """
357 For QBasic source code.
358 """
359
360 name = 'QBasic'
361 aliases = ['qbasic', 'basic']
362 filenames = ['*.BAS', '*.bas']
363 mimetypes = ['text/basic']
364 url = 'https://en.wikipedia.org/wiki/QBasic'
365 version_added = '2.0'
366
367 declarations = ('DATA', 'LET')
368
369 functions = (
370 'ABS', 'ASC', 'ATN', 'CDBL', 'CHR$', 'CINT', 'CLNG',
371 'COMMAND$', 'COS', 'CSNG', 'CSRLIN', 'CVD', 'CVDMBF', 'CVI',
372 'CVL', 'CVS', 'CVSMBF', 'DATE$', 'ENVIRON$', 'EOF', 'ERDEV',
373 'ERDEV$', 'ERL', 'ERR', 'EXP', 'FILEATTR', 'FIX', 'FRE',
374 'FREEFILE', 'HEX$', 'INKEY$', 'INP', 'INPUT$', 'INSTR', 'INT',
375 'IOCTL$', 'LBOUND', 'LCASE$', 'LEFT$', 'LEN', 'LOC', 'LOF',
376 'LOG', 'LPOS', 'LTRIM$', 'MID$', 'MKD$', 'MKDMBF$', 'MKI$',
377 'MKL$', 'MKS$', 'MKSMBF$', 'OCT$', 'PEEK', 'PEN', 'PLAY',
378 'PMAP', 'POINT', 'POS', 'RIGHT$', 'RND', 'RTRIM$', 'SADD',
379 'SCREEN', 'SEEK', 'SETMEM', 'SGN', 'SIN', 'SPACE$', 'SPC',
380 'SQR', 'STICK', 'STR$', 'STRIG', 'STRING$', 'TAB', 'TAN',
381 'TIME$', 'TIMER', 'UBOUND', 'UCASE$', 'VAL', 'VARPTR',
382 'VARPTR$', 'VARSEG'
383 )
384
385 metacommands = ('$DYNAMIC', '$INCLUDE', '$STATIC')
386
387 operators = ('AND', 'EQV', 'IMP', 'NOT', 'OR', 'XOR')
388
389 statements = (
390 'BEEP', 'BLOAD', 'BSAVE', 'CALL', 'CALL ABSOLUTE',
391 'CALL INTERRUPT', 'CALLS', 'CHAIN', 'CHDIR', 'CIRCLE', 'CLEAR',
392 'CLOSE', 'CLS', 'COLOR', 'COM', 'COMMON', 'CONST', 'DATA',
393 'DATE$', 'DECLARE', 'DEF FN', 'DEF SEG', 'DEFDBL', 'DEFINT',
394 'DEFLNG', 'DEFSNG', 'DEFSTR', 'DEF', 'DIM', 'DO', 'LOOP',
395 'DRAW', 'END', 'ENVIRON', 'ERASE', 'ERROR', 'EXIT', 'FIELD',
396 'FILES', 'FOR', 'NEXT', 'FUNCTION', 'GET', 'GOSUB', 'GOTO',
397 'IF', 'THEN', 'INPUT', 'INPUT #', 'IOCTL', 'KEY', 'KEY',
398 'KILL', 'LET', 'LINE', 'LINE INPUT', 'LINE INPUT #', 'LOCATE',
399 'LOCK', 'UNLOCK', 'LPRINT', 'LSET', 'MID$', 'MKDIR', 'NAME',
400 'ON COM', 'ON ERROR', 'ON KEY', 'ON PEN', 'ON PLAY',
401 'ON STRIG', 'ON TIMER', 'ON UEVENT', 'ON', 'OPEN', 'OPEN COM',
402 'OPTION BASE', 'OUT', 'PAINT', 'PALETTE', 'PCOPY', 'PEN',
403 'PLAY', 'POKE', 'PRESET', 'PRINT', 'PRINT #', 'PRINT USING',
404 'PSET', 'PUT', 'PUT', 'RANDOMIZE', 'READ', 'REDIM', 'REM',
405 'RESET', 'RESTORE', 'RESUME', 'RETURN', 'RMDIR', 'RSET', 'RUN',
406 'SCREEN', 'SEEK', 'SELECT CASE', 'SHARED', 'SHELL', 'SLEEP',
407 'SOUND', 'STATIC', 'STOP', 'STRIG', 'SUB', 'SWAP', 'SYSTEM',
408 'TIME$', 'TIMER', 'TROFF', 'TRON', 'TYPE', 'UEVENT', 'UNLOCK',
409 'VIEW', 'WAIT', 'WHILE', 'WEND', 'WIDTH', 'WINDOW', 'WRITE'
410 )
411
412 keywords = (
413 'ACCESS', 'ALIAS', 'ANY', 'APPEND', 'AS', 'BASE', 'BINARY',
414 'BYVAL', 'CASE', 'CDECL', 'DOUBLE', 'ELSE', 'ELSEIF', 'ENDIF',
415 'INTEGER', 'IS', 'LIST', 'LOCAL', 'LONG', 'LOOP', 'MOD',
416 'NEXT', 'OFF', 'ON', 'OUTPUT', 'RANDOM', 'SIGNAL', 'SINGLE',
417 'STEP', 'STRING', 'THEN', 'TO', 'UNTIL', 'USING', 'WEND'
418 )
419
420 tokens = {
421 'root': [
422 (r'\n+', Text),
423 (r'\s+', Text.Whitespace),
424 (r'^(\s*)(\d*)(\s*)(REM .*)$',
425 bygroups(Text.Whitespace, Name.Label, Text.Whitespace,
426 Comment.Single)),
427 (r'^(\s*)(\d+)(\s*)',
428 bygroups(Text.Whitespace, Name.Label, Text.Whitespace)),
429 (r'(?=[\s]*)(\w+)(?=[\s]*=)', Name.Variable.Global),
430 (r'(?=[^"]*)\'.*$', Comment.Single),
431 (r'"[^\n"]*"', String.Double),
432 (r'(END)(\s+)(FUNCTION|IF|SELECT|SUB)',
433 bygroups(Keyword.Reserved, Text.Whitespace, Keyword.Reserved)),
434 (r'(DECLARE)(\s+)([A-Z]+)(\s+)(\S+)',
435 bygroups(Keyword.Declaration, Text.Whitespace, Name.Variable,
436 Text.Whitespace, Name)),
437 (r'(DIM)(\s+)(SHARED)(\s+)([^\s(]+)',
438 bygroups(Keyword.Declaration, Text.Whitespace, Name.Variable,
439 Text.Whitespace, Name.Variable.Global)),
440 (r'(DIM)(\s+)([^\s(]+)',
441 bygroups(Keyword.Declaration, Text.Whitespace, Name.Variable.Global)),
442 (r'^(\s*)([a-zA-Z_]+)(\s*)(\=)',
443 bygroups(Text.Whitespace, Name.Variable.Global, Text.Whitespace,
444 Operator)),
445 (r'(GOTO|GOSUB)(\s+)(\w+\:?)',
446 bygroups(Keyword.Reserved, Text.Whitespace, Name.Label)),
447 (r'(SUB)(\s+)(\w+\:?)',
448 bygroups(Keyword.Reserved, Text.Whitespace, Name.Label)),
449 include('declarations'),
450 include('functions'),
451 include('metacommands'),
452 include('operators'),
453 include('statements'),
454 include('keywords'),
455 (r'[a-zA-Z_]\w*[$@#&!]', Name.Variable.Global),
456 (r'[a-zA-Z_]\w*\:', Name.Label),
457 (r'\-?\d*\.\d+[@|#]?', Number.Float),
458 (r'\-?\d+[@|#]', Number.Float),
459 (r'\-?\d+#?', Number.Integer.Long),
460 (r'\-?\d+#?', Number.Integer),
461 (r'!=|==|:=|\.=|<<|>>|[-~+/\\*%=<>&^|?:!.]', Operator),
462 (r'[\[\]{}(),;]', Punctuation),
463 (r'[\w]+', Name.Variable.Global),
464 ],
465 # can't use regular \b because of X$()
466 # XXX: use words() here
467 'declarations': [
468 (r'\b({})(?=\(|\b)'.format('|'.join(map(re.escape, declarations))),
469 Keyword.Declaration),
470 ],
471 'functions': [
472 (r'\b({})(?=\(|\b)'.format('|'.join(map(re.escape, functions))),
473 Keyword.Reserved),
474 ],
475 'metacommands': [
476 (r'\b({})(?=\(|\b)'.format('|'.join(map(re.escape, metacommands))),
477 Keyword.Constant),
478 ],
479 'operators': [
480 (r'\b({})(?=\(|\b)'.format('|'.join(map(re.escape, operators))), Operator.Word),
481 ],
482 'statements': [
483 (r'\b({})\b'.format('|'.join(map(re.escape, statements))),
484 Keyword.Reserved),
485 ],
486 'keywords': [
487 (r'\b({})\b'.format('|'.join(keywords)), Keyword),
488 ],
489 }
490
491 def analyse_text(text):
492 if '$DYNAMIC' in text or '$STATIC' in text:
493 return 0.9
494
495
496class VBScriptLexer(RegexLexer):
497 """
498 VBScript is scripting language that is modeled on Visual Basic.
499 """
500 name = 'VBScript'
501 aliases = ['vbscript']
502 filenames = ['*.vbs', '*.VBS']
503 url = 'https://learn.microsoft.com/en-us/previous-versions/t0aew7h6(v=vs.85)'
504 version_added = '2.4'
505
506 flags = re.IGNORECASE
507
508 tokens = {
509 'root': [
510 (r"'[^\n]*", Comment.Single),
511 (r'\s+', Whitespace),
512 ('"', String.Double, 'string'),
513 ('&h[0-9a-f]+', Number.Hex),
514 # Float variant 1, for example: 1., 1.e2, 1.2e3
515 (r'[0-9]+\.[0-9]*(e[+-]?[0-9]+)?', Number.Float),
516 (r'\.[0-9]+(e[+-]?[0-9]+)?', Number.Float), # Float variant 2, for example: .1, .1e2
517 (r'[0-9]+e[+-]?[0-9]+', Number.Float), # Float variant 3, for example: 123e45
518 (r'[0-9]+', Number.Integer),
519 ('#.+#', String), # date or time value
520 (r'(dim)(\s+)([a-z_][a-z0-9_]*)',
521 bygroups(Keyword.Declaration, Whitespace, Name.Variable), 'dim_more'),
522 (r'(function|sub)(\s+)([a-z_][a-z0-9_]*)',
523 bygroups(Keyword.Declaration, Whitespace, Name.Function)),
524 (r'(class)(\s+)([a-z_][a-z0-9_]*)',
525 bygroups(Keyword.Declaration, Whitespace, Name.Class)),
526 (r'(const)(\s+)([a-z_][a-z0-9_]*)',
527 bygroups(Keyword.Declaration, Whitespace, Name.Constant)),
528 (r'(end)(\s+)(class|function|if|property|sub|with)',
529 bygroups(Keyword, Whitespace, Keyword)),
530 (r'(on)(\s+)(error)(\s+)(goto)(\s+)(0)',
531 bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword, Whitespace, Number.Integer)),
532 (r'(on)(\s+)(error)(\s+)(resume)(\s+)(next)',
533 bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword, Whitespace, Keyword)),
534 (r'(option)(\s+)(explicit)', bygroups(Keyword, Whitespace, Keyword)),
535 (r'(property)(\s+)(get|let|set)(\s+)([a-z_][a-z0-9_]*)',
536 bygroups(Keyword.Declaration, Whitespace, Keyword.Declaration, Whitespace, Name.Property)),
537 (r'rem\s.*[^\n]*', Comment.Single),
538 (words(_vbscript_builtins.KEYWORDS, suffix=r'\b'), Keyword),
539 (words(_vbscript_builtins.OPERATORS), Operator),
540 (words(_vbscript_builtins.OPERATOR_WORDS, suffix=r'\b'), Operator.Word),
541 (words(_vbscript_builtins.BUILTIN_CONSTANTS, suffix=r'\b'), Name.Constant),
542 (words(_vbscript_builtins.BUILTIN_FUNCTIONS, suffix=r'\b'), Name.Builtin),
543 (words(_vbscript_builtins.BUILTIN_VARIABLES, suffix=r'\b'), Name.Builtin),
544 (r'[a-z_][a-z0-9_]*', Name),
545 (r'\b_\n', Operator),
546 (words(r'(),.:'), Punctuation),
547 (r'.+(\n)?', Error)
548 ],
549 'dim_more': [
550 (r'(\s*)(,)(\s*)([a-z_][a-z0-9]*)',
551 bygroups(Whitespace, Punctuation, Whitespace, Name.Variable)),
552 default('#pop'),
553 ],
554 'string': [
555 (r'[^"\n]+', String.Double),
556 (r'\"\"', String.Double),
557 (r'"', String.Double, '#pop'),
558 (r'\n', Error, '#pop'), # Unterminated string
559 ],
560 }
561
562
563class BBCBasicLexer(RegexLexer):
564 """
565 BBC Basic was supplied on the BBC Micro, and later Acorn RISC OS.
566 It is also used by BBC Basic For Windows.
567 """
568 base_keywords = ['OTHERWISE', 'AND', 'DIV', 'EOR', 'MOD', 'OR', 'ERROR',
569 'LINE', 'OFF', 'STEP', 'SPC', 'TAB', 'ELSE', 'THEN',
570 'OPENIN', 'PTR', 'PAGE', 'TIME', 'LOMEM', 'HIMEM', 'ABS',
571 'ACS', 'ADVAL', 'ASC', 'ASN', 'ATN', 'BGET', 'COS', 'COUNT',
572 'DEG', 'ERL', 'ERR', 'EVAL', 'EXP', 'EXT', 'FALSE', 'FN',
573 'GET', 'INKEY', 'INSTR', 'INT', 'LEN', 'LN', 'LOG', 'NOT',
574 'OPENUP', 'OPENOUT', 'PI', 'POINT', 'POS', 'RAD', 'RND',
575 'SGN', 'SIN', 'SQR', 'TAN', 'TO', 'TRUE', 'USR', 'VAL',
576 'VPOS', 'CHR$', 'GET$', 'INKEY$', 'LEFT$', 'MID$',
577 'RIGHT$', 'STR$', 'STRING$', 'EOF', 'PTR', 'PAGE', 'TIME',
578 'LOMEM', 'HIMEM', 'SOUND', 'BPUT', 'CALL', 'CHAIN', 'CLEAR',
579 'CLOSE', 'CLG', 'CLS', 'DATA', 'DEF', 'DIM', 'DRAW', 'END',
580 'ENDPROC', 'ENVELOPE', 'FOR', 'GOSUB', 'GOTO', 'GCOL', 'IF',
581 'INPUT', 'LET', 'LOCAL', 'MODE', 'MOVE', 'NEXT', 'ON',
582 'VDU', 'PLOT', 'PRINT', 'PROC', 'READ', 'REM', 'REPEAT',
583 'REPORT', 'RESTORE', 'RETURN', 'RUN', 'STOP', 'COLOUR',
584 'TRACE', 'UNTIL', 'WIDTH', 'OSCLI']
585
586 basic5_keywords = ['WHEN', 'OF', 'ENDCASE', 'ENDIF', 'ENDWHILE', 'CASE',
587 'CIRCLE', 'FILL', 'ORIGIN', 'POINT', 'RECTANGLE', 'SWAP',
588 'WHILE', 'WAIT', 'MOUSE', 'QUIT', 'SYS', 'INSTALL',
589 'LIBRARY', 'TINT', 'ELLIPSE', 'BEATS', 'TEMPO', 'VOICES',
590 'VOICE', 'STEREO', 'OVERLAY', 'APPEND', 'AUTO', 'CRUNCH',
591 'DELETE', 'EDIT', 'HELP', 'LIST', 'LOAD', 'LVAR', 'NEW',
592 'OLD', 'RENUMBER', 'SAVE', 'TEXTLOAD', 'TEXTSAVE',
593 'TWIN', 'TWINO', 'INSTALL', 'SUM', 'BEAT']
594
595
596 name = 'BBC Basic'
597 aliases = ['bbcbasic']
598 filenames = ['*.bbc']
599 url = 'https://www.bbcbasic.co.uk/bbcbasic.html'
600 version_added = '2.4'
601
602 tokens = {
603 'root': [
604 (r"[0-9]+", Name.Label),
605 (r"(\*)([^\n]*)",
606 bygroups(Keyword.Pseudo, Comment.Special)),
607 default('code'),
608 ],
609
610 'code': [
611 (r"(REM)([^\n]*)",
612 bygroups(Keyword.Declaration, Comment.Single)),
613 (r'\n', Whitespace, 'root'),
614 (r'\s+', Whitespace),
615 (r':', Comment.Preproc),
616
617 # Some special cases to make functions come out nicer
618 (r'(DEF)(\s*)(FN|PROC)([A-Za-z_@][\w@]*)',
619 bygroups(Keyword.Declaration, Whitespace,
620 Keyword.Declaration, Name.Function)),
621 (r'(FN|PROC)([A-Za-z_@][\w@]*)',
622 bygroups(Keyword, Name.Function)),
623
624 (r'(GOTO|GOSUB|THEN|RESTORE)(\s*)(\d+)',
625 bygroups(Keyword, Whitespace, Name.Label)),
626
627 (r'(TRUE|FALSE)', Keyword.Constant),
628 (r'(PAGE|LOMEM|HIMEM|TIME|WIDTH|ERL|ERR|REPORT\$|POS|VPOS|VOICES)',
629 Keyword.Pseudo),
630
631 (words(base_keywords), Keyword),
632 (words(basic5_keywords), Keyword),
633
634 ('"', String.Double, 'string'),
635
636 ('%[01]{1,32}', Number.Bin),
637 ('&[0-9a-f]{1,8}', Number.Hex),
638
639 (r'[+-]?[0-9]+\.[0-9]*(E[+-]?[0-9]+)?', Number.Float),
640 (r'[+-]?\.[0-9]+(E[+-]?[0-9]+)?', Number.Float),
641 (r'[+-]?[0-9]+E[+-]?[0-9]+', Number.Float),
642 (r'[+-]?\d+', Number.Integer),
643
644 (r'([A-Za-z_@][\w@]*[%$]?)', Name.Variable),
645 (r'([+\-]=|[$!|?+\-*/%^=><();]|>=|<=|<>|<<|>>|>>>|,)', Operator),
646 ],
647 'string': [
648 (r'[^"\n]+', String.Double),
649 (r'"', String.Double, '#pop'),
650 (r'\n', Error, 'root'), # Unterminated string
651 ],
652 }
653
654 def analyse_text(text):
655 if text.startswith('10REM >') or text.startswith('REM >'):
656 return 0.9