ruby.py 22 KB

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