1"""
2 pygments.lexers.ruby
3 ~~~~~~~~~~~~~~~~~~~~
4
5 Lexers for Ruby and related 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 Lexer, RegexLexer, ExtendedRegexLexer, include, \
14 bygroups, default, LexerContext, do_insertions, words, line_re
15from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
16 Number, Punctuation, Error, Generic, Whitespace
17from pygments.util import shebang_matches
18
19__all__ = ['RubyLexer', 'RubyConsoleLexer', 'FancyLexer']
20
21
22RUBY_OPERATORS = (
23 '*', '**', '-', '+', '-@', '+@', '/', '%', '&', '|', '^', '`', '~',
24 '[]', '[]=', '<<', '>>', '<', '<>', '<=>', '>', '>=', '==', '==='
25)
26
27
28class RubyLexer(ExtendedRegexLexer):
29 """
30 For Ruby source code.
31 """
32
33 name = 'Ruby'
34 url = 'http://www.ruby-lang.org'
35 aliases = ['ruby', 'rb', 'duby']
36 filenames = ['*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec',
37 '*.rbx', '*.duby', 'Gemfile', 'Vagrantfile']
38 mimetypes = ['text/x-ruby', 'application/x-ruby']
39 version_added = ''
40
41 flags = re.DOTALL | re.MULTILINE
42
43 def heredoc_callback(self, match, ctx):
44 # okay, this is the hardest part of parsing Ruby...
45 # match: 1 = <<[-~]?, 2 = quote? 3 = name 4 = quote? 5 = rest of line
46
47 start = match.start(1)
48 yield start, Operator, match.group(1) # <<[-~]?
49 yield match.start(2), String.Heredoc, match.group(2) # quote ", ', `
50 yield match.start(3), String.Delimiter, match.group(3) # heredoc name
51 yield match.start(4), String.Heredoc, match.group(4) # quote again
52
53 heredocstack = ctx.__dict__.setdefault('heredocstack', [])
54 outermost = not bool(heredocstack)
55 heredocstack.append((match.group(1) in ('<<-', '<<~'), match.group(3)))
56
57 ctx.pos = match.start(5)
58 ctx.end = match.end(5)
59 # this may find other heredocs, so limit the recursion depth
60 if len(heredocstack) < 100:
61 yield from self.get_tokens_unprocessed(context=ctx)
62 else:
63 yield ctx.pos, String.Heredoc, match.group(5)
64 ctx.pos = match.end()
65
66 if outermost:
67 # this is the outer heredoc again, now we can process them all
68 for tolerant, hdname in heredocstack:
69 lines = []
70 for match in line_re.finditer(ctx.text, ctx.pos):
71 if tolerant:
72 check = match.group().strip()
73 else:
74 check = match.group().rstrip()
75 if check == hdname:
76 for amatch in lines:
77 yield amatch.start(), String.Heredoc, amatch.group()
78 yield match.start(), String.Delimiter, match.group()
79 ctx.pos = match.end()
80 break
81 else:
82 lines.append(match)
83 else:
84 # end of heredoc not found -- error!
85 for amatch in lines:
86 yield amatch.start(), Error, amatch.group()
87 ctx.end = len(ctx.text)
88 del heredocstack[:]
89
90 def gen_rubystrings_rules():
91 def intp_regex_callback(self, match, ctx):
92 yield match.start(1), String.Regex, match.group(1) # begin
93 nctx = LexerContext(match.group(3), 0, ['interpolated-regex'])
94 for i, t, v in self.get_tokens_unprocessed(context=nctx):
95 yield match.start(3)+i, t, v
96 yield match.start(4), String.Regex, match.group(4) # end[mixounse]*
97 ctx.pos = match.end()
98
99 def intp_string_callback(self, match, ctx):
100 yield match.start(1), String.Other, match.group(1)
101 nctx = LexerContext(match.group(3), 0, ['interpolated-string'])
102 for i, t, v in self.get_tokens_unprocessed(context=nctx):
103 yield match.start(3)+i, t, v
104 yield match.start(4), String.Other, match.group(4) # end
105 ctx.pos = match.end()
106
107 states = {}
108 states['strings'] = [
109 # easy ones
110 (r'\:@{0,2}[a-zA-Z_]\w*[!?]?', String.Symbol),
111 (words(RUBY_OPERATORS, prefix=r'\:@{0,2}'), String.Symbol),
112 (r":'(\\\\|\\[^\\]|[^'\\])*'", String.Symbol),
113 (r':"', String.Symbol, 'simple-sym'),
114 (r'([a-zA-Z_]\w*)(:)(?!:)',
115 bygroups(String.Symbol, Punctuation)), # Since Ruby 1.9
116 (r'"', String.Double, 'simple-string-double'),
117 (r"'", String.Single, 'simple-string-single'),
118 (r'(?<!\.)`', String.Backtick, 'simple-backtick'),
119 ]
120
121 # quoted string and symbol
122 for name, ttype, end in ('string-double', String.Double, '"'), \
123 ('string-single', String.Single, "'"),\
124 ('sym', String.Symbol, '"'), \
125 ('backtick', String.Backtick, '`'):
126 states['simple-'+name] = [
127 include('string-intp-escaped'),
128 (rf'[^\\{end}#]+', ttype),
129 (r'[\\#]', ttype),
130 (end, ttype, '#pop'),
131 ]
132
133 # braced quoted strings
134 for lbrace, rbrace, bracecc, name in \
135 ('\\{', '\\}', '{}', 'cb'), \
136 ('\\[', '\\]', '\\[\\]', 'sb'), \
137 ('\\(', '\\)', '()', 'pa'), \
138 ('<', '>', '<>', 'ab'):
139 states[name+'-intp-string'] = [
140 (r'\\[\\' + bracecc + ']', String.Other),
141 (lbrace, String.Other, '#push'),
142 (rbrace, String.Other, '#pop'),
143 include('string-intp-escaped'),
144 (r'[\\#' + bracecc + ']', String.Other),
145 (r'[^\\#' + bracecc + ']+', String.Other),
146 ]
147 states['strings'].append((r'%[QWx]?' + lbrace, String.Other,
148 name+'-intp-string'))
149 states[name+'-string'] = [
150 (r'\\[\\' + bracecc + ']', String.Other),
151 (lbrace, String.Other, '#push'),
152 (rbrace, String.Other, '#pop'),
153 (r'[\\#' + bracecc + ']', String.Other),
154 (r'[^\\#' + bracecc + ']+', String.Other),
155 ]
156 states['strings'].append((r'%[qsw]' + lbrace, String.Other,
157 name+'-string'))
158 states[name+'-regex'] = [
159 (r'\\[\\' + bracecc + ']', String.Regex),
160 (lbrace, String.Regex, '#push'),
161 (rbrace + '[mixounse]*', String.Regex, '#pop'),
162 include('string-intp'),
163 (r'[\\#' + bracecc + ']', String.Regex),
164 (r'[^\\#' + bracecc + ']+', String.Regex),
165 ]
166 states['strings'].append((r'%r' + lbrace, String.Regex,
167 name+'-regex'))
168
169 # these must come after %<brace>!
170 states['strings'] += [
171 # %r regex
172 (r'(%r([\W_]))((?:\\\2|(?!\2).)*)(\2[mixounse]*)',
173 intp_regex_callback),
174 # regular fancy strings with qsw
175 (r'%[qsw]([\W_])((?:\\\1|(?!\1).)*)\1', String.Other),
176 (r'(%[QWx]([\W_]))((?:\\\2|(?!\2).)*)(\2)',
177 intp_string_callback),
178 # special forms of fancy strings after operators or
179 # in method calls with braces
180 (r'(?<=[-+/*%=<>&!^|~,(])(\s*)(%([\t ])(?:(?:\\\3|(?!\3).)*)\3)',
181 bygroups(Whitespace, String.Other, None)),
182 # and because of fixed width lookbehinds the whole thing a
183 # second time for line startings...
184 (r'^(\s*)(%([\t ])(?:(?:\\\3|(?!\3).)*)\3)',
185 bygroups(Whitespace, String.Other, None)),
186 # all regular fancy strings without qsw
187 (r'(%([^a-zA-Z0-9\s]))((?:\\\2|(?!\2).)*)(\2)',
188 intp_string_callback),
189 ]
190
191 return states
192
193 tokens = {
194 'root': [
195 (r'\A#!.+?$', Comment.Hashbang),
196 (r'#.*?$', Comment.Single),
197 (r'=begin\s.*?\n=end.*?$', Comment.Multiline),
198 # keywords
199 (words((
200 'BEGIN', 'END', 'alias', 'begin', 'break', 'case', 'defined?',
201 'do', 'else', 'elsif', 'end', 'ensure', 'for', 'if', 'in', 'next', 'redo',
202 'rescue', 'raise', 'retry', 'return', 'super', 'then', 'undef',
203 'unless', 'until', 'when', 'while', 'yield'), suffix=r'\b'),
204 Keyword),
205 # start of function, class and module names
206 (r'(module)(\s+)([a-zA-Z_]\w*'
207 r'(?:::[a-zA-Z_]\w*)*)',
208 bygroups(Keyword, Whitespace, Name.Namespace)),
209 (r'(def)(\s+)', bygroups(Keyword, Whitespace), 'funcname'),
210 (r'def(?=[*%&^`~+-/\[<>=])', Keyword, 'funcname'),
211 (r'(class)(\s+)', bygroups(Keyword, Whitespace), 'classname'),
212 # special methods
213 (words((
214 'initialize', 'new', 'loop', 'include', 'extend', 'raise', 'attr_reader',
215 'attr_writer', 'attr_accessor', 'attr', 'catch', 'throw', 'private',
216 'module_function', 'public', 'protected', 'true', 'false', 'nil'),
217 suffix=r'\b'),
218 Keyword.Pseudo),
219 (r'(not|and|or)\b', Operator.Word),
220 (words((
221 'autoload', 'block_given', 'const_defined', 'eql', 'equal', 'frozen', 'include',
222 'instance_of', 'is_a', 'iterator', 'kind_of', 'method_defined', 'nil',
223 'private_method_defined', 'protected_method_defined',
224 'public_method_defined', 'respond_to', 'tainted'), suffix=r'\?'),
225 Name.Builtin),
226 (r'(chomp|chop|exit|gsub|sub)!', Name.Builtin),
227 (words((
228 'Array', 'Float', 'Integer', 'String', '__id__', '__send__', 'abort',
229 'ancestors', 'at_exit', 'autoload', 'binding', 'callcc', 'caller',
230 'catch', 'chomp', 'chop', 'class_eval', 'class_variables',
231 'clone', 'const_defined?', 'const_get', 'const_missing', 'const_set',
232 'constants', 'display', 'dup', 'eval', 'exec', 'exit', 'extend', 'fail', 'fork',
233 'format', 'freeze', 'getc', 'gets', 'global_variables', 'gsub',
234 'hash', 'id', 'included_modules', 'inspect', 'instance_eval',
235 'instance_method', 'instance_methods',
236 'instance_variable_get', 'instance_variable_set', 'instance_variables',
237 'lambda', 'load', 'local_variables', 'loop',
238 'method', 'method_missing', 'methods', 'module_eval', 'name',
239 'object_id', 'open', 'p', 'print', 'printf', 'private_class_method',
240 'private_instance_methods',
241 'private_methods', 'proc', 'protected_instance_methods',
242 'protected_methods', 'public_class_method',
243 'public_instance_methods', 'public_methods',
244 'putc', 'puts', 'raise', 'rand', 'readline', 'readlines', 'require',
245 'scan', 'select', 'self', 'send', 'set_trace_func', 'singleton_methods', 'sleep',
246 'split', 'sprintf', 'srand', 'sub', 'syscall', 'system', 'taint',
247 'test', 'throw', 'to_a', 'to_s', 'trace_var', 'trap', 'untaint',
248 'untrace_var', 'warn'), prefix=r'(?<!\.)', suffix=r'\b'),
249 Name.Builtin),
250 (r'__(FILE|LINE)__\b', Name.Builtin.Pseudo),
251 # normal heredocs
252 (r'(?<!\w)(<<[-~]?)(["`\']?)([a-zA-Z_]\w*)(\2)(.*?\n)',
253 heredoc_callback),
254 # empty string heredocs
255 (r'(<<[-~]?)("|\')()(\2)(.*?\n)', heredoc_callback),
256 (r'__END__', Comment.Preproc, 'end-part'),
257 # multiline regex (after keywords or assignments)
258 (r'(?:^|(?<=[=<>~!:])|'
259 r'(?<=(?:\s|;)when\s)|'
260 r'(?<=(?:\s|;)or\s)|'
261 r'(?<=(?:\s|;)and\s)|'
262 r'(?<=\.index\s)|'
263 r'(?<=\.scan\s)|'
264 r'(?<=\.sub\s)|'
265 r'(?<=\.sub!\s)|'
266 r'(?<=\.gsub\s)|'
267 r'(?<=\.gsub!\s)|'
268 r'(?<=\.match\s)|'
269 r'(?<=(?:\s|;)if\s)|'
270 r'(?<=(?:\s|;)elsif\s)|'
271 r'(?<=^when\s)|'
272 r'(?<=^index\s)|'
273 r'(?<=^scan\s)|'
274 r'(?<=^sub\s)|'
275 r'(?<=^gsub\s)|'
276 r'(?<=^sub!\s)|'
277 r'(?<=^gsub!\s)|'
278 r'(?<=^match\s)|'
279 r'(?<=^if\s)|'
280 r'(?<=^elsif\s)'
281 r')(\s*)(/)', bygroups(Text, String.Regex), 'multiline-regex'),
282 # multiline regex (in method calls or subscripts)
283 (r'(?<=\(|,|\[)/', String.Regex, 'multiline-regex'),
284 # multiline regex (this time the funny no whitespace rule)
285 (r'(\s+)(/)(?![\s=])', bygroups(Whitespace, String.Regex),
286 'multiline-regex'),
287 # lex numbers and ignore following regular expressions which
288 # are division operators in fact (grrrr. i hate that. any
289 # better ideas?)
290 # since pygments 0.7 we also eat a "?" operator after numbers
291 # so that the char operator does not work. Chars are not allowed
292 # there so that you can use the ternary operator.
293 # stupid example:
294 # x>=0?n[x]:""
295 (r'(0_?[0-7]+(?:_[0-7]+)*)(\s*)([/?])?',
296 bygroups(Number.Oct, Whitespace, Operator)),
297 (r'(0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)([/?])?',
298 bygroups(Number.Hex, Whitespace, Operator)),
299 (r'(0b[01]+(?:_[01]+)*)(\s*)([/?])?',
300 bygroups(Number.Bin, Whitespace, Operator)),
301 (r'([\d]+(?:_\d+)*)(\s*)([/?])?',
302 bygroups(Number.Integer, Whitespace, Operator)),
303 # Names
304 (r'@@[a-zA-Z_]\w*', Name.Variable.Class),
305 (r'@[a-zA-Z_]\w*', Name.Variable.Instance),
306 (r'\$\w+', Name.Variable.Global),
307 (r'\$[!@&`\'+~=/\\,;.<>_*$?:"^-]', Name.Variable.Global),
308 (r'\$-[0adFiIlpvw]', Name.Variable.Global),
309 (r'::', Operator),
310 include('strings'),
311 # chars
312 (r'\?(\\[MC]-)*' # modifiers
313 r'(\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})|\S)'
314 r'(?!\w)',
315 String.Char),
316 (r'[A-Z]\w+', Name.Constant),
317 # this is needed because ruby attributes can look
318 # like keywords (class) or like this: ` ?!?
319 (words(RUBY_OPERATORS, prefix=r'(\.|::)'),
320 bygroups(Operator, Name.Operator)),
321 (r'(\.|::)([a-zA-Z_]\w*[!?]?|[*%&^`~+\-/\[<>=])',
322 bygroups(Operator, Name)),
323 (r'[a-zA-Z_]\w*[!?]?', Name),
324 (r'(\[|\]|\*\*|<<?|>>?|>=|<=|<=>|=~|={3}|'
325 r'!~|&&?|\|\||\.{1,3})', Operator),
326 (r'[-+/*%=<>&!^|~]=?', Operator),
327 (r'[(){};,/?:\\]', Punctuation),
328 (r'\s+', Whitespace)
329 ],
330 'funcname': [
331 (r'\(', Punctuation, 'defexpr'),
332 (r'(?:([a-zA-Z_]\w*)(\.))?' # optional scope name, like "self."
333 r'('
334 r'[a-zA-Z\u0080-\uffff][a-zA-Z0-9_\u0080-\uffff]*[!?=]?' # method name
335 r'|!=|!~|=~|\*\*?|[-+!~]@?|[/%&|^]|<=>|<[<=]?|>[>=]?|===?' # or operator override
336 r'|\[\]=?' # or element reference/assignment override
337 r'|`' # or the undocumented backtick override
338 r')',
339 bygroups(Name.Class, Operator, Name.Function), '#pop'),
340 default('#pop')
341 ],
342 'classname': [
343 (r'\(', Punctuation, 'defexpr'),
344 (r'<<', Operator, '#pop'),
345 (r'[A-Z_]\w*', Name.Class, '#pop'),
346 default('#pop')
347 ],
348 'defexpr': [
349 (r'(\))(\.|::)?', bygroups(Punctuation, Operator), '#pop'),
350 (r'\(', Operator, '#push'),
351 include('root')
352 ],
353 'in-intp': [
354 (r'\{', String.Interpol, '#push'),
355 (r'\}', String.Interpol, '#pop'),
356 include('root'),
357 ],
358 'string-intp': [
359 (r'#\{', String.Interpol, 'in-intp'),
360 (r'#@@?[a-zA-Z_]\w*', String.Interpol),
361 (r'#\$[a-zA-Z_]\w*', String.Interpol)
362 ],
363 'string-intp-escaped': [
364 include('string-intp'),
365 (r'\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})',
366 String.Escape)
367 ],
368 'interpolated-regex': [
369 include('string-intp'),
370 (r'[\\#]', String.Regex),
371 (r'[^\\#]+', String.Regex),
372 ],
373 'interpolated-string': [
374 include('string-intp'),
375 (r'[\\#]', String.Other),
376 (r'[^\\#]+', String.Other),
377 ],
378 'multiline-regex': [
379 include('string-intp'),
380 (r'\\\\', String.Regex),
381 (r'\\/', String.Regex),
382 (r'[\\#]', String.Regex),
383 (r'[^\\/#]+', String.Regex),
384 (r'/[mixounse]*', String.Regex, '#pop'),
385 ],
386 'end-part': [
387 (r'.+', Comment.Preproc, '#pop')
388 ]
389 }
390 tokens.update(gen_rubystrings_rules())
391
392 def analyse_text(text):
393 return shebang_matches(text, r'ruby(1\.\d)?')
394
395
396class RubyConsoleLexer(Lexer):
397 """
398 For Ruby interactive console (**irb**) output.
399 """
400 name = 'Ruby irb session'
401 aliases = ['rbcon', 'irb']
402 mimetypes = ['text/x-ruby-shellsession']
403 url = 'https://www.ruby-lang.org'
404 version_added = ''
405 _example = 'rbcon/console'
406
407 _prompt_re = re.compile(r'irb\([a-zA-Z_]\w*\):\d{3}:\d+[>*"\'] '
408 r'|>> |\?> ')
409
410 def get_tokens_unprocessed(self, text):
411 rblexer = RubyLexer(**self.options)
412
413 curcode = ''
414 insertions = []
415 for match in line_re.finditer(text):
416 line = match.group()
417 m = self._prompt_re.match(line)
418 if m is not None:
419 end = m.end()
420 insertions.append((len(curcode),
421 [(0, Generic.Prompt, line[:end])]))
422 curcode += line[end:]
423 else:
424 if curcode:
425 yield from do_insertions(
426 insertions, rblexer.get_tokens_unprocessed(curcode))
427 curcode = ''
428 insertions = []
429 yield match.start(), Generic.Output, line
430 if curcode:
431 yield from do_insertions(
432 insertions, rblexer.get_tokens_unprocessed(curcode))
433
434
435class FancyLexer(RegexLexer):
436 """
437 Pygments Lexer For Fancy.
438
439 Fancy is a self-hosted, pure object-oriented, dynamic,
440 class-based, concurrent general-purpose programming language
441 running on Rubinius, the Ruby VM.
442 """
443 name = 'Fancy'
444 url = 'https://github.com/bakkdoor/fancy'
445 filenames = ['*.fy', '*.fancypack']
446 aliases = ['fancy', 'fy']
447 mimetypes = ['text/x-fancysrc']
448 version_added = '1.5'
449
450 tokens = {
451 # copied from PerlLexer:
452 'balanced-regex': [
453 (r'/(\\\\|\\[^\\]|[^/\\])*/[egimosx]*', String.Regex, '#pop'),
454 (r'!(\\\\|\\[^\\]|[^!\\])*![egimosx]*', String.Regex, '#pop'),
455 (r'\\(\\\\|[^\\])*\\[egimosx]*', String.Regex, '#pop'),
456 (r'\{(\\\\|\\[^\\]|[^}\\])*\}[egimosx]*', String.Regex, '#pop'),
457 (r'<(\\\\|\\[^\\]|[^>\\])*>[egimosx]*', String.Regex, '#pop'),
458 (r'\[(\\\\|\\[^\\]|[^\]\\])*\][egimosx]*', String.Regex, '#pop'),
459 (r'\((\\\\|\\[^\\]|[^)\\])*\)[egimosx]*', String.Regex, '#pop'),
460 (r'@(\\\\|\\[^\\]|[^@\\])*@[egimosx]*', String.Regex, '#pop'),
461 (r'%(\\\\|\\[^\\]|[^%\\])*%[egimosx]*', String.Regex, '#pop'),
462 (r'\$(\\\\|\\[^\\]|[^$\\])*\$[egimosx]*', String.Regex, '#pop'),
463 ],
464 'root': [
465 (r'\s+', Whitespace),
466
467 # balanced delimiters (copied from PerlLexer):
468 (r's\{(\\\\|\\[^\\]|[^}\\])*\}\s*', String.Regex, 'balanced-regex'),
469 (r's<(\\\\|\\[^\\]|[^>\\])*>\s*', String.Regex, 'balanced-regex'),
470 (r's\[(\\\\|\\[^\\]|[^\]\\])*\]\s*', String.Regex, 'balanced-regex'),
471 (r's\((\\\\|\\[^\\]|[^)\\])*\)\s*', String.Regex, 'balanced-regex'),
472 (r'm?/(\\\\|\\[^\\]|[^///\n])*/[gcimosx]*', String.Regex),
473 (r'm(?=[/!\\{<\[(@%$])', String.Regex, 'balanced-regex'),
474
475 # Comments
476 (r'#(.*?)\n', Comment.Single),
477 # Symbols
478 (r'\'([^\'\s\[\](){}]+|\[\])', String.Symbol),
479 # Multi-line DoubleQuotedString
480 (r'"""(\\\\|\\[^\\]|[^\\])*?"""', String),
481 # DoubleQuotedString
482 (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
483 # keywords
484 (r'(def|class|try|catch|finally|retry|return|return_local|match|'
485 r'case|->|=>)\b', Keyword),
486 # constants
487 (r'(self|super|nil|false|true)\b', Name.Constant),
488 (r'[(){};,/?|:\\]', Punctuation),
489 # names
490 (words((
491 'Object', 'Array', 'Hash', 'Directory', 'File', 'Class', 'String',
492 'Number', 'Enumerable', 'FancyEnumerable', 'Block', 'TrueClass',
493 'NilClass', 'FalseClass', 'Tuple', 'Symbol', 'Stack', 'Set',
494 'FancySpec', 'Method', 'Package', 'Range'), suffix=r'\b'),
495 Name.Builtin),
496 # functions
497 (r'[a-zA-Z](\w|[-+?!=*/^><%])*:', Name.Function),
498 # operators, must be below functions
499 (r'[-+*/~,<>=&!?%^\[\].$]+', Operator),
500 (r'[A-Z]\w*', Name.Constant),
501 (r'@[a-zA-Z_]\w*', Name.Variable.Instance),
502 (r'@@[a-zA-Z_]\w*', Name.Variable.Class),
503 ('@@?', Operator),
504 (r'[a-zA-Z_]\w*', Name),
505 # numbers - / checks are necessary to avoid mismarking regexes,
506 # see comment in RubyLexer
507 (r'(0[oO]?[0-7]+(?:_[0-7]+)*)(\s*)([/?])?',
508 bygroups(Number.Oct, Whitespace, Operator)),
509 (r'(0[xX][0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)([/?])?',
510 bygroups(Number.Hex, Whitespace, Operator)),
511 (r'(0[bB][01]+(?:_[01]+)*)(\s*)([/?])?',
512 bygroups(Number.Bin, Whitespace, Operator)),
513 (r'([\d]+(?:_\d+)*)(\s*)([/?])?',
514 bygroups(Number.Integer, Whitespace, Operator)),
515 (r'\d+([eE][+-]?[0-9]+)|\d+\.\d+([eE][+-]?[0-9]+)?', Number.Float),
516 (r'\d+', Number.Integer)
517 ]
518 }