comparison vendor/gems/coderay-0.9.7/lib/coderay/scanners/java_script.rb @ 523:0b6c82dead28 luisf

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