OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of csslib.parser; | 5 part of csslib.parser; |
6 | 6 |
7 class Tokenizer extends TokenizerBase { | 7 class Tokenizer extends TokenizerBase { |
8 /** U+ prefix for unicode characters. */ | 8 /** U+ prefix for unicode characters. */ |
9 final UNICODE_U = 'U'.codeUnitAt(0); | 9 final UNICODE_U = 'U'.codeUnitAt(0); |
10 final UNICODE_LOWER_U = 'u'.codeUnitAt(0); | 10 final UNICODE_LOWER_U = 'u'.codeUnitAt(0); |
11 final UNICODE_PLUS = '+'.codeUnitAt(0); | 11 final UNICODE_PLUS = '+'.codeUnitAt(0); |
12 | 12 |
13 final QUESTION_MARK = '?'.codeUnitAt(0); | 13 final QUESTION_MARK = '?'.codeUnitAt(0); |
14 | 14 |
15 /** CDATA keyword. */ | 15 /** CDATA keyword. */ |
16 final List CDATA_NAME = 'CDATA'.codeUnits; | 16 final List CDATA_NAME = 'CDATA'.codeUnits; |
17 | 17 |
18 Tokenizer(SourceFile file, String text, bool skipWhitespace, | 18 Tokenizer(SourceFile file, String text, bool skipWhitespace, [int index = 0]) |
19 [int index = 0]) | |
20 : super(file, text, skipWhitespace, index); | 19 : super(file, text, skipWhitespace, index); |
21 | 20 |
22 Token next({unicodeRange: false}) { | 21 Token next({unicodeRange: false}) { |
23 // keep track of our starting position | 22 // keep track of our starting position |
24 _startIndex = _index; | 23 _startIndex = _index; |
25 | 24 |
26 int ch; | 25 int ch; |
27 ch = _nextChar(); | 26 ch = _nextChar(); |
28 switch (ch) { | 27 switch (ch) { |
29 case TokenChar.NEWLINE: | 28 case TokenChar.NEWLINE: |
30 case TokenChar.RETURN: | 29 case TokenChar.RETURN: |
31 case TokenChar.SPACE: | 30 case TokenChar.SPACE: |
32 case TokenChar.TAB: | 31 case TokenChar.TAB: |
33 return finishWhitespace(); | 32 return finishWhitespace(); |
34 case TokenChar.END_OF_FILE: | 33 case TokenChar.END_OF_FILE: |
35 return _finishToken(TokenKind.END_OF_FILE); | 34 return _finishToken(TokenKind.END_OF_FILE); |
36 case TokenChar.AT: | 35 case TokenChar.AT: |
37 int peekCh = _peekChar(); | 36 int peekCh = _peekChar(); |
38 if (TokenizerHelpers.isIdentifierStart(peekCh)) { | 37 if (TokenizerHelpers.isIdentifierStart(peekCh)) { |
39 var oldIndex = _index; | 38 var oldIndex = _index; |
40 var oldStartIndex = _startIndex; | 39 var oldStartIndex = _startIndex; |
41 | 40 |
42 _startIndex = _index; | 41 _startIndex = _index; |
43 ch = _nextChar(); | 42 ch = _nextChar(); |
44 Token ident = finishIdentifier(); | 43 Token ident = finishIdentifier(); |
45 | 44 |
46 // Is it a directive? | 45 // Is it a directive? |
47 int tokId = TokenKind.matchDirectives(_text, _startIndex, | 46 int tokId = TokenKind.matchDirectives( |
48 _index - _startIndex); | 47 _text, _startIndex, _index - _startIndex); |
49 if (tokId == -1) { | 48 if (tokId == -1) { |
50 // No, is it a margin directive? | 49 // No, is it a margin directive? |
51 tokId = TokenKind.matchMarginDirectives(_text, _startIndex, | 50 tokId = TokenKind.matchMarginDirectives( |
52 _index - _startIndex); | 51 _text, _startIndex, _index - _startIndex); |
53 } | 52 } |
54 | 53 |
55 if (tokId != -1) { | 54 if (tokId != -1) { |
56 return _finishToken(tokId); | 55 return _finishToken(tokId); |
57 } else { | 56 } else { |
58 // Didn't find a CSS directive or margin directive so the @name is | 57 // Didn't find a CSS directive or margin directive so the @name is |
59 // probably the Less definition '@name: value_variable_definition'. | 58 // probably the Less definition '@name: value_variable_definition'. |
60 _startIndex = oldStartIndex; | 59 _startIndex = oldStartIndex; |
61 _index = oldIndex; | 60 _index = oldIndex; |
62 } | 61 } |
63 } | 62 } |
64 return _finishToken(TokenKind.AT); | 63 return _finishToken(TokenKind.AT); |
65 case TokenChar.DOT: | 64 case TokenChar.DOT: |
66 int start = _startIndex; // Start where the dot started. | 65 int start = _startIndex; // Start where the dot started. |
67 if (maybeEatDigit()) { | 66 if (maybeEatDigit()) { |
68 // looks like a number dot followed by digit(s). | 67 // looks like a number dot followed by digit(s). |
69 Token number = finishNumber(); | 68 Token number = finishNumber(); |
70 if (number.kind == TokenKind.INTEGER) { | 69 if (number.kind == TokenKind.INTEGER) { |
71 // It's a number but it's preceeded by a dot, so make it a double. | 70 // It's a number but it's preceeded by a dot, so make it a double. |
72 _startIndex = start; | 71 _startIndex = start; |
73 return _finishToken(TokenKind.DOUBLE); | 72 return _finishToken(TokenKind.DOUBLE); |
74 } else { | 73 } else { |
75 // Don't allow dot followed by a double (e.g, '..1'). | 74 // Don't allow dot followed by a double (e.g, '..1'). |
76 return _errorToken(); | 75 return _errorToken(); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 } else if (maybeEatDigit()) { | 108 } else if (maybeEatDigit()) { |
110 return finishNumber(); | 109 return finishNumber(); |
111 } else if (TokenizerHelpers.isIdentifierStart(ch)) { | 110 } else if (TokenizerHelpers.isIdentifierStart(ch)) { |
112 return finishIdentifier(); | 111 return finishIdentifier(); |
113 } | 112 } |
114 return _finishToken(TokenKind.MINUS); | 113 return _finishToken(TokenKind.MINUS); |
115 case TokenChar.GREATER: | 114 case TokenChar.GREATER: |
116 return _finishToken(TokenKind.GREATER); | 115 return _finishToken(TokenKind.GREATER); |
117 case TokenChar.TILDE: | 116 case TokenChar.TILDE: |
118 if (_maybeEatChar(TokenChar.EQUALS)) { | 117 if (_maybeEatChar(TokenChar.EQUALS)) { |
119 return _finishToken(TokenKind.INCLUDES); // ~= | 118 return _finishToken(TokenKind.INCLUDES); // ~= |
120 } | 119 } |
121 return _finishToken(TokenKind.TILDE); | 120 return _finishToken(TokenKind.TILDE); |
122 case TokenChar.ASTERISK: | 121 case TokenChar.ASTERISK: |
123 if (_maybeEatChar(TokenChar.EQUALS)) { | 122 if (_maybeEatChar(TokenChar.EQUALS)) { |
124 return _finishToken(TokenKind.SUBSTRING_MATCH); // *= | 123 return _finishToken(TokenKind.SUBSTRING_MATCH); // *= |
125 } | 124 } |
126 return _finishToken(TokenKind.ASTERISK); | 125 return _finishToken(TokenKind.ASTERISK); |
127 case TokenChar.AMPERSAND: | 126 case TokenChar.AMPERSAND: |
128 return _finishToken(TokenKind.AMPERSAND); | 127 return _finishToken(TokenKind.AMPERSAND); |
129 case TokenChar.NAMESPACE: | 128 case TokenChar.NAMESPACE: |
130 if (_maybeEatChar(TokenChar.EQUALS)) { | 129 if (_maybeEatChar(TokenChar.EQUALS)) { |
131 return _finishToken(TokenKind.DASH_MATCH); // |= | 130 return _finishToken(TokenKind.DASH_MATCH); // |= |
132 } | 131 } |
133 return _finishToken(TokenKind.NAMESPACE); | 132 return _finishToken(TokenKind.NAMESPACE); |
134 case TokenChar.COLON: | 133 case TokenChar.COLON: |
135 return _finishToken(TokenKind.COLON); | 134 return _finishToken(TokenKind.COLON); |
136 case TokenChar.COMMA: | 135 case TokenChar.COMMA: |
137 return _finishToken(TokenKind.COMMA); | 136 return _finishToken(TokenKind.COMMA); |
138 case TokenChar.SEMICOLON: | 137 case TokenChar.SEMICOLON: |
139 return _finishToken(TokenKind.SEMICOLON); | 138 return _finishToken(TokenKind.SEMICOLON); |
140 case TokenChar.PERCENT: | 139 case TokenChar.PERCENT: |
141 return _finishToken(TokenKind.PERCENT); | 140 return _finishToken(TokenKind.PERCENT); |
142 case TokenChar.SINGLE_QUOTE: | 141 case TokenChar.SINGLE_QUOTE: |
143 return _finishToken(TokenKind.SINGLE_QUOTE); | 142 return _finishToken(TokenKind.SINGLE_QUOTE); |
144 case TokenChar.DOUBLE_QUOTE: | 143 case TokenChar.DOUBLE_QUOTE: |
145 return _finishToken(TokenKind.DOUBLE_QUOTE); | 144 return _finishToken(TokenKind.DOUBLE_QUOTE); |
146 case TokenChar.SLASH: | 145 case TokenChar.SLASH: |
147 if (_maybeEatChar(TokenChar.ASTERISK)) return finishMultiLineComment(); | 146 if (_maybeEatChar(TokenChar.ASTERISK)) return finishMultiLineComment(); |
148 return _finishToken(TokenKind.SLASH); | 147 return _finishToken(TokenKind.SLASH); |
149 case TokenChar.LESS: // <!-- | 148 case TokenChar.LESS: // <!-- |
150 if (_maybeEatChar(TokenChar.BANG)) { | 149 if (_maybeEatChar(TokenChar.BANG)) { |
151 if (_maybeEatChar(TokenChar.MINUS) && | 150 if (_maybeEatChar(TokenChar.MINUS) && |
152 _maybeEatChar(TokenChar.MINUS)) { | 151 _maybeEatChar(TokenChar.MINUS)) { |
153 return finishMultiLineComment(); | 152 return finishMultiLineComment(); |
154 } else if (_maybeEatChar(TokenChar.LBRACK) && | 153 } else if (_maybeEatChar(TokenChar.LBRACK) && |
155 _maybeEatChar(CDATA_NAME[0]) && | 154 _maybeEatChar(CDATA_NAME[0]) && |
156 _maybeEatChar(CDATA_NAME[1]) && | 155 _maybeEatChar(CDATA_NAME[1]) && |
157 _maybeEatChar(CDATA_NAME[2]) && | 156 _maybeEatChar(CDATA_NAME[2]) && |
158 _maybeEatChar(CDATA_NAME[3]) && | 157 _maybeEatChar(CDATA_NAME[3]) && |
159 _maybeEatChar(CDATA_NAME[4]) && | 158 _maybeEatChar(CDATA_NAME[4]) && |
160 _maybeEatChar(TokenChar.LBRACK)) { | 159 _maybeEatChar(TokenChar.LBRACK)) { |
161 // <![CDATA[ | 160 // <![CDATA[ |
162 return next(); | 161 return next(); |
163 } | 162 } |
164 } | 163 } |
165 return _finishToken(TokenKind.LESS); | 164 return _finishToken(TokenKind.LESS); |
166 case TokenChar.EQUALS: | 165 case TokenChar.EQUALS: |
167 return _finishToken(TokenKind.EQUALS); | 166 return _finishToken(TokenKind.EQUALS); |
168 case TokenChar.CARET: | 167 case TokenChar.CARET: |
169 if (_maybeEatChar(TokenChar.EQUALS)) { | 168 if (_maybeEatChar(TokenChar.EQUALS)) { |
170 return _finishToken(TokenKind.PREFIX_MATCH); // ^= | 169 return _finishToken(TokenKind.PREFIX_MATCH); // ^= |
171 } | 170 } |
172 return _finishToken(TokenKind.CARET); | 171 return _finishToken(TokenKind.CARET); |
173 case TokenChar.DOLLAR: | 172 case TokenChar.DOLLAR: |
174 if (_maybeEatChar(TokenChar.EQUALS)) { | 173 if (_maybeEatChar(TokenChar.EQUALS)) { |
175 return _finishToken(TokenKind.SUFFIX_MATCH); // $= | 174 return _finishToken(TokenKind.SUFFIX_MATCH); // $= |
176 } | 175 } |
177 return _finishToken(TokenKind.DOLLAR); | 176 return _finishToken(TokenKind.DOLLAR); |
178 case TokenChar.BANG: | 177 case TokenChar.BANG: |
179 Token tok = finishIdentifier(); | 178 Token tok = finishIdentifier(); |
180 return (tok == null) ? _finishToken(TokenKind.BANG) : tok; | 179 return (tok == null) ? _finishToken(TokenKind.BANG) : tok; |
181 default: | 180 default: |
182 // TODO(jmesserly): this is used for IE8 detection; I'm not sure it's | 181 // TODO(jmesserly): this is used for IE8 detection; I'm not sure it's |
183 // appropriate outside of a few specific places; certainly shouldn't | 182 // appropriate outside of a few specific places; certainly shouldn't |
184 // be parsed in selectors. | 183 // be parsed in selectors. |
185 if (!inSelector && ch == TokenChar.BACKSLASH) { | 184 if (!inSelector && ch == TokenChar.BACKSLASH) { |
(...skipping 14 matching lines...) Expand all Loading... |
200 } else if (maybeEatQuestionMark()) { | 199 } else if (maybeEatQuestionMark()) { |
201 // HEX_RANGE U+N??? | 200 // HEX_RANGE U+N??? |
202 return finishUnicodeRange(); | 201 return finishUnicodeRange(); |
203 } else { | 202 } else { |
204 return _errorToken(); | 203 return _errorToken(); |
205 } | 204 } |
206 } else if ((ch == UNICODE_U || ch == UNICODE_LOWER_U) && | 205 } else if ((ch == UNICODE_U || ch == UNICODE_LOWER_U) && |
207 (_peekChar() == UNICODE_PLUS)) { | 206 (_peekChar() == UNICODE_PLUS)) { |
208 // Unicode range: U+uNumber[-U+uNumber] | 207 // Unicode range: U+uNumber[-U+uNumber] |
209 // uNumber = 0..10FFFF | 208 // uNumber = 0..10FFFF |
210 _nextChar(); // Skip + | 209 _nextChar(); // Skip + |
211 _startIndex = _index; // Starts at the number | 210 _startIndex = _index; // Starts at the number |
212 return _finishToken(TokenKind.UNICODE_RANGE); | 211 return _finishToken(TokenKind.UNICODE_RANGE); |
213 } else if (varDef(ch)) { | 212 } else if (varDef(ch)) { |
214 return _finishToken(TokenKind.VAR_DEFINITION); | 213 return _finishToken(TokenKind.VAR_DEFINITION); |
215 } else if (varUsage(ch)) { | 214 } else if (varUsage(ch)) { |
216 return _finishToken(TokenKind.VAR_USAGE); | 215 return _finishToken(TokenKind.VAR_USAGE); |
217 } else if (TokenizerHelpers.isIdentifierStart(ch)) { | 216 } else if (TokenizerHelpers.isIdentifierStart(ch)) { |
218 return finishIdentifier(); | 217 return finishIdentifier(); |
219 } else if (TokenizerHelpers.isDigit(ch)) { | 218 } else if (TokenizerHelpers.isDigit(ch)) { |
220 return finishNumber(); | 219 return finishNumber(); |
221 } | 220 } |
222 return _errorToken(); | 221 return _errorToken(); |
223 } | 222 } |
224 } | 223 } |
225 | 224 |
226 bool varDef(int ch) { | 225 bool varDef(int ch) { |
227 return ch == 'v'.codeUnitAt(0) && _maybeEatChar('a'.codeUnitAt(0)) && | 226 return ch == 'v'.codeUnitAt(0) && |
228 _maybeEatChar('r'.codeUnitAt(0)) && _maybeEatChar('-'.codeUnitAt(0)); | 227 _maybeEatChar('a'.codeUnitAt(0)) && |
| 228 _maybeEatChar('r'.codeUnitAt(0)) && |
| 229 _maybeEatChar('-'.codeUnitAt(0)); |
229 } | 230 } |
230 | 231 |
231 bool varUsage(int ch) { | 232 bool varUsage(int ch) { |
232 return ch == 'v'.codeUnitAt(0) && _maybeEatChar('a'.codeUnitAt(0)) && | 233 return ch == 'v'.codeUnitAt(0) && |
233 _maybeEatChar('r'.codeUnitAt(0)) && (_peekChar() == '-'.codeUnitAt(0)); | 234 _maybeEatChar('a'.codeUnitAt(0)) && |
| 235 _maybeEatChar('r'.codeUnitAt(0)) && |
| 236 (_peekChar() == '-'.codeUnitAt(0)); |
234 } | 237 } |
235 | 238 |
236 Token _errorToken([String message = null]) { | 239 Token _errorToken([String message = null]) { |
237 return _finishToken(TokenKind.ERROR); | 240 return _finishToken(TokenKind.ERROR); |
238 } | 241 } |
239 | 242 |
240 int getIdentifierKind() { | 243 int getIdentifierKind() { |
241 // Is the identifier a unit type? | 244 // Is the identifier a unit type? |
242 int tokId = -1; | 245 int tokId = -1; |
243 | 246 |
244 // Don't match units in selectors or selector expressions. | 247 // Don't match units in selectors or selector expressions. |
245 if (!inSelectorExpression && !inSelector) { | 248 if (!inSelectorExpression && !inSelector) { |
246 tokId = TokenKind.matchUnits(_text, _startIndex, _index - _startIndex); | 249 tokId = TokenKind.matchUnits(_text, _startIndex, _index - _startIndex); |
247 } | 250 } |
248 if (tokId == -1) { | 251 if (tokId == -1) { |
249 tokId = (_text.substring(_startIndex, _index) == '!important') ? | 252 tokId = (_text.substring(_startIndex, _index) == '!important') |
250 TokenKind.IMPORTANT : -1; | 253 ? TokenKind.IMPORTANT |
| 254 : -1; |
251 } | 255 } |
252 | 256 |
253 return tokId >= 0 ? tokId : TokenKind.IDENTIFIER; | 257 return tokId >= 0 ? tokId : TokenKind.IDENTIFIER; |
254 } | 258 } |
255 | 259 |
256 Token finishIdentifier() { | 260 Token finishIdentifier() { |
257 // If we encounter an escape sequence, remember it so we can post-process | 261 // If we encounter an escape sequence, remember it so we can post-process |
258 // to unescape. | 262 // to unescape. |
259 bool hasEscapedChars = false; | 263 bool hasEscapedChars = false; |
260 var chars = []; | 264 var chars = []; |
261 | 265 |
262 // backup so we can start with the first character | 266 // backup so we can start with the first character |
263 int validateFrom = _index; | 267 int validateFrom = _index; |
264 _index = _startIndex; | 268 _index = _startIndex; |
265 while (_index < _text.length) { | 269 while (_index < _text.length) { |
266 int ch = _text.codeUnitAt(_index); | 270 int ch = _text.codeUnitAt(_index); |
267 | 271 |
268 // If the previous character was "\" we need to escape. T | 272 // If the previous character was "\" we need to escape. T |
269 // http://www.w3.org/TR/CSS21/syndata.html#characters | 273 // http://www.w3.org/TR/CSS21/syndata.html#characters |
270 // if followed by hexadecimal digits, create the appropriate character. | 274 // if followed by hexadecimal digits, create the appropriate character. |
271 // otherwise, include the character in the identifier and don't treat it | 275 // otherwise, include the character in the identifier and don't treat it |
272 // specially. | 276 // specially. |
273 if (ch == 92/*\*/) { | 277 if (ch == 92 /*\*/) { |
274 int startHex = ++_index; | 278 int startHex = ++_index; |
275 eatHexDigits(startHex + 6); | 279 eatHexDigits(startHex + 6); |
276 if (_index != startHex) { | 280 if (_index != startHex) { |
277 // Parse the hex digits and add that character. | 281 // Parse the hex digits and add that character. |
278 chars.add(int.parse('0x' + _text.substring(startHex, _index))); | 282 chars.add(int.parse('0x' + _text.substring(startHex, _index))); |
279 | 283 |
280 if (_index == _text.length) break; | 284 if (_index == _text.length) break; |
281 | 285 |
282 // if we stopped the hex because of a whitespace char, skip it | 286 // if we stopped the hex because of a whitespace char, skip it |
283 ch = _text.codeUnitAt(_index); | 287 ch = _text.codeUnitAt(_index); |
284 if (_index - startHex != 6 && | 288 if (_index - startHex != 6 && |
285 (ch == TokenChar.SPACE || ch == TokenChar.TAB || | 289 (ch == TokenChar.SPACE || |
286 ch == TokenChar.RETURN || ch == TokenChar.NEWLINE)) { | 290 ch == TokenChar.TAB || |
| 291 ch == TokenChar.RETURN || |
| 292 ch == TokenChar.NEWLINE)) { |
287 _index++; | 293 _index++; |
288 } | 294 } |
289 } else { | 295 } else { |
290 // not a digit, just add the next character literally | 296 // not a digit, just add the next character literally |
291 if (_index == _text.length) break; | 297 if (_index == _text.length) break; |
292 chars.add(_text.codeUnitAt(_index++)); | 298 chars.add(_text.codeUnitAt(_index++)); |
293 } | 299 } |
294 } else if (_index < validateFrom || (inSelectorExpression | 300 } else if (_index < validateFrom || |
295 ? TokenizerHelpers.isIdentifierPartExpr(ch) | 301 (inSelectorExpression |
296 : TokenizerHelpers.isIdentifierPart(ch))) { | 302 ? TokenizerHelpers.isIdentifierPartExpr(ch) |
| 303 : TokenizerHelpers.isIdentifierPart(ch))) { |
297 chars.add(ch); | 304 chars.add(ch); |
298 _index++; | 305 _index++; |
299 } else { | 306 } else { |
300 // Not an identifier or escaped character. | 307 // Not an identifier or escaped character. |
301 break; | 308 break; |
302 } | 309 } |
303 } | 310 } |
304 | 311 |
305 var span = _file.span(_startIndex, _index); | 312 var span = _file.span(_startIndex, _index); |
306 var text = new String.fromCharCodes(chars); | 313 var text = new String.fromCharCodes(chars); |
307 | 314 |
308 return new IdentifierToken(text, getIdentifierKind(), span); | 315 return new IdentifierToken(text, getIdentifierKind(), span); |
309 } | 316 } |
310 | 317 |
311 Token finishNumber() { | 318 Token finishNumber() { |
312 eatDigits(); | 319 eatDigits(); |
313 | 320 |
314 if (_peekChar() == 46/*.*/) { | 321 if (_peekChar() == 46 /*.*/) { |
315 // Handle the case of 1.toString(). | 322 // Handle the case of 1.toString(). |
316 _nextChar(); | 323 _nextChar(); |
317 if (TokenizerHelpers.isDigit(_peekChar())) { | 324 if (TokenizerHelpers.isDigit(_peekChar())) { |
318 eatDigits(); | 325 eatDigits(); |
319 return _finishToken(TokenKind.DOUBLE); | 326 return _finishToken(TokenKind.DOUBLE); |
320 } else { | 327 } else { |
321 _index -= 1; | 328 _index -= 1; |
322 } | 329 } |
323 } | 330 } |
324 | 331 |
325 return _finishToken(TokenKind.INTEGER); | 332 return _finishToken(TokenKind.INTEGER); |
326 } | 333 } |
327 | 334 |
328 bool maybeEatDigit() { | 335 bool maybeEatDigit() { |
329 if (_index < _text.length | 336 if (_index < _text.length && |
330 && TokenizerHelpers.isDigit(_text.codeUnitAt(_index))) { | 337 TokenizerHelpers.isDigit(_text.codeUnitAt(_index))) { |
331 _index += 1; | 338 _index += 1; |
332 return true; | 339 return true; |
333 } | 340 } |
334 return false; | 341 return false; |
335 } | 342 } |
336 | 343 |
337 Token finishHexNumber() { | 344 Token finishHexNumber() { |
338 eatHexDigits(_text.length); | 345 eatHexDigits(_text.length); |
339 return _finishToken(TokenKind.HEX_INTEGER); | 346 return _finishToken(TokenKind.HEX_INTEGER); |
340 } | 347 } |
341 | 348 |
342 void eatHexDigits(int end) { | 349 void eatHexDigits(int end) { |
343 end = math.min(end, _text.length); | 350 end = math.min(end, _text.length); |
344 while (_index < end) { | 351 while (_index < end) { |
345 if (TokenizerHelpers.isHexDigit(_text.codeUnitAt(_index))) { | 352 if (TokenizerHelpers.isHexDigit(_text.codeUnitAt(_index))) { |
346 _index += 1; | 353 _index += 1; |
347 } else { | 354 } else { |
348 return; | 355 return; |
349 } | 356 } |
350 } | 357 } |
351 } | 358 } |
352 | 359 |
353 bool maybeEatHexDigit() { | 360 bool maybeEatHexDigit() { |
354 if (_index < _text.length | 361 if (_index < _text.length && |
355 && TokenizerHelpers.isHexDigit(_text.codeUnitAt(_index))) { | 362 TokenizerHelpers.isHexDigit(_text.codeUnitAt(_index))) { |
356 _index += 1; | 363 _index += 1; |
357 return true; | 364 return true; |
358 } | 365 } |
359 return false; | 366 return false; |
360 } | 367 } |
361 | 368 |
362 bool maybeEatQuestionMark() { | 369 bool maybeEatQuestionMark() { |
363 if (_index < _text.length && | 370 if (_index < _text.length && _text.codeUnitAt(_index) == QUESTION_MARK) { |
364 _text.codeUnitAt(_index) == QUESTION_MARK) { | |
365 _index += 1; | 371 _index += 1; |
366 return true; | 372 return true; |
367 } | 373 } |
368 return false; | 374 return false; |
369 } | 375 } |
370 | 376 |
371 void eatQuestionMarks() { | 377 void eatQuestionMarks() { |
372 while (_index < _text.length) { | 378 while (_index < _text.length) { |
373 if (_text.codeUnitAt(_index) == QUESTION_MARK) { | 379 if (_text.codeUnitAt(_index) == QUESTION_MARK) { |
374 _index += 1; | 380 _index += 1; |
375 } else { | 381 } else { |
376 return; | 382 return; |
377 } | 383 } |
378 } | 384 } |
379 } | 385 } |
380 | 386 |
381 Token finishUnicodeRange() { | 387 Token finishUnicodeRange() { |
382 eatQuestionMarks(); | 388 eatQuestionMarks(); |
383 return _finishToken(TokenKind.HEX_RANGE); | 389 return _finishToken(TokenKind.HEX_RANGE); |
384 } | 390 } |
385 | 391 |
386 Token finishMultiLineComment() { | 392 Token finishMultiLineComment() { |
387 while (true) { | 393 while (true) { |
388 int ch = _nextChar(); | 394 int ch = _nextChar(); |
389 if (ch == 0) { | 395 if (ch == 0) { |
390 return _finishToken(TokenKind.INCOMPLETE_COMMENT); | 396 return _finishToken(TokenKind.INCOMPLETE_COMMENT); |
391 } else if (ch == 42/*'*'*/) { | 397 } else if (ch == 42 /*'*'*/) { |
392 if (_maybeEatChar(47/*'/'*/)) { | 398 if (_maybeEatChar(47 /*'/'*/)) { |
393 if (_skipWhitespace) { | 399 if (_skipWhitespace) { |
394 return next(); | 400 return next(); |
395 } else { | 401 } else { |
396 return _finishToken(TokenKind.COMMENT); | 402 return _finishToken(TokenKind.COMMENT); |
397 } | 403 } |
398 } | 404 } |
399 } else if (ch == TokenChar.MINUS) { | 405 } else if (ch == TokenChar.MINUS) { |
400 /* Check if close part of Comment Definition --> (CDC). */ | 406 /* Check if close part of Comment Definition --> (CDC). */ |
401 if (_maybeEatChar(TokenChar.MINUS)) { | 407 if (_maybeEatChar(TokenChar.MINUS)) { |
402 if (_maybeEatChar(TokenChar.GREATER)) { | 408 if (_maybeEatChar(TokenChar.GREATER)) { |
403 if (_skipWhitespace) { | 409 if (_skipWhitespace) { |
404 return next(); | 410 return next(); |
405 } else { | 411 } else { |
406 return _finishToken(TokenKind.HTML_COMMENT); | 412 return _finishToken(TokenKind.HTML_COMMENT); |
407 } | 413 } |
408 } | 414 } |
409 } | 415 } |
410 } | 416 } |
411 } | 417 } |
412 return _errorToken(); | 418 return _errorToken(); |
413 } | 419 } |
414 | |
415 } | 420 } |
416 | 421 |
417 /** Static helper methods. */ | 422 /** Static helper methods. */ |
418 class TokenizerHelpers { | 423 class TokenizerHelpers { |
419 static bool isIdentifierStart(int c) { | 424 static bool isIdentifierStart(int c) { |
420 return isIdentifierStartExpr(c) || c == 45 /*-*/; | 425 return isIdentifierStartExpr(c) || c == 45 /*-*/; |
421 } | 426 } |
422 | 427 |
423 static bool isDigit(int c) { | 428 static bool isDigit(int c) { |
424 return (c >= 48/*0*/ && c <= 57/*9*/); | 429 return (c >= 48 /*0*/ && c <= 57 /*9*/); |
425 } | 430 } |
426 | 431 |
427 static bool isHexDigit(int c) { | 432 static bool isHexDigit(int c) { |
428 return (isDigit(c) || (c >= 97/*a*/ && c <= 102/*f*/) | 433 return (isDigit(c) || |
429 || (c >= 65/*A*/ && c <= 70/*F*/)); | 434 (c >= 97 /*a*/ && c <= 102 /*f*/) || |
| 435 (c >= 65 /*A*/ && c <= 70 /*F*/)); |
430 } | 436 } |
431 | 437 |
432 static bool isIdentifierPart(int c) { | 438 static bool isIdentifierPart(int c) { |
433 return isIdentifierPartExpr(c) || c == 45 /*-*/; | 439 return isIdentifierPartExpr(c) || c == 45 /*-*/; |
434 } | 440 } |
435 | 441 |
436 /** Pseudo function expressions identifiers can't have a minus sign. */ | 442 /** Pseudo function expressions identifiers can't have a minus sign. */ |
437 static bool isIdentifierStartExpr(int c) { | 443 static bool isIdentifierStartExpr(int c) { |
438 return ((c >= 97/*a*/ && c <= 122/*z*/) || (c >= 65/*A*/ && c <= 90/*Z*/) || | 444 return ((c >= 97 /*a*/ && c <= 122 /*z*/) || |
| 445 (c >= 65 /*A*/ && c <= 90 /*Z*/) || |
439 // Note: Unicode 10646 chars U+00A0 or higher are allowed, see: | 446 // Note: Unicode 10646 chars U+00A0 or higher are allowed, see: |
440 // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier | 447 // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier |
441 // http://www.w3.org/TR/CSS21/syndata.html#characters | 448 // http://www.w3.org/TR/CSS21/syndata.html#characters |
442 // Also, escaped character should be allowed. | 449 // Also, escaped character should be allowed. |
443 c == 95/*_*/ || c >= 0xA0 || c == 92/*\*/); | 450 c == 95 /*_*/ || c >= 0xA0 || c == 92 /*\*/); |
444 } | 451 } |
445 | 452 |
446 /** Pseudo function expressions identifiers can't have a minus sign. */ | 453 /** Pseudo function expressions identifiers can't have a minus sign. */ |
447 static bool isIdentifierPartExpr(int c) { | 454 static bool isIdentifierPartExpr(int c) { |
448 return (isIdentifierStartExpr(c) || isDigit(c)); | 455 return (isIdentifierStartExpr(c) || isDigit(c)); |
449 } | 456 } |
450 } | 457 } |
OLD | NEW |