Mercurial > hg > soundsoftware-site
comparison vendor/gems/coderay-1.0.0/lib/coderay/scanners/groovy.rb @ 909:cbb26bc654de redmine-1.3
Update to Redmine 1.3-stable branch (Redmine SVN rev 8964)
author | Chris Cannam |
---|---|
date | Fri, 24 Feb 2012 19:09:32 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
908:c6c2cbd0afee | 909:cbb26bc654de |
---|---|
1 module CodeRay | |
2 module Scanners | |
3 | |
4 load :java | |
5 | |
6 # Scanner for Groovy. | |
7 class Groovy < Java | |
8 | |
9 register_for :groovy | |
10 | |
11 # TODO: check list of keywords | |
12 GROOVY_KEYWORDS = %w[ | |
13 as assert def in | |
14 ] # :nodoc: | |
15 KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[ | |
16 case instanceof new return throw typeof while as assert in | |
17 ] # :nodoc: | |
18 GROOVY_MAGIC_VARIABLES = %w[ it ] # :nodoc: | |
19 | |
20 IDENT_KIND = Java::IDENT_KIND.dup. | |
21 add(GROOVY_KEYWORDS, :keyword). | |
22 add(GROOVY_MAGIC_VARIABLES, :local_variable) # :nodoc: | |
23 | |
24 ESCAPE = / [bfnrtv$\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: | |
25 UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc: no 4-byte unicode chars? U[a-fA-F0-9]{8} | |
26 REGEXP_ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | \d | [bBdDsSwW\/] /x # :nodoc: | |
27 | |
28 # TODO: interpretation inside ', ", / | |
29 STRING_CONTENT_PATTERN = { | |
30 "'" => /(?>\\[^\\'\n]+|[^\\'\n]+)+/, | |
31 '"' => /[^\\$"\n]+/, | |
32 "'''" => /(?>[^\\']+|'(?!''))+/, | |
33 '"""' => /(?>[^\\$"]+|"(?!""))+/, | |
34 '/' => /[^\\$\/\n]+/, | |
35 } # :nodoc: | |
36 | |
37 protected | |
38 | |
39 def scan_tokens encoder, options | |
40 | |
41 state = :initial | |
42 inline_block_stack = [] | |
43 inline_block_paren_depth = nil | |
44 string_delimiter = nil | |
45 import_clause = class_name_follows = last_token = after_def = false | |
46 value_expected = true | |
47 | |
48 until eos? | |
49 | |
50 case state | |
51 | |
52 when :initial | |
53 | |
54 if match = scan(/ \s+ | \\\n /x) | |
55 encoder.text_token match, :space | |
56 if match.index ?\n | |
57 import_clause = after_def = false | |
58 value_expected = true unless value_expected | |
59 end | |
60 next | |
61 | |
62 elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) | |
63 value_expected = true | |
64 after_def = false | |
65 encoder.text_token match, :comment | |
66 | |
67 elsif bol? && match = scan(/ \#!.* /x) | |
68 encoder.text_token match, :doctype | |
69 | |
70 elsif import_clause && match = scan(/ (?!as) #{IDENT} (?: \. #{IDENT} )* (?: \.\* )? /ox) | |
71 after_def = value_expected = false | |
72 encoder.text_token match, :include | |
73 | |
74 elsif match = scan(/ #{IDENT} | \[\] /ox) | |
75 kind = IDENT_KIND[match] | |
76 value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match] | |
77 if last_token == '.' | |
78 kind = :ident | |
79 elsif class_name_follows | |
80 kind = :class | |
81 class_name_follows = false | |
82 elsif after_def && check(/\s*[({]/) | |
83 kind = :method | |
84 after_def = false | |
85 elsif kind == :ident && last_token != '?' && check(/:/) | |
86 kind = :key | |
87 else | |
88 class_name_follows = true if match == 'class' || (import_clause && match == 'as') | |
89 import_clause = match == 'import' | |
90 after_def = true if match == 'def' | |
91 end | |
92 encoder.text_token match, kind | |
93 | |
94 elsif match = scan(/;/) | |
95 import_clause = after_def = false | |
96 value_expected = true | |
97 encoder.text_token match, :operator | |
98 | |
99 elsif match = scan(/\{/) | |
100 class_name_follows = after_def = false | |
101 value_expected = true | |
102 encoder.text_token match, :operator | |
103 if !inline_block_stack.empty? | |
104 inline_block_paren_depth += 1 | |
105 end | |
106 | |
107 # TODO: ~'...', ~"..." and ~/.../ style regexps | |
108 elsif match = scan(/ \.\.<? | \*?\.(?!\d)@? | \.& | \?:? | [,?:(\[] | -[->] | \+\+ | | |
109 && | \|\| | \*\*=? | ==?~ | <=?>? | [-+*%^~&|>=!]=? | <<<?=? | >>>?=? /x) | |
110 value_expected = true | |
111 value_expected = :regexp if match == '~' | |
112 after_def = false | |
113 encoder.text_token match, :operator | |
114 | |
115 elsif match = scan(/ [)\]}] /x) | |
116 value_expected = after_def = false | |
117 if !inline_block_stack.empty? && match == '}' | |
118 inline_block_paren_depth -= 1 | |
119 if inline_block_paren_depth == 0 # closing brace of inline block reached | |
120 encoder.text_token match, :inline_delimiter | |
121 encoder.end_group :inline | |
122 state, string_delimiter, inline_block_paren_depth = inline_block_stack.pop | |
123 next | |
124 end | |
125 end | |
126 encoder.text_token match, :operator | |
127 | |
128 elsif check(/[\d.]/) | |
129 after_def = value_expected = false | |
130 if match = scan(/0[xX][0-9A-Fa-f]+/) | |
131 encoder.text_token match, :hex | |
132 elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/) | |
133 encoder.text_token match, :octal | |
134 elsif match = scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/) | |
135 encoder.text_token match, :float | |
136 elsif match = scan(/\d+[lLgG]?/) | |
137 encoder.text_token match, :integer | |
138 end | |
139 | |
140 elsif match = scan(/'''|"""/) | |
141 after_def = value_expected = false | |
142 state = :multiline_string | |
143 encoder.begin_group :string | |
144 string_delimiter = match | |
145 encoder.text_token match, :delimiter | |
146 | |
147 # TODO: record.'name' syntax | |
148 elsif match = scan(/["']/) | |
149 after_def = value_expected = false | |
150 state = match == '/' ? :regexp : :string | |
151 encoder.begin_group state | |
152 string_delimiter = match | |
153 encoder.text_token match, :delimiter | |
154 | |
155 elsif value_expected && match = scan(/\//) | |
156 after_def = value_expected = false | |
157 encoder.begin_group :regexp | |
158 state = :regexp | |
159 string_delimiter = '/' | |
160 encoder.text_token match, :delimiter | |
161 | |
162 elsif match = scan(/ @ #{IDENT} /ox) | |
163 after_def = value_expected = false | |
164 encoder.text_token match, :annotation | |
165 | |
166 elsif match = scan(/\//) | |
167 after_def = false | |
168 value_expected = true | |
169 encoder.text_token match, :operator | |
170 | |
171 else | |
172 encoder.text_token getch, :error | |
173 | |
174 end | |
175 | |
176 when :string, :regexp, :multiline_string | |
177 if match = scan(STRING_CONTENT_PATTERN[string_delimiter]) | |
178 encoder.text_token match, :content | |
179 | |
180 elsif match = scan(state == :multiline_string ? /'''|"""/ : /["'\/]/) | |
181 encoder.text_token match, :delimiter | |
182 if state == :regexp | |
183 # TODO: regexp modifiers? s, m, x, i? | |
184 modifiers = scan(/[ix]+/) | |
185 encoder.text_token modifiers, :modifier if modifiers && !modifiers.empty? | |
186 end | |
187 state = :string if state == :multiline_string | |
188 encoder.end_group state | |
189 string_delimiter = nil | |
190 after_def = value_expected = false | |
191 state = :initial | |
192 next | |
193 | |
194 elsif (state == :string || state == :multiline_string) && | |
195 (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) | |
196 if string_delimiter[0] == ?' && !(match == "\\\\" || match == "\\'") | |
197 encoder.text_token match, :content | |
198 else | |
199 encoder.text_token match, :char | |
200 end | |
201 elsif state == :regexp && match = scan(/ \\ (?: #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox) | |
202 encoder.text_token match, :char | |
203 | |
204 elsif match = scan(/ \$ #{IDENT} /mox) | |
205 encoder.begin_group :inline | |
206 encoder.text_token '$', :inline_delimiter | |
207 match = match[1..-1] | |
208 encoder.text_token match, IDENT_KIND[match] | |
209 encoder.end_group :inline | |
210 next | |
211 elsif match = scan(/ \$ \{ /x) | |
212 encoder.begin_group :inline | |
213 encoder.text_token match, :inline_delimiter | |
214 inline_block_stack << [state, string_delimiter, inline_block_paren_depth] | |
215 inline_block_paren_depth = 1 | |
216 state = :initial | |
217 next | |
218 | |
219 elsif match = scan(/ \$ /mx) | |
220 encoder.text_token match, :content | |
221 | |
222 elsif match = scan(/ \\. /mx) | |
223 encoder.text_token match, :content # TODO: Shouldn't this be :error? | |
224 | |
225 elsif match = scan(/ \\ | \n /x) | |
226 encoder.end_group state | |
227 encoder.text_token match, :error | |
228 after_def = value_expected = false | |
229 state = :initial | |
230 | |
231 else | |
232 raise_inspect "else case \" reached; %p not handled." % peek(1), encoder | |
233 | |
234 end | |
235 | |
236 else | |
237 raise_inspect 'Unknown state', encoder | |
238 | |
239 end | |
240 | |
241 last_token = match unless [:space, :comment, :doctype].include? kind | |
242 | |
243 end | |
244 | |
245 if [:multiline_string, :string, :regexp].include? state | |
246 encoder.end_group state | |
247 end | |
248 | |
249 encoder | |
250 end | |
251 | |
252 end | |
253 | |
254 end | |
255 end |