raldred-coderay 0.9.0 → 0.9.339

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. data/lib/README +128 -0
  2. data/lib/coderay.rb +319 -0
  3. data/lib/coderay/duo.rb +85 -0
  4. data/lib/coderay/encoder.rb +187 -0
  5. data/lib/coderay/encoders/_map.rb +9 -0
  6. data/lib/coderay/encoders/count.rb +21 -0
  7. data/lib/coderay/encoders/debug.rb +49 -0
  8. data/lib/coderay/encoders/div.rb +20 -0
  9. data/lib/coderay/encoders/html.rb +306 -0
  10. data/lib/coderay/encoders/html/css.rb +70 -0
  11. data/lib/coderay/encoders/html/numerization.rb +133 -0
  12. data/lib/coderay/encoders/html/output.rb +206 -0
  13. data/lib/coderay/encoders/json.rb +19 -0
  14. data/lib/coderay/encoders/null.rb +26 -0
  15. data/lib/coderay/encoders/page.rb +21 -0
  16. data/lib/coderay/encoders/span.rb +20 -0
  17. data/lib/coderay/encoders/statistic.rb +77 -0
  18. data/lib/coderay/encoders/term.rb +114 -0
  19. data/lib/coderay/encoders/text.rb +32 -0
  20. data/lib/coderay/encoders/tokens.rb +44 -0
  21. data/lib/coderay/encoders/xml.rb +71 -0
  22. data/lib/coderay/encoders/yaml.rb +22 -0
  23. data/lib/coderay/for_redcloth.rb +73 -0
  24. data/lib/coderay/helpers/file_type.rb +226 -0
  25. data/lib/coderay/helpers/gzip_simple.rb +123 -0
  26. data/lib/coderay/helpers/plugin.rb +339 -0
  27. data/lib/coderay/helpers/word_list.rb +124 -0
  28. data/lib/coderay/scanner.rb +271 -0
  29. data/lib/coderay/scanners/_map.rb +21 -0
  30. data/lib/coderay/scanners/c.rb +166 -0
  31. data/lib/coderay/scanners/css.rb +202 -0
  32. data/lib/coderay/scanners/debug.rb +61 -0
  33. data/lib/coderay/scanners/delphi.rb +150 -0
  34. data/lib/coderay/scanners/diff.rb +104 -0
  35. data/lib/coderay/scanners/groovy.rb +271 -0
  36. data/lib/coderay/scanners/html.rb +175 -0
  37. data/lib/coderay/scanners/java.rb +173 -0
  38. data/lib/coderay/scanners/java/builtin_types.rb +419 -0
  39. data/lib/coderay/scanners/java_script.rb +195 -0
  40. data/lib/coderay/scanners/json.rb +107 -0
  41. data/lib/coderay/scanners/nitro_xhtml.rb +132 -0
  42. data/lib/coderay/scanners/php.rb +404 -0
  43. data/lib/coderay/scanners/plaintext.rb +18 -0
  44. data/lib/coderay/scanners/python.rb +232 -0
  45. data/lib/coderay/scanners/rhtml.rb +71 -0
  46. data/lib/coderay/scanners/ruby.rb +386 -0
  47. data/lib/coderay/scanners/ruby/patterns.rb +232 -0
  48. data/lib/coderay/scanners/scheme.rb +142 -0
  49. data/lib/coderay/scanners/sql.rb +162 -0
  50. data/lib/coderay/scanners/xml.rb +17 -0
  51. data/lib/coderay/scanners/yaml.rb +142 -0
  52. data/lib/coderay/style.rb +20 -0
  53. data/lib/coderay/styles/_map.rb +7 -0
  54. data/lib/coderay/styles/cycnus.rb +151 -0
  55. data/lib/coderay/styles/murphy.rb +132 -0
  56. data/lib/coderay/token_classes.rb +86 -0
  57. data/lib/coderay/tokens.rb +387 -0
  58. metadata +59 -1
@@ -0,0 +1,18 @@
1
+ module CodeRay
2
+ module Scanners
3
+
4
+ class Plaintext < Scanner
5
+
6
+ register_for :plaintext, :plain
7
+
8
+ include Streamable
9
+
10
+ def scan_tokens tokens, options
11
+ text = (scan_until(/\z/) || '')
12
+ tokens << [text, :plain]
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,232 @@
1
+ module CodeRay
2
+ module Scanners
3
+
4
+ # Bases on pygments' PythonLexer, see
5
+ # http://dev.pocoo.org/projects/pygments/browser/pygments/lexers/agile.py.
6
+ class Python < Scanner
7
+
8
+ include Streamable
9
+
10
+ register_for :python
11
+ file_extension 'py'
12
+
13
+ KEYWORDS = [
14
+ 'and', 'as', 'assert', 'break', 'class', 'continue', 'def',
15
+ 'del', 'elif', 'else', 'except', 'finally', 'for',
16
+ 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not',
17
+ 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield',
18
+ 'nonlocal', # new in Python 3
19
+ ]
20
+
21
+ OLD_KEYWORDS = [
22
+ 'exec', 'print', # gone in Python 3
23
+ ]
24
+
25
+ PREDEFINED_METHODS_AND_TYPES = %w[
26
+ __import__ abs all any apply basestring bin bool buffer
27
+ bytearray bytes callable chr classmethod cmp coerce compile
28
+ complex delattr dict dir divmod enumerate eval execfile exit
29
+ file filter float frozenset getattr globals hasattr hash hex id
30
+ input int intern isinstance issubclass iter len list locals
31
+ long map max min next object oct open ord pow property range
32
+ raw_input reduce reload repr reversed round set setattr slice
33
+ sorted staticmethod str sum super tuple type unichr unicode
34
+ vars xrange zip
35
+ ]
36
+
37
+ PREDEFINED_EXCEPTIONS = %w[
38
+ ArithmeticError AssertionError AttributeError
39
+ BaseException DeprecationWarning EOFError EnvironmentError
40
+ Exception FloatingPointError FutureWarning GeneratorExit IOError
41
+ ImportError ImportWarning IndentationError IndexError KeyError
42
+ KeyboardInterrupt LookupError MemoryError NameError
43
+ NotImplemented NotImplementedError OSError OverflowError
44
+ OverflowWarning PendingDeprecationWarning ReferenceError
45
+ RuntimeError RuntimeWarning StandardError StopIteration
46
+ SyntaxError SyntaxWarning SystemError SystemExit TabError
47
+ TypeError UnboundLocalError UnicodeDecodeError
48
+ UnicodeEncodeError UnicodeError UnicodeTranslateError
49
+ UnicodeWarning UserWarning ValueError Warning ZeroDivisionError
50
+ ]
51
+
52
+ PREDEFINED_VARIABLES_AND_CONSTANTS = [
53
+ 'False', 'True', 'None', # "keywords" since Python 3
54
+ 'self', 'Ellipsis', 'NotImplemented',
55
+ ]
56
+
57
+ IDENT_KIND = WordList.new(:ident).
58
+ add(KEYWORDS, :keyword).
59
+ add(OLD_KEYWORDS, :old_keyword).
60
+ add(PREDEFINED_METHODS_AND_TYPES, :predefined).
61
+ add(PREDEFINED_VARIABLES_AND_CONSTANTS, :pre_constant).
62
+ add(PREDEFINED_EXCEPTIONS, :exception)
63
+
64
+ ESCAPE = / [abfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
65
+ UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} | N\{[-\w ]+\} /x
66
+
67
+ OPERATOR = /
68
+ \.\.\. | # ellipsis
69
+ \.(?!\d) | # dot but not decimal point
70
+ [,;:()\[\]{}] | # simple delimiters
71
+ \/\/=? | \*\*=? | # special math
72
+ [-+*\/%&|^]=? | # ordinary math and binary logic
73
+ ~ | # binary complement
74
+ <<=? | >>=? | [<>=]=? | != # comparison and assignment
75
+ /x
76
+
77
+ STRING_DELIMITER_REGEXP = Hash.new do |h, delimiter|
78
+ h[delimiter] = Regexp.union delimiter
79
+ end
80
+
81
+ STRING_CONTENT_REGEXP = Hash.new do |h, delimiter|
82
+ h[delimiter] = / [^\\\n]+? (?= \\ | $ | #{Regexp.escape(delimiter)} ) /x
83
+ end
84
+
85
+ def scan_tokens tokens, options
86
+
87
+ state = :initial
88
+ string_delimiter = nil
89
+ import_clause = class_name_follows = last_token_dot = false
90
+ unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8'
91
+
92
+ until eos?
93
+
94
+ kind = nil
95
+ match = nil
96
+
97
+ case state
98
+
99
+ when :initial
100
+
101
+ if match = scan(/ [ \t]+ | \\?\n /x)
102
+ tokens << [match, :space]
103
+ next
104
+
105
+ elsif match = scan(/ \# [^\n]* /mx)
106
+ tokens << [match, :comment]
107
+ next
108
+
109
+ elsif scan(/#{OPERATOR}/o)
110
+ kind = :operator
111
+
112
+ elsif match = scan(/(u?r?|b)?("""|"|'''|')/i)
113
+ tokens << [:open, :string]
114
+ string_delimiter = self[2]
115
+ string_raw = false
116
+ modifiers = self[1]
117
+ unless modifiers.empty?
118
+ string_raw = !!modifiers.index(?r)
119
+ tokens << [modifiers, :modifier]
120
+ match = string_delimiter
121
+ end
122
+ state = :string
123
+ kind = :delimiter
124
+
125
+ elsif match = (unicode && scan(/[[:alpha:]_]\w*/ux)) ||
126
+ scan(/[[:alpha:]_]\w*/x)
127
+ kind = IDENT_KIND[match]
128
+ # TODO: handle class, def, from, import
129
+ # TODO: keyword arguments
130
+ kind = :ident if last_token_dot
131
+ kind = check(/\(/) ? :ident : :keyword if kind == :old_keyword
132
+
133
+ elsif scan(/@[a-zA-Z0-9_.]+[lL]?/)
134
+ kind = :decorator
135
+
136
+ elsif scan(/0[xX][0-9A-Fa-f]+[lL]?/)
137
+ kind = :hex
138
+
139
+ elsif scan(/0[bB][01]+[lL]?/)
140
+ kind = :bin
141
+
142
+ elsif match = scan(/(?:\d*\.\d+|\d+\.\d*)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/)
143
+ kind = :float
144
+ if scan(/[jJ]/)
145
+ match << matched
146
+ kind = :imaginary
147
+ end
148
+
149
+ elsif scan(/0[oO][0-7]+|0[0-7]+(?![89.eE])[lL]?/)
150
+ kind = :oct
151
+
152
+ elsif match = scan(/\d+([lL])?/)
153
+ kind = :integer
154
+ if self[1] == nil && scan(/[jJ]/)
155
+ match << matched
156
+ kind = :imaginary
157
+ end
158
+
159
+ else
160
+ getch
161
+ kind = :error
162
+
163
+ end
164
+
165
+ when :string
166
+ if scan(STRING_DELIMITER_REGEXP[string_delimiter])
167
+ tokens << [matched, :delimiter]
168
+ tokens << [:close, :string]
169
+ state = :initial
170
+ next
171
+ elsif string_delimiter.size == 3 && scan(/\n/)
172
+ kind = :content
173
+ elsif scan(STRING_CONTENT_REGEXP[string_delimiter])
174
+ kind = :content
175
+ elsif !string_raw && scan(/ \\ #{ESCAPE} /ox)
176
+ kind = :char
177
+ elsif scan(/ \\ #{UNICODE_ESCAPE} /ox)
178
+ kind = :char
179
+ elsif scan(/ \\ . /x)
180
+ kind = :content
181
+ elsif scan(/ \\ | $ /x)
182
+ tokens << [:close, :string]
183
+ kind = :error
184
+ state = :initial
185
+ else
186
+ raise_inspect "else case \" reached; %p not handled." % peek(1), tokens, state
187
+ end
188
+
189
+ when :include_expected
190
+ if scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/)
191
+ kind = :include
192
+ state = :initial
193
+
194
+ elsif match = scan(/\s+/)
195
+ kind = :space
196
+ state = :initial if match.index ?\n
197
+
198
+ else
199
+ getch
200
+ kind = :error
201
+
202
+ end
203
+
204
+ else
205
+ raise_inspect 'Unknown state', tokens, state
206
+
207
+ end
208
+
209
+ match ||= matched
210
+ if $DEBUG and not kind
211
+ raise_inspect 'Error token %p in line %d' %
212
+ [[match, kind], line], tokens, state
213
+ end
214
+ raise_inspect 'Empty token', tokens, state unless match
215
+
216
+ last_token_dot = match == '.'
217
+
218
+ tokens << [match, kind]
219
+
220
+ end
221
+
222
+ if state == :string
223
+ tokens << [:close, :string]
224
+ end
225
+
226
+ tokens
227
+ end
228
+
229
+ end
230
+
231
+ end
232
+ end
@@ -0,0 +1,71 @@
1
+ module CodeRay
2
+ module Scanners
3
+
4
+ load :html
5
+ load :ruby
6
+
7
+ # RHTML Scanner
8
+ class RHTML < Scanner
9
+
10
+ include Streamable
11
+ register_for :rhtml
12
+
13
+ ERB_RUBY_BLOCK = /
14
+ <%(?!%)[=-]?
15
+ (?>
16
+ [^\-%]* # normal*
17
+ (?> # special
18
+ (?: %(?!>) | -(?!%>) )
19
+ [^\-%]* # normal*
20
+ )*
21
+ )
22
+ (?: -?%> )?
23
+ /x
24
+
25
+ START_OF_ERB = /
26
+ <%(?!%)
27
+ /x
28
+
29
+ private
30
+
31
+ def setup
32
+ @ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true
33
+ @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true
34
+ end
35
+
36
+ def reset_instance
37
+ super
38
+ @html_scanner.reset
39
+ end
40
+
41
+ def scan_tokens tokens, options
42
+
43
+ until eos?
44
+
45
+ if (match = scan_until(/(?=#{START_OF_ERB})/o) || scan_until(/\z/)) and not match.empty?
46
+ @html_scanner.tokenize match
47
+
48
+ elsif match = scan(/#{ERB_RUBY_BLOCK}/o)
49
+ start_tag = match[/\A<%[-=]?/]
50
+ end_tag = match[/-?%?>?\z/]
51
+ tokens << [:open, :inline]
52
+ tokens << [start_tag, :inline_delimiter]
53
+ code = match[start_tag.size .. -1 - end_tag.size]
54
+ @ruby_scanner.tokenize code
55
+ tokens << [end_tag, :inline_delimiter] unless end_tag.empty?
56
+ tokens << [:close, :inline]
57
+
58
+ else
59
+ raise_inspect 'else-case reached!', tokens
60
+ end
61
+
62
+ end
63
+
64
+ tokens
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,386 @@
1
+ module CodeRay
2
+ module Scanners
3
+
4
+ # This scanner is really complex, since Ruby _is_ a complex language!
5
+ #
6
+ # It tries to highlight 100% of all common code,
7
+ # and 90% of strange codes.
8
+ #
9
+ # It is optimized for HTML highlighting, and is not very useful for
10
+ # parsing or pretty printing.
11
+ #
12
+ # For now, I think it's better than the scanners in VIM or Syntax, or
13
+ # any highlighter I was able to find, except Caleb's RubyLexer.
14
+ #
15
+ # I hope it's also better than the rdoc/irb lexer.
16
+ class Ruby < Scanner
17
+
18
+ include Streamable
19
+
20
+ register_for :ruby
21
+ file_extension 'rb'
22
+
23
+ helper :patterns
24
+
25
+ private
26
+ def scan_tokens tokens, options
27
+ last_token_dot = false
28
+ value_expected = true
29
+ heredocs = nil
30
+ last_state = nil
31
+ state = :initial
32
+ depth = nil
33
+ inline_block_stack = []
34
+
35
+ patterns = Patterns # avoid constant lookup
36
+
37
+ until eos?
38
+ match = nil
39
+ kind = nil
40
+
41
+ if state.instance_of? patterns::StringState
42
+ # {{{
43
+ match = scan_until(state.pattern) || scan_until(/\z/)
44
+ tokens << [match, :content] unless match.empty?
45
+ break if eos?
46
+
47
+ if state.heredoc and self[1] # end of heredoc
48
+ match = getch.to_s
49
+ match << scan_until(/$/) unless eos?
50
+ tokens << [match, :delimiter]
51
+ tokens << [:close, state.type]
52
+ state = state.next_state
53
+ next
54
+ end
55
+
56
+ case match = getch
57
+
58
+ when state.delim
59
+ if state.paren
60
+ state.paren_depth -= 1
61
+ if state.paren_depth > 0
62
+ tokens << [match, :nesting_delimiter]
63
+ next
64
+ end
65
+ end
66
+ tokens << [match, :delimiter]
67
+ if state.type == :regexp and not eos?
68
+ modifiers = scan(/#{patterns::REGEXP_MODIFIERS}/ox)
69
+ tokens << [modifiers, :modifier] unless modifiers.empty?
70
+ end
71
+ tokens << [:close, state.type]
72
+ value_expected = false
73
+ state = state.next_state
74
+
75
+ when '\\'
76
+ if state.interpreted
77
+ if esc = scan(/ #{patterns::ESCAPE} /ox)
78
+ tokens << [match + esc, :char]
79
+ else
80
+ tokens << [match, :error]
81
+ end
82
+ else
83
+ case m = getch
84
+ when state.delim, '\\'
85
+ tokens << [match + m, :char]
86
+ when nil
87
+ tokens << [match, :error]
88
+ else
89
+ tokens << [match + m, :content]
90
+ end
91
+ end
92
+
93
+ when '#'
94
+ case peek(1)
95
+ when '{'
96
+ inline_block_stack << [state, depth, heredocs]
97
+ value_expected = true
98
+ state = :initial
99
+ depth = 1
100
+ tokens << [:open, :inline]
101
+ tokens << [match + getch, :inline_delimiter]
102
+ when '$', '@'
103
+ tokens << [match, :escape]
104
+ last_state = state # scan one token as normal code, then return here
105
+ state = :initial
106
+ else
107
+ raise_inspect 'else-case # reached; #%p not handled' % peek(1), tokens
108
+ end
109
+
110
+ when state.paren
111
+ state.paren_depth += 1
112
+ tokens << [match, :nesting_delimiter]
113
+
114
+ when /#{patterns::REGEXP_SYMBOLS}/ox
115
+ tokens << [match, :function]
116
+
117
+ else
118
+ raise_inspect 'else-case " reached; %p not handled, state = %p' % [match, state], tokens
119
+
120
+ end
121
+ next
122
+ # }}}
123
+ else
124
+ # {{{
125
+ if match = scan(/[ \t\f]+/)
126
+ kind = :space
127
+ match << scan(/\s*/) unless eos? || heredocs
128
+ value_expected = true if match.index(?\n) # FIXME not quite true
129
+ tokens << [match, kind]
130
+ next
131
+
132
+ elsif match = scan(/\\?\n/)
133
+ kind = :space
134
+ if match == "\n"
135
+ value_expected = true # FIXME not quite true
136
+ state = :initial if state == :undef_comma_expected
137
+ end
138
+ if heredocs
139
+ unscan # heredoc scanning needs \n at start
140
+ state = heredocs.shift
141
+ tokens << [:open, state.type]
142
+ heredocs = nil if heredocs.empty?
143
+ next
144
+ else
145
+ match << scan(/\s*/) unless eos?
146
+ end
147
+ tokens << [match, kind]
148
+ next
149
+
150
+ elsif bol? && match = scan(/\#!.*/)
151
+ tokens << [match, :doctype]
152
+ next
153
+
154
+ elsif match = scan(/\#.*/) or
155
+ ( bol? and match = scan(/#{patterns::RUBYDOC_OR_DATA}/o) )
156
+ kind = :comment
157
+ value_expected = true
158
+ tokens << [match, kind]
159
+ next
160
+
161
+ elsif state == :initial
162
+
163
+ # IDENTS #
164
+ if match = scan(/#{patterns::METHOD_NAME}/o)
165
+ if last_token_dot
166
+ kind = if match[/^[A-Z]/] and not match?(/\(/) then :constant else :ident end
167
+ else
168
+ kind = patterns::IDENT_KIND[match]
169
+ if kind == :ident and match[/^[A-Z]/] and not match[/[!?]$/] and not match?(/\(/)
170
+ kind = :constant
171
+ elsif kind == :reserved
172
+ state = patterns::DEF_NEW_STATE[match]
173
+ end
174
+ end
175
+ ## experimental!
176
+ value_expected = :set if check(/#{patterns::VALUE_FOLLOWS}/o)
177
+
178
+ elsif last_token_dot and match = scan(/#{patterns::METHOD_NAME_OPERATOR}/o)
179
+ kind = :ident
180
+ value_expected = :set if check(/#{patterns::VALUE_FOLLOWS}/o)
181
+
182
+ # OPERATORS #
183
+ # TODO: match (), [], {} as one single operator
184
+ elsif not last_token_dot and match = scan(/ \.\.\.? | (?:\.|::)() | [,\(\)\[\]\{\}] | ==?=? /x)
185
+ if match !~ / [.\)\]\}] /x or match =~ /\.\.\.?/
186
+ value_expected = :set
187
+ end
188
+ last_token_dot = :set if self[1]
189
+ kind = :operator
190
+ unless inline_block_stack.empty?
191
+ case match
192
+ when '{'
193
+ depth += 1
194
+ when '}'
195
+ depth -= 1
196
+ if depth == 0 # closing brace of inline block reached
197
+ state, depth, heredocs = inline_block_stack.pop
198
+ heredocs = nil if heredocs && heredocs.empty?
199
+ tokens << [match, :inline_delimiter]
200
+ kind = :inline
201
+ match = :close
202
+ end
203
+ end
204
+ end
205
+
206
+ elsif match = scan(/ ['"] /mx)
207
+ tokens << [:open, :string]
208
+ kind = :delimiter
209
+ state = patterns::StringState.new :string, match == '"', match # important for streaming
210
+
211
+ elsif match = scan(/#{patterns::INSTANCE_VARIABLE}/o)
212
+ kind = :instance_variable
213
+
214
+ elsif value_expected and match = scan(/\//)
215
+ tokens << [:open, :regexp]
216
+ kind = :delimiter
217
+ interpreted = true
218
+ state = patterns::StringState.new :regexp, interpreted, match
219
+
220
+ # elsif match = scan(/[-+]?#{patterns::NUMERIC}/o)
221
+ elsif match = value_expected ? scan(/[-+]?#{patterns::NUMERIC}/o) : scan(/#{patterns::NUMERIC}/o)
222
+ kind = self[1] ? :float : :integer
223
+
224
+ elsif match = scan(/#{patterns::SYMBOL}/o)
225
+ case delim = match[1]
226
+ when ?', ?"
227
+ tokens << [:open, :symbol]
228
+ tokens << [':', :symbol]
229
+ match = delim.chr
230
+ kind = :delimiter
231
+ state = patterns::StringState.new :symbol, delim == ?", match
232
+ else
233
+ kind = :symbol
234
+ end
235
+
236
+ elsif match = scan(/ [-+!~^]=? | [*|&]{1,2}=? | >>? /x)
237
+ value_expected = :set
238
+ kind = :operator
239
+
240
+ elsif value_expected and match = scan(/#{patterns::HEREDOC_OPEN}/o)
241
+ indented = self[1] == '-'
242
+ quote = self[3]
243
+ delim = self[quote ? 4 : 2]
244
+ kind = patterns::QUOTE_TO_TYPE[quote]
245
+ tokens << [:open, kind]
246
+ tokens << [match, :delimiter]
247
+ match = :close
248
+ heredoc = patterns::StringState.new kind, quote != '\'', delim, (indented ? :indented : :linestart )
249
+ heredocs ||= [] # create heredocs if empty
250
+ heredocs << heredoc
251
+
252
+ elsif value_expected and match = scan(/#{patterns::FANCY_START_CORRECT}/o)
253
+ kind, interpreted = *patterns::FancyStringType.fetch(self[1]) do
254
+ raise_inspect 'Unknown fancy string: %%%p' % k, tokens
255
+ end
256
+ tokens << [:open, kind]
257
+ state = patterns::StringState.new kind, interpreted, self[2]
258
+ kind = :delimiter
259
+
260
+ elsif value_expected and match = scan(/#{patterns::CHARACTER}/o)
261
+ kind = :integer
262
+
263
+ elsif match = scan(/ [\/%]=? | <(?:<|=>?)? | [?:;] /x)
264
+ value_expected = :set
265
+ kind = :operator
266
+
267
+ elsif match = scan(/`/)
268
+ if last_token_dot
269
+ kind = :operator
270
+ else
271
+ tokens << [:open, :shell]
272
+ kind = :delimiter
273
+ state = patterns::StringState.new :shell, true, match
274
+ end
275
+
276
+ elsif match = scan(/#{patterns::GLOBAL_VARIABLE}/o)
277
+ kind = :global_variable
278
+
279
+ elsif match = scan(/#{patterns::CLASS_VARIABLE}/o)
280
+ kind = :class_variable
281
+
282
+ else
283
+ kind = :error
284
+ match = getch
285
+
286
+ end
287
+
288
+ elsif state == :def_expected
289
+ state = :initial
290
+ if match = scan(/(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o)
291
+ kind = :method
292
+ else
293
+ next
294
+ end
295
+
296
+ elsif state == :module_expected
297
+ if match = scan(/<</)
298
+ kind = :operator
299
+ else
300
+ state = :initial
301
+ if match = scan(/ (?:#{patterns::IDENT}::)* #{patterns::IDENT} /ox)
302
+ kind = :class
303
+ else
304
+ next
305
+ end
306
+ end
307
+
308
+ elsif state == :undef_expected
309
+ state = :undef_comma_expected
310
+ if match = scan(/#{patterns::METHOD_NAME_EX}/o)
311
+ kind = :method
312
+ elsif match = scan(/#{patterns::SYMBOL}/o)
313
+ case delim = match[1]
314
+ when ?', ?"
315
+ tokens << [:open, :symbol]
316
+ tokens << [':', :symbol]
317
+ match = delim.chr
318
+ kind = :delimiter
319
+ state = patterns::StringState.new :symbol, delim == ?", match
320
+ state.next_state = :undef_comma_expected
321
+ else
322
+ kind = :symbol
323
+ end
324
+ else
325
+ state = :initial
326
+ next
327
+ end
328
+
329
+ elsif state == :alias_expected
330
+ if match = scan(/(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/o)
331
+ tokens << [self[1], (self[1][0] == ?: ? :symbol : :method)]
332
+ tokens << [self[2], :space]
333
+ tokens << [self[3], (self[3][0] == ?: ? :symbol : :method)]
334
+ end
335
+ state = :initial
336
+ next
337
+
338
+ elsif state == :undef_comma_expected
339
+ if match = scan(/,/)
340
+ kind = :operator
341
+ state = :undef_expected
342
+ else
343
+ state = :initial
344
+ next
345
+ end
346
+
347
+ end
348
+ # }}}
349
+
350
+ unless kind == :error
351
+ value_expected = value_expected == :set
352
+ last_token_dot = last_token_dot == :set
353
+ end
354
+
355
+ if $DEBUG and not kind
356
+ raise_inspect 'Error token %p in line %d' %
357
+ [[match, kind], line], tokens, state
358
+ end
359
+ raise_inspect 'Empty token', tokens unless match
360
+
361
+ tokens << [match, kind]
362
+
363
+ if last_state
364
+ state = last_state
365
+ last_state = nil
366
+ end
367
+ end
368
+ end
369
+
370
+ inline_block_stack << [state] if state.is_a? patterns::StringState
371
+ until inline_block_stack.empty?
372
+ this_block = inline_block_stack.pop
373
+ tokens << [:close, :inline] if this_block.size > 1
374
+ state = this_block.first
375
+ tokens << [:close, state.type]
376
+ end
377
+
378
+ tokens
379
+ end
380
+
381
+ end
382
+
383
+ end
384
+ end
385
+
386
+ # vim:fdm=marker