ruby.py 22 KB

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