Mercurial > hg > soundsoftware-site
comparison vendor/gems/coderay-1.0.0/lib/coderay/scanners/java_script.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 # Scanner for JavaScript. | |
5 # | |
6 # Aliases: +ecmascript+, +ecma_script+, +javascript+ | |
7 class JavaScript < Scanner | |
8 | |
9 register_for :java_script | |
10 file_extension 'js' | |
11 | |
12 # The actual JavaScript keywords. | |
13 KEYWORDS = %w[ | |
14 break case catch continue default delete do else | |
15 finally for function if in instanceof new | |
16 return switch throw try typeof var void while with | |
17 ] # :nodoc: | |
18 PREDEFINED_CONSTANTS = %w[ | |
19 false null true undefined NaN Infinity | |
20 ] # :nodoc: | |
21 | |
22 MAGIC_VARIABLES = %w[ this arguments ] # :nodoc: arguments was introduced in JavaScript 1.4 | |
23 | |
24 KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[ | |
25 case delete in instanceof new return throw typeof with | |
26 ] # :nodoc: | |
27 | |
28 # Reserved for future use. | |
29 RESERVED_WORDS = %w[ | |
30 abstract boolean byte char class debugger double enum export extends | |
31 final float goto implements import int interface long native package | |
32 private protected public short static super synchronized throws transient | |
33 volatile | |
34 ] # :nodoc: | |
35 | |
36 IDENT_KIND = WordList.new(:ident). | |
37 add(RESERVED_WORDS, :reserved). | |
38 add(PREDEFINED_CONSTANTS, :predefined_constant). | |
39 add(MAGIC_VARIABLES, :local_variable). | |
40 add(KEYWORDS, :keyword) # :nodoc: | |
41 | |
42 ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: | |
43 UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: | |
44 REGEXP_ESCAPE = / [bBdDsSwW] /x # :nodoc: | |
45 STRING_CONTENT_PATTERN = { | |
46 "'" => /[^\\']+/, | |
47 '"' => /[^\\"]+/, | |
48 '/' => /[^\\\/]+/, | |
49 } # :nodoc: | |
50 KEY_CHECK_PATTERN = { | |
51 "'" => / (?> [^\\']* (?: \\. [^\\']* )* ) ' \s* : /mx, | |
52 '"' => / (?> [^\\"]* (?: \\. [^\\"]* )* ) " \s* : /mx, | |
53 } # :nodoc: | |
54 | |
55 protected | |
56 | |
57 def scan_tokens encoder, options | |
58 | |
59 state = :initial | |
60 string_delimiter = nil | |
61 value_expected = true | |
62 key_expected = false | |
63 function_expected = false | |
64 | |
65 until eos? | |
66 | |
67 case state | |
68 | |
69 when :initial | |
70 | |
71 if match = scan(/ \s+ | \\\n /x) | |
72 value_expected = true if !value_expected && match.index(?\n) | |
73 encoder.text_token match, :space | |
74 | |
75 elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) | |
76 value_expected = true | |
77 encoder.text_token match, :comment | |
78 | |
79 elsif check(/\.?\d/) | |
80 key_expected = value_expected = false | |
81 if match = scan(/0[xX][0-9A-Fa-f]+/) | |
82 encoder.text_token match, :hex | |
83 elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/) | |
84 encoder.text_token match, :octal | |
85 elsif match = scan(/\d+[fF]|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) | |
86 encoder.text_token match, :float | |
87 elsif match = scan(/\d+/) | |
88 encoder.text_token match, :integer | |
89 end | |
90 | |
91 elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim) | |
92 # TODO: scan over nested tags | |
93 xml_scanner.tokenize match, :tokens => encoder | |
94 value_expected = false | |
95 next | |
96 | |
97 elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x) | |
98 value_expected = true | |
99 last_operator = match[-1] | |
100 key_expected = (last_operator == ?{) || (last_operator == ?,) | |
101 function_expected = false | |
102 encoder.text_token match, :operator | |
103 | |
104 elsif match = scan(/ [)\]}]+ /x) | |
105 function_expected = key_expected = value_expected = false | |
106 encoder.text_token match, :operator | |
107 | |
108 elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x) | |
109 kind = IDENT_KIND[match] | |
110 value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match] | |
111 # TODO: labels | |
112 if kind == :ident | |
113 if match.index(?$) # $ allowed inside an identifier | |
114 kind = :predefined | |
115 elsif function_expected | |
116 kind = :function | |
117 elsif check(/\s*[=:]\s*function\b/) | |
118 kind = :function | |
119 elsif key_expected && check(/\s*:/) | |
120 kind = :key | |
121 end | |
122 end | |
123 function_expected = (kind == :keyword) && (match == 'function') | |
124 key_expected = false | |
125 encoder.text_token match, kind | |
126 | |
127 elsif match = scan(/["']/) | |
128 if key_expected && check(KEY_CHECK_PATTERN[match]) | |
129 state = :key | |
130 else | |
131 state = :string | |
132 end | |
133 encoder.begin_group state | |
134 string_delimiter = match | |
135 encoder.text_token match, :delimiter | |
136 | |
137 elsif value_expected && (match = scan(/\//)) | |
138 encoder.begin_group :regexp | |
139 state = :regexp | |
140 string_delimiter = '/' | |
141 encoder.text_token match, :delimiter | |
142 | |
143 elsif match = scan(/ \/ /x) | |
144 value_expected = true | |
145 key_expected = false | |
146 encoder.text_token match, :operator | |
147 | |
148 else | |
149 encoder.text_token getch, :error | |
150 | |
151 end | |
152 | |
153 when :string, :regexp, :key | |
154 if match = scan(STRING_CONTENT_PATTERN[string_delimiter]) | |
155 encoder.text_token match, :content | |
156 elsif match = scan(/["'\/]/) | |
157 encoder.text_token match, :delimiter | |
158 if state == :regexp | |
159 modifiers = scan(/[gim]+/) | |
160 encoder.text_token modifiers, :modifier if modifiers && !modifiers.empty? | |
161 end | |
162 encoder.end_group state | |
163 string_delimiter = nil | |
164 key_expected = value_expected = false | |
165 state = :initial | |
166 elsif state != :regexp && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) | |
167 if string_delimiter == "'" && !(match == "\\\\" || match == "\\'") | |
168 encoder.text_token match, :content | |
169 else | |
170 encoder.text_token match, :char | |
171 end | |
172 elsif state == :regexp && match = scan(/ \\ (?: #{ESCAPE} | #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox) | |
173 encoder.text_token match, :char | |
174 elsif match = scan(/\\./m) | |
175 encoder.text_token match, :content | |
176 elsif match = scan(/ \\ | $ /x) | |
177 encoder.end_group state | |
178 encoder.text_token match, :error | |
179 key_expected = value_expected = false | |
180 state = :initial | |
181 else | |
182 raise_inspect "else case \" reached; %p not handled." % peek(1), encoder | |
183 end | |
184 | |
185 else | |
186 raise_inspect 'Unknown state', encoder | |
187 | |
188 end | |
189 | |
190 end | |
191 | |
192 if [:string, :regexp].include? state | |
193 encoder.end_group state | |
194 end | |
195 | |
196 encoder | |
197 end | |
198 | |
199 protected | |
200 | |
201 def reset_instance | |
202 super | |
203 @xml_scanner.reset if defined? @xml_scanner | |
204 end | |
205 | |
206 def xml_scanner | |
207 @xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => false | |
208 end | |
209 | |
210 end | |
211 | |
212 end | |
213 end |