ruby.py 22 KB

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