"""
    pygments.lexers.ruby
    ~~~~~~~~~~~~~~~~~~~~

    Lexers for Ruby and related languages.

    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

import re

from pygments.lexer import (
    Lexer,
    RegexLexer,
    ExtendedRegexLexer,
    include,
    bygroups,
    default,
    LexerContext,
    do_insertions,
    words,
    line_re,
)
from pygments.token import (
    Text,
    Comment,
    Operator,
    Keyword,
    Name,
    String,
    Number,
    Punctuation,
    Error,
    Generic,
    Whitespace,
)
from pygments.util import shebang_matches

__all__ = ["RubyLexer", "RubyConsoleLexer", "FancyLexer"]


RUBY_OPERATORS = (
    "*",
    "**",
    "-",
    "+",
    "-@",
    "+@",
    "/",
    "%",
    "&",
    "|",
    "^",
    "`",
    "~",
    "[]",
    "[]=",
    "<<",
    ">>",
    "<",
    "<>",
    "<=>",
    ">",
    ">=",
    "==",
    "===",
)


class RubyLexer(ExtendedRegexLexer):
    """
    For Ruby source code.
    """

    name = "Ruby"
    url = "http://www.ruby-lang.org"
    aliases = ["ruby", "rb", "duby"]
    filenames = ["*.rb", "*.rbw", "Rakefile", "*.rake", "*.gemspec", "*.rbx", "*.duby", "Gemfile", "Vagrantfile"]
    mimetypes = ["text/x-ruby", "application/x-ruby"]

    flags = re.DOTALL | re.MULTILINE

    def heredoc_callback(self, match, ctx):
        # okay, this is the hardest part of parsing Ruby...
        # match: 1 = <<[-~]?, 2 = quote? 3 = name 4 = quote? 5 = rest of line

        start = match.start(1)
        yield start, Operator, match.group(1)  # <<[-~]?
        yield match.start(2), String.Heredoc, match.group(2)  # quote ", ', `
        yield match.start(3), String.Delimiter, match.group(3)  # heredoc name
        yield match.start(4), String.Heredoc, match.group(4)  # quote again

        heredocstack = ctx.__dict__.setdefault("heredocstack", [])
        outermost = not bool(heredocstack)
        heredocstack.append((match.group(1) in ("<<-", "<<~"), match.group(3)))

        ctx.pos = match.start(5)
        ctx.end = match.end(5)
        # this may find other heredocs, so limit the recursion depth
        if len(heredocstack) < 100:
            yield from self.get_tokens_unprocessed(context=ctx)
        else:
            yield ctx.pos, String.Heredoc, match.group(5)
        ctx.pos = match.end()

        if outermost:
            # this is the outer heredoc again, now we can process them all
            for tolerant, hdname in heredocstack:
                lines = []
                for match in line_re.finditer(ctx.text, ctx.pos):
                    if tolerant:
                        check = match.group().strip()
                    else:
                        check = match.group().rstrip()
                    if check == hdname:
                        for amatch in lines:
                            yield amatch.start(), String.Heredoc, amatch.group()
                        yield match.start(), String.Delimiter, match.group()
                        ctx.pos = match.end()
                        break
                    else:
                        lines.append(match)
                else:
                    # end of heredoc not found -- error!
                    for amatch in lines:
                        yield amatch.start(), Error, amatch.group()
            ctx.end = len(ctx.text)
            del heredocstack[:]

    def gen_rubystrings_rules():
        def intp_regex_callback(self, match, ctx):
            yield match.start(1), String.Regex, match.group(1)  # begin
            nctx = LexerContext(match.group(3), 0, ["interpolated-regex"])
            for i, t, v in self.get_tokens_unprocessed(context=nctx):
                yield match.start(3) + i, t, v
            yield match.start(4), String.Regex, match.group(4)  # end[mixounse]*
            ctx.pos = match.end()

        def intp_string_callback(self, match, ctx):
            yield match.start(1), String.Other, match.group(1)
            nctx = LexerContext(match.group(3), 0, ["interpolated-string"])
            for i, t, v in self.get_tokens_unprocessed(context=nctx):
                yield match.start(3) + i, t, v
            yield match.start(4), String.Other, match.group(4)  # end
            ctx.pos = match.end()

        states = {}
        states["strings"] = [
            # easy ones
            (r"\:@{0,2}[a-zA-Z_]\w*[!?]?", String.Symbol),
            (words(RUBY_OPERATORS, prefix=r"\:@{0,2}"), String.Symbol),
            (r":'(\\\\|\\[^\\]|[^'\\])*'", String.Symbol),
            (r':"', String.Symbol, "simple-sym"),
            (r"([a-zA-Z_]\w*)(:)(?!:)", bygroups(String.Symbol, Punctuation)),  # Since Ruby 1.9
            (r'"', String.Double, "simple-string-double"),
            (r"'", String.Single, "simple-string-single"),
            (r"(?<!\.)`", String.Backtick, "simple-backtick"),
        ]

        # quoted string and symbol
        for name, ttype, end in (
            ("string-double", String.Double, '"'),
            ("string-single", String.Single, "'"),
            ("sym", String.Symbol, '"'),
            ("backtick", String.Backtick, "`"),
        ):
            states["simple-" + name] = [
                include("string-intp-escaped"),
                (r"[^\\%s#]+" % end, ttype),
                (r"[\\#]", ttype),
                (end, ttype, "#pop"),
            ]

        # braced quoted strings
        for lbrace, rbrace, bracecc, name in (
            ("\\{", "\\}", "{}", "cb"),
            ("\\[", "\\]", "\\[\\]", "sb"),
            ("\\(", "\\)", "()", "pa"),
            ("<", ">", "<>", "ab"),
        ):
            states[name + "-intp-string"] = [
                (r"\\[\\" + bracecc + "]", String.Other),
                (lbrace, String.Other, "#push"),
                (rbrace, String.Other, "#pop"),
                include("string-intp-escaped"),
                (r"[\\#" + bracecc + "]", String.Other),
                (r"[^\\#" + bracecc + "]+", String.Other),
            ]
            states["strings"].append((r"%[QWx]?" + lbrace, String.Other, name + "-intp-string"))
            states[name + "-string"] = [
                (r"\\[\\" + bracecc + "]", String.Other),
                (lbrace, String.Other, "#push"),
                (rbrace, String.Other, "#pop"),
                (r"[\\#" + bracecc + "]", String.Other),
                (r"[^\\#" + bracecc + "]+", String.Other),
            ]
            states["strings"].append((r"%[qsw]" + lbrace, String.Other, name + "-string"))
            states[name + "-regex"] = [
                (r"\\[\\" + bracecc + "]", String.Regex),
                (lbrace, String.Regex, "#push"),
                (rbrace + "[mixounse]*", String.Regex, "#pop"),
                include("string-intp"),
                (r"[\\#" + bracecc + "]", String.Regex),
                (r"[^\\#" + bracecc + "]+", String.Regex),
            ]
            states["strings"].append((r"%r" + lbrace, String.Regex, name + "-regex"))

        # these must come after %<brace>!
        states["strings"] += [
            # %r regex
            (r"(%r([\W_]))((?:\\\2|(?!\2).)*)(\2[mixounse]*)", intp_regex_callback),
            # regular fancy strings with qsw
            (r"%[qsw]([\W_])((?:\\\1|(?!\1).)*)\1", String.Other),
            (r"(%[QWx]([\W_]))((?:\\\2|(?!\2).)*)(\2)", intp_string_callback),
            # special forms of fancy strings after operators or
            # in method calls with braces
            (r"(?<=[-+/*%=<>&!^|~,(])(\s*)(%([\t ])(?:(?:\\\3|(?!\3).)*)\3)", bygroups(Whitespace, String.Other, None)),
            # and because of fixed width lookbehinds the whole thing a
            # second time for line startings...
            (r"^(\s*)(%([\t ])(?:(?:\\\3|(?!\3).)*)\3)", bygroups(Whitespace, String.Other, None)),
            # all regular fancy strings without qsw
            (r"(%([^a-zA-Z0-9\s]))((?:\\\2|(?!\2).)*)(\2)", intp_string_callback),
        ]

        return states

    tokens = {
        "root": [
            (r"\A#!.+?$", Comment.Hashbang),
            (r"#.*?$", Comment.Single),
            (r"=begin\s.*?\n=end.*?$", Comment.Multiline),
            # keywords
            (
                words(
                    (
                        "BEGIN",
                        "END",
                        "alias",
                        "begin",
                        "break",
                        "case",
                        "defined?",
                        "do",
                        "else",
                        "elsif",
                        "end",
                        "ensure",
                        "for",
                        "if",
                        "in",
                        "next",
                        "redo",
                        "rescue",
                        "raise",
                        "retry",
                        "return",
                        "super",
                        "then",
                        "undef",
                        "unless",
                        "until",
                        "when",
                        "while",
                        "yield",
                    ),
                    suffix=r"\b",
                ),
                Keyword,
            ),
            # start of function, class and module names
            (r"(module)(\s+)([a-zA-Z_]\w*" r"(?:::[a-zA-Z_]\w*)*)", bygroups(Keyword, Whitespace, Name.Namespace)),
            (r"(def)(\s+)", bygroups(Keyword, Whitespace), "funcname"),
            (r"def(?=[*%&^`~+-/\[<>=])", Keyword, "funcname"),
            (r"(class)(\s+)", bygroups(Keyword, Whitespace), "classname"),
            # special methods
            (
                words(
                    (
                        "initialize",
                        "new",
                        "loop",
                        "include",
                        "extend",
                        "raise",
                        "attr_reader",
                        "attr_writer",
                        "attr_accessor",
                        "attr",
                        "catch",
                        "throw",
                        "private",
                        "module_function",
                        "public",
                        "protected",
                        "true",
                        "false",
                        "nil",
                    ),
                    suffix=r"\b",
                ),
                Keyword.Pseudo,
            ),
            (r"(not|and|or)\b", Operator.Word),
            (
                words(
                    (
                        "autoload",
                        "block_given",
                        "const_defined",
                        "eql",
                        "equal",
                        "frozen",
                        "include",
                        "instance_of",
                        "is_a",
                        "iterator",
                        "kind_of",
                        "method_defined",
                        "nil",
                        "private_method_defined",
                        "protected_method_defined",
                        "public_method_defined",
                        "respond_to",
                        "tainted",
                    ),
                    suffix=r"\?",
                ),
                Name.Builtin,
            ),
            (r"(chomp|chop|exit|gsub|sub)!", Name.Builtin),
            (
                words(
                    (
                        "Array",
                        "Float",
                        "Integer",
                        "String",
                        "__id__",
                        "__send__",
                        "abort",
                        "ancestors",
                        "at_exit",
                        "autoload",
                        "binding",
                        "callcc",
                        "caller",
                        "catch",
                        "chomp",
                        "chop",
                        "class_eval",
                        "class_variables",
                        "clone",
                        "const_defined?",
                        "const_get",
                        "const_missing",
                        "const_set",
                        "constants",
                        "display",
                        "dup",
                        "eval",
                        "exec",
                        "exit",
                        "extend",
                        "fail",
                        "fork",
                        "format",
                        "freeze",
                        "getc",
                        "gets",
                        "global_variables",
                        "gsub",
                        "hash",
                        "id",
                        "included_modules",
                        "inspect",
                        "instance_eval",
                        "instance_method",
                        "instance_methods",
                        "instance_variable_get",
                        "instance_variable_set",
                        "instance_variables",
                        "lambda",
                        "load",
                        "local_variables",
                        "loop",
                        "method",
                        "method_missing",
                        "methods",
                        "module_eval",
                        "name",
                        "object_id",
                        "open",
                        "p",
                        "print",
                        "printf",
                        "private_class_method",
                        "private_instance_methods",
                        "private_methods",
                        "proc",
                        "protected_instance_methods",
                        "protected_methods",
                        "public_class_method",
                        "public_instance_methods",
                        "public_methods",
                        "putc",
                        "puts",
                        "raise",
                        "rand",
                        "readline",
                        "readlines",
                        "require",
                        "scan",
                        "select",
                        "self",
                        "send",
                        "set_trace_func",
                        "singleton_methods",
                        "sleep",
                        "split",
                        "sprintf",
                        "srand",
                        "sub",
                        "syscall",
                        "system",
                        "taint",
                        "test",
                        "throw",
                        "to_a",
                        "to_s",
                        "trace_var",
                        "trap",
                        "untaint",
                        "untrace_var",
                        "warn",
                    ),
                    prefix=r"(?<!\.)",
                    suffix=r"\b",
                ),
                Name.Builtin,
            ),
            (r"__(FILE|LINE)__\b", Name.Builtin.Pseudo),
            # normal heredocs
            (r'(?<!\w)(<<[-~]?)(["`\']?)([a-zA-Z_]\w*)(\2)(.*?\n)', heredoc_callback),
            # empty string heredocs
            (r'(<<[-~]?)("|\')()(\2)(.*?\n)', heredoc_callback),
            (r"__END__", Comment.Preproc, "end-part"),
            # multiline regex (after keywords or assignments)
            (
                r"(?:^|(?<=[=<>~!:])|"
                r"(?<=(?:\s|;)when\s)|"
                r"(?<=(?:\s|;)or\s)|"
                r"(?<=(?:\s|;)and\s)|"
                r"(?<=\.index\s)|"
                r"(?<=\.scan\s)|"
                r"(?<=\.sub\s)|"
                r"(?<=\.sub!\s)|"
                r"(?<=\.gsub\s)|"
                r"(?<=\.gsub!\s)|"
                r"(?<=\.match\s)|"
                r"(?<=(?:\s|;)if\s)|"
                r"(?<=(?:\s|;)elsif\s)|"
                r"(?<=^when\s)|"
                r"(?<=^index\s)|"
                r"(?<=^scan\s)|"
                r"(?<=^sub\s)|"
                r"(?<=^gsub\s)|"
                r"(?<=^sub!\s)|"
                r"(?<=^gsub!\s)|"
                r"(?<=^match\s)|"
                r"(?<=^if\s)|"
                r"(?<=^elsif\s)"
                r")(\s*)(/)",
                bygroups(Text, String.Regex),
                "multiline-regex",
            ),
            # multiline regex (in method calls or subscripts)
            (r"(?<=\(|,|\[)/", String.Regex, "multiline-regex"),
            # multiline regex (this time the funny no whitespace rule)
            (r"(\s+)(/)(?![\s=])", bygroups(Whitespace, String.Regex), "multiline-regex"),
            # lex numbers and ignore following regular expressions which
            # are division operators in fact (grrrr. i hate that. any
            # better ideas?)
            # since pygments 0.7 we also eat a "?" operator after numbers
            # so that the char operator does not work. Chars are not allowed
            # there so that you can use the ternary operator.
            # stupid example:
            #   x>=0?n[x]:""
            (r"(0_?[0-7]+(?:_[0-7]+)*)(\s*)([/?])?", bygroups(Number.Oct, Whitespace, Operator)),
            (r"(0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)([/?])?", bygroups(Number.Hex, Whitespace, Operator)),
            (r"(0b[01]+(?:_[01]+)*)(\s*)([/?])?", bygroups(Number.Bin, Whitespace, Operator)),
            (r"([\d]+(?:_\d+)*)(\s*)([/?])?", bygroups(Number.Integer, Whitespace, Operator)),
            # Names
            (r"@@[a-zA-Z_]\w*", Name.Variable.Class),
            (r"@[a-zA-Z_]\w*", Name.Variable.Instance),
            (r"\$\w+", Name.Variable.Global),
            (r'\$[!@&`\'+~=/\\,;.<>_*$?:"^-]', Name.Variable.Global),
            (r"\$-[0adFiIlpvw]", Name.Variable.Global),
            (r"::", Operator),
            include("strings"),
            # chars
            (
                r"\?(\\[MC]-)*"  # modifiers
                r'(\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})|\S)'
                r"(?!\w)",
                String.Char,
            ),
            (r"[A-Z]\w+", Name.Constant),
            # this is needed because ruby attributes can look
            # like keywords (class) or like this: ` ?!?
            (words(RUBY_OPERATORS, prefix=r"(\.|::)"), bygroups(Operator, Name.Operator)),
            (r"(\.|::)([a-zA-Z_]\w*[!?]?|[*%&^`~+\-/\[<>=])", bygroups(Operator, Name)),
            (r"[a-zA-Z_]\w*[!?]?", Name),
            (r"(\[|\]|\*\*|<<?|>>?|>=|<=|<=>|=~|={3}|" r"!~|&&?|\|\||\.{1,3})", Operator),
            (r"[-+/*%=<>&!^|~]=?", Operator),
            (r"[(){};,/?:\\]", Punctuation),
            (r"\s+", Whitespace),
        ],
        "funcname": [
            (r"\(", Punctuation, "defexpr"),
            (
                r"(?:([a-zA-Z_]\w*)(\.))?"  # optional scope name, like "self."
                r"("
                r"[a-zA-Z\u0080-\uffff][a-zA-Z0-9_\u0080-\uffff]*[!?=]?"  # method name
                r"|!=|!~|=~|\*\*?|[-+!~]@?|[/%&|^]|<=>|<[<=]?|>[>=]?|===?"  # or operator override
                r"|\[\]=?"  # or element reference/assignment override
                r"|`"  # or the undocumented backtick override
                r")",
                bygroups(Name.Class, Operator, Name.Function),
                "#pop",
            ),
            default("#pop"),
        ],
        "classname": [
            (r"\(", Punctuation, "defexpr"),
            (r"<<", Operator, "#pop"),
            (r"[A-Z_]\w*", Name.Class, "#pop"),
            default("#pop"),
        ],
        "defexpr": [
            (r"(\))(\.|::)?", bygroups(Punctuation, Operator), "#pop"),
            (r"\(", Operator, "#push"),
            include("root"),
        ],
        "in-intp": [
            (r"\{", String.Interpol, "#push"),
            (r"\}", String.Interpol, "#pop"),
            include("root"),
        ],
        "string-intp": [
            (r"#\{", String.Interpol, "in-intp"),
            (r"#@@?[a-zA-Z_]\w*", String.Interpol),
            (r"#\$[a-zA-Z_]\w*", String.Interpol),
        ],
        "string-intp-escaped": [
            include("string-intp"),
            (r'\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})', String.Escape),
        ],
        "interpolated-regex": [
            include("string-intp"),
            (r"[\\#]", String.Regex),
            (r"[^\\#]+", String.Regex),
        ],
        "interpolated-string": [
            include("string-intp"),
            (r"[\\#]", String.Other),
            (r"[^\\#]+", String.Other),
        ],
        "multiline-regex": [
            include("string-intp"),
            (r"\\\\", String.Regex),
            (r"\\/", String.Regex),
            (r"[\\#]", String.Regex),
            (r"[^\\/#]+", String.Regex),
            (r"/[mixounse]*", String.Regex, "#pop"),
        ],
        "end-part": [(r".+", Comment.Preproc, "#pop")],
    }
    tokens.update(gen_rubystrings_rules())

    def analyse_text(text):
        return shebang_matches(text, r"ruby(1\.\d)?")


class RubyConsoleLexer(Lexer):
    """
    For Ruby interactive console (**irb**) output.
    """

    name = "Ruby irb session"
    aliases = ["rbcon", "irb"]
    mimetypes = ["text/x-ruby-shellsession"]
    _example = "rbcon/console"

    _prompt_re = re.compile(r'irb\([a-zA-Z_]\w*\):\d{3}:\d+[>*"\'] ' r"|>> |\?> ")

    def get_tokens_unprocessed(self, text):
        rblexer = RubyLexer(**self.options)

        curcode = ""
        insertions = []
        for match in line_re.finditer(text):
            line = match.group()
            m = self._prompt_re.match(line)
            if m is not None:
                end = m.end()
                insertions.append((len(curcode), [(0, Generic.Prompt, line[:end])]))
                curcode += line[end:]
            else:
                if curcode:
                    yield from do_insertions(insertions, rblexer.get_tokens_unprocessed(curcode))
                    curcode = ""
                    insertions = []
                yield match.start(), Generic.Output, line
        if curcode:
            yield from do_insertions(insertions, rblexer.get_tokens_unprocessed(curcode))


class FancyLexer(RegexLexer):
    """
    Pygments Lexer For Fancy.

    Fancy is a self-hosted, pure object-oriented, dynamic,
    class-based, concurrent general-purpose programming language
    running on Rubinius, the Ruby VM.

    .. versionadded:: 1.5
    """

    name = "Fancy"
    url = "https://github.com/bakkdoor/fancy"
    filenames = ["*.fy", "*.fancypack"]
    aliases = ["fancy", "fy"]
    mimetypes = ["text/x-fancysrc"]

    tokens = {
        # copied from PerlLexer:
        "balanced-regex": [
            (r"/(\\\\|\\[^\\]|[^/\\])*/[egimosx]*", String.Regex, "#pop"),
            (r"!(\\\\|\\[^\\]|[^!\\])*![egimosx]*", String.Regex, "#pop"),
            (r"\\(\\\\|[^\\])*\\[egimosx]*", String.Regex, "#pop"),
            (r"\{(\\\\|\\[^\\]|[^}\\])*\}[egimosx]*", String.Regex, "#pop"),
            (r"<(\\\\|\\[^\\]|[^>\\])*>[egimosx]*", String.Regex, "#pop"),
            (r"\[(\\\\|\\[^\\]|[^\]\\])*\][egimosx]*", String.Regex, "#pop"),
            (r"\((\\\\|\\[^\\]|[^)\\])*\)[egimosx]*", String.Regex, "#pop"),
            (r"@(\\\\|\\[^\\]|[^@\\])*@[egimosx]*", String.Regex, "#pop"),
            (r"%(\\\\|\\[^\\]|[^%\\])*%[egimosx]*", String.Regex, "#pop"),
            (r"\$(\\\\|\\[^\\]|[^$\\])*\$[egimosx]*", String.Regex, "#pop"),
        ],
        "root": [
            (r"\s+", Whitespace),
            # balanced delimiters (copied from PerlLexer):
            (r"s\{(\\\\|\\[^\\]|[^}\\])*\}\s*", String.Regex, "balanced-regex"),
            (r"s<(\\\\|\\[^\\]|[^>\\])*>\s*", String.Regex, "balanced-regex"),
            (r"s\[(\\\\|\\[^\\]|[^\]\\])*\]\s*", String.Regex, "balanced-regex"),
            (r"s\((\\\\|\\[^\\]|[^)\\])*\)\s*", String.Regex, "balanced-regex"),
            (r"m?/(\\\\|\\[^\\]|[^///\n])*/[gcimosx]*", String.Regex),
            (r"m(?=[/!\\{<\[(@%$])", String.Regex, "balanced-regex"),
            # Comments
            (r"#(.*?)\n", Comment.Single),
            # Symbols
            (r"\'([^\'\s\[\](){}]+|\[\])", String.Symbol),
            # Multi-line DoubleQuotedString
            (r'"""(\\\\|\\[^\\]|[^\\])*?"""', String),
            # DoubleQuotedString
            (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
            # keywords
            (r"(def|class|try|catch|finally|retry|return|return_local|match|" r"case|->|=>)\b", Keyword),
            # constants
            (r"(self|super|nil|false|true)\b", Name.Constant),
            (r"[(){};,/?|:\\]", Punctuation),
            # names
            (
                words(
                    (
                        "Object",
                        "Array",
                        "Hash",
                        "Directory",
                        "File",
                        "Class",
                        "String",
                        "Number",
                        "Enumerable",
                        "FancyEnumerable",
                        "Block",
                        "TrueClass",
                        "NilClass",
                        "FalseClass",
                        "Tuple",
                        "Symbol",
                        "Stack",
                        "Set",
                        "FancySpec",
                        "Method",
                        "Package",
                        "Range",
                    ),
                    suffix=r"\b",
                ),
                Name.Builtin,
            ),
            # functions
            (r"[a-zA-Z](\w|[-+?!=*/^><%])*:", Name.Function),
            # operators, must be below functions
            (r"[-+*/~,<>=&!?%^\[\].$]+", Operator),
            (r"[A-Z]\w*", Name.Constant),
            (r"@[a-zA-Z_]\w*", Name.Variable.Instance),
            (r"@@[a-zA-Z_]\w*", Name.Variable.Class),
            ("@@?", Operator),
            (r"[a-zA-Z_]\w*", Name),
            # numbers - / checks are necessary to avoid mismarking regexes,
            # see comment in RubyLexer
            (r"(0[oO]?[0-7]+(?:_[0-7]+)*)(\s*)([/?])?", bygroups(Number.Oct, Whitespace, Operator)),
            (r"(0[xX][0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)([/?])?", bygroups(Number.Hex, Whitespace, Operator)),
            (r"(0[bB][01]+(?:_[01]+)*)(\s*)([/?])?", bygroups(Number.Bin, Whitespace, Operator)),
            (r"([\d]+(?:_\d+)*)(\s*)([/?])?", bygroups(Number.Integer, Whitespace, Operator)),
            (r"\d+([eE][+-]?[0-9]+)|\d+\.\d+([eE][+-]?[0-9]+)?", Number.Float),
            (r"\d+", Number.Integer),
        ],
    }
