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