OLD | NEW |
1 // Copyright (c) 2011, 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 // Generated by scripts/tokenizer_gen.py. | 4 // Generated by scripts/tokenizer_gen.py. |
5 | 5 |
| 6 part of csslib.parser; |
6 | 7 |
7 abstract class TokenSource { | 8 /** Tokenizer state to support look ahead for Less' nested selectors. */ |
8 Token next(); | 9 class TokenizerState { |
9 } | 10 final int index; |
| 11 final int startIndex; |
| 12 final bool selectorExpression; |
10 | 13 |
11 class InterpStack { | 14 TokenizerState(TokenizerBase base) : |
12 InterpStack next, previous; | 15 this.index = base._index, |
13 final int quote; | 16 this.startIndex = base._startIndex, |
14 final bool isMultiline; | 17 this.selectorExpression = base.selectorExpression; |
15 int depth; | |
16 | |
17 InterpStack(this.previous, this.quote, this.isMultiline): depth = -1; | |
18 | |
19 InterpStack pop() { | |
20 return this.previous; | |
21 } | |
22 | |
23 static InterpStack push(InterpStack stack, int quote, bool isMultiline) { | |
24 var newStack = new InterpStack(stack, quote, isMultiline); | |
25 if (stack != null) newStack.previous = stack; | |
26 return newStack; | |
27 } | |
28 } | 18 } |
29 | 19 |
30 /** | 20 /** |
31 * The base class for our tokenizer. The hand coded parts are in this file, with | 21 * The base class for our tokenizer. The hand coded parts are in this file, with |
32 * the generated parts in the subclass Tokenizer. | 22 * the generated parts in the subclass Tokenizer. |
33 */ | 23 */ |
34 class CSSTokenizerBase implements TokenSource { | 24 abstract class TokenizerBase { |
35 final SourceFile _source; | 25 final SourceFile _file; |
36 final bool _skipWhitespace; | 26 final bool _skipWhitespace; |
37 String _text; | 27 final String _text; |
| 28 |
| 29 /** |
| 30 * Changes tokenization when in a pseudo function expression. If true then |
| 31 * minus signs are handled as operators instead of identifiers. |
| 32 */ |
| 33 bool selectorExpression = false; |
38 | 34 |
39 int _index; | 35 int _index; |
40 int _startIndex; | 36 int _startIndex; |
41 | 37 |
42 /** Keeps track of string interpolation state. */ | 38 static const String _CDATA_START = '<![CDATA['; |
43 InterpStack _interpStack; | 39 static const String _CDATA_END = ']]>'; |
44 | 40 |
45 CSSTokenizerBase(this._source, this._skipWhitespace, [index = 0]) | 41 TokenizerBase(this._file, this._text, this._skipWhitespace, |
46 : this._index = index { | 42 [this._index = 0]); |
47 _text = _source.text; | 43 |
| 44 Token next(); |
| 45 int getIdentifierKind(); |
| 46 |
| 47 /** Snapshot of Tokenizer scanning state. */ |
| 48 TokenizerState get mark => new TokenizerState(this); |
| 49 |
| 50 /** Restore Tokenizer scanning state. */ |
| 51 void restore(TokenizerState markedData) { |
| 52 _index = markedData.index; |
| 53 _startIndex = markedData.startIndex; |
| 54 selectorExpression = markedData.selectorExpression; |
48 } | 55 } |
49 | 56 |
50 abstract Token next(); | |
51 abstract int getIdentifierKind(); | |
52 | |
53 int _nextChar() { | 57 int _nextChar() { |
54 if (_index < _text.length) { | 58 if (_index < _text.length) { |
55 return _text.codeUnitAt(_index++); | 59 return _text.codeUnitAt(_index++); |
56 } else { | 60 } else { |
57 return 0; | 61 return 0; |
58 } | 62 } |
59 } | 63 } |
60 | 64 |
61 int _peekChar() { | 65 int _peekChar() { |
62 if (_index < _text.length) { | 66 if (_index < _text.length) { |
(...skipping 18 matching lines...) Expand all Loading... |
81 | 85 |
82 String _tokenText() { | 86 String _tokenText() { |
83 if (_index < _text.length) { | 87 if (_index < _text.length) { |
84 return _text.substring(_startIndex, _index); | 88 return _text.substring(_startIndex, _index); |
85 } else { | 89 } else { |
86 return _text.substring(_startIndex, _text.length); | 90 return _text.substring(_startIndex, _text.length); |
87 } | 91 } |
88 } | 92 } |
89 | 93 |
90 Token _finishToken(int kind) { | 94 Token _finishToken(int kind) { |
91 return new Token(kind, _source, _startIndex, _index); | 95 return new Token(kind, _file.span(_startIndex, _index)); |
92 } | 96 } |
93 | 97 |
94 Token _errorToken([String message = null]) { | 98 Token _errorToken([String message = null]) { |
95 return new ErrorToken( | 99 return new ErrorToken( |
96 TokenKind.ERROR, _source, _startIndex, _index, message); | 100 TokenKind.ERROR, _file.span(_startIndex, _index), message); |
97 } | 101 } |
98 | 102 |
99 Token finishWhitespace() { | 103 Token finishWhitespace() { |
100 _index--; | 104 _index--; |
101 while (_index < _text.length) { | 105 while (_index < _text.length) { |
102 final ch = _text.codeUnitAt(_index++); | 106 final ch = _text.codeUnitAt(_index++); |
103 if (ch == 32/*' '*/ || ch == 9/*'\t'*/ || ch == 13/*'\r'*/) { | 107 if (ch == TokenChar.SPACE || |
| 108 ch == TokenChar.TAB || |
| 109 ch == TokenChar.RETURN) { |
104 // do nothing | 110 // do nothing |
105 } else if (ch == 10/*'\n'*/) { | 111 } else if (ch == TokenChar.NEWLINE) { |
106 if (!_skipWhitespace) { | 112 if (!_skipWhitespace) { |
107 return _finishToken(TokenKind.WHITESPACE); // note the newline? | 113 return _finishToken(TokenKind.WHITESPACE); // note the newline? |
108 } | 114 } |
109 } else { | 115 } else { |
110 _index--; | 116 _index--; |
111 if (_skipWhitespace) { | 117 if (_skipWhitespace) { |
112 return next(); | 118 return next(); |
113 } else { | 119 } else { |
114 return _finishToken(TokenKind.WHITESPACE); | 120 return _finishToken(TokenKind.WHITESPACE); |
115 } | 121 } |
116 } | 122 } |
117 | 123 |
118 } | 124 } |
119 return _finishToken(TokenKind.END_OF_FILE); | 125 return _finishToken(TokenKind.END_OF_FILE); |
120 } | 126 } |
121 | 127 |
122 Token finishSingleLineComment() { | 128 Token finishSingleLineComment() { |
123 while (true) { | 129 while (true) { |
124 int ch = _nextChar(); | 130 int ch = _nextChar(); |
125 if (ch == 0 || ch == 10/*'\n'*/ || ch == 13/*'\r'*/) { | 131 if (ch == 0 || ch == TokenChar.NEWLINE || ch == TokenChar.RETURN) { |
126 if (_skipWhitespace) { | 132 if (_skipWhitespace) { |
127 return next(); | 133 return next(); |
128 } else { | 134 } else { |
129 return _finishToken(TokenKind.COMMENT); | 135 return _finishToken(TokenKind.COMMENT); |
130 } | 136 } |
131 } | 137 } |
132 } | 138 } |
133 } | 139 } |
134 | 140 |
135 Token finishMultiLineComment() { | 141 Token finishMultiLineComment() { |
136 int nesting = 1; | 142 int nesting = 1; |
137 do { | 143 do { |
138 int ch = _nextChar(); | 144 int ch = _nextChar(); |
139 if (ch == 0) { | 145 if (ch == 0) { |
140 return _errorToken(); | 146 return _errorToken(); |
141 } else if (ch == 42/*'*'*/) { | 147 } else if (ch == TokenChar.ASTERISK) { |
142 if (_maybeEatChar(47/*'/'*/)) { | 148 if (_maybeEatChar(TokenChar.SLASH)) { |
143 nesting--; | 149 nesting--; |
144 } | 150 } |
145 } else if (ch == 47/*'/'*/) { | 151 } else if (ch == TokenChar.SLASH) { |
146 if (_maybeEatChar(42/*'*'*/)) { | 152 if (_maybeEatChar(TokenChar.ASTERISK)) { |
147 nesting++; | 153 nesting++; |
148 } | 154 } |
149 } | 155 } |
150 } while (nesting > 0); | 156 } while (nesting > 0); |
151 | 157 |
152 if (_skipWhitespace) { | 158 if (_skipWhitespace) { |
153 return next(); | 159 return next(); |
154 } else { | 160 } else { |
155 return _finishToken(TokenKind.COMMENT); | 161 return _finishToken(TokenKind.COMMENT); |
156 } | 162 } |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 result = (result * 16) + digit; | 211 result = (result * 16) + digit; |
206 _index++; | 212 _index++; |
207 } | 213 } |
208 | 214 |
209 return result; | 215 return result; |
210 } | 216 } |
211 | 217 |
212 Token finishNumber() { | 218 Token finishNumber() { |
213 eatDigits(); | 219 eatDigits(); |
214 | 220 |
215 if (_peekChar() == 46/*.*/) { | 221 if (_peekChar() == TokenChar.DOT) { |
216 // Handle the case of 1.toString(). | 222 // Handle the case of 1.toString(). |
217 _nextChar(); | 223 _nextChar(); |
218 if (TokenizerHelpers.isDigit(_peekChar())) { | 224 if (TokenizerHelpers.isDigit(_peekChar())) { |
219 eatDigits(); | 225 eatDigits(); |
220 return finishNumberExtra(TokenKind.DOUBLE); | 226 return finishNumberExtra(TokenKind.DOUBLE); |
221 } else { | 227 } else { |
222 _index--; | 228 _index--; |
223 } | 229 } |
224 } | 230 } |
225 | 231 |
226 return finishNumberExtra(TokenKind.INTEGER); | 232 return finishNumberExtra(TokenKind.INTEGER); |
227 } | 233 } |
228 | 234 |
229 Token finishNumberExtra(int kind) { | 235 Token finishNumberExtra(int kind) { |
230 if (_maybeEatChar(101/*e*/) || _maybeEatChar(69/*E*/)) { | 236 if (_maybeEatChar(101/*e*/) || _maybeEatChar(69/*E*/)) { |
231 kind = TokenKind.DOUBLE; | 237 kind = TokenKind.DOUBLE; |
232 _maybeEatChar(45/*-*/); | 238 _maybeEatChar(TokenKind.MINUS); |
233 _maybeEatChar(43/*+*/); | 239 _maybeEatChar(TokenKind.PLUS); |
234 eatDigits(); | 240 eatDigits(); |
235 } | 241 } |
236 if (_peekChar() != 0 && TokenizerHelpers.isIdentifierStart(_peekChar())) { | 242 if (_peekChar() != 0 && TokenizerHelpers.isIdentifierStart(_peekChar())) { |
237 _nextChar(); | 243 _nextChar(); |
238 return _errorToken("illegal character in number"); | 244 return _errorToken("illegal character in number"); |
239 } | 245 } |
240 | 246 |
241 return _finishToken(kind); | 247 return _finishToken(kind); |
242 } | 248 } |
243 | 249 |
244 Token _makeStringToken(List<int> buf, bool isPart) { | 250 Token _makeStringToken(List<int> buf, bool isPart) { |
245 final s = new String.fromCharCodes(buf); | 251 final s = new String.fromCharCodes(buf); |
246 final kind = isPart ? TokenKind.STRING_PART : TokenKind.STRING; | 252 final kind = isPart ? TokenKind.STRING_PART : TokenKind.STRING; |
247 return new LiteralToken(kind, _source, _startIndex, _index, s); | 253 return new LiteralToken(kind, _file.span(_startIndex, _index), s); |
| 254 } |
| 255 |
| 256 Token makeIEFilter(int start, int end) { |
| 257 var filter = _text.substring(start, end); |
| 258 return new LiteralToken(TokenKind.STRING, _file.span(start, end), filter); |
248 } | 259 } |
249 | 260 |
250 Token _makeRawStringToken(bool isMultiline) { | 261 Token _makeRawStringToken(bool isMultiline) { |
251 String s; | 262 var s; |
252 if (isMultiline) { | 263 if (isMultiline) { |
253 // Skip initial newline in multiline strings | 264 // Skip initial newline in multiline strings |
254 int start = _startIndex + 4; | 265 int start = _startIndex + 4; |
255 if (_source.text[start] == '\n') start++; | 266 if (_text[start] == '\n') start++; |
256 s = _source.text.substring(start, _index - 3); | 267 s = _text.substring(start, _index - 3); |
257 } else { | 268 } else { |
258 s = _source.text.substring(_startIndex + 2, _index - 1); | 269 s = _text.substring(_startIndex + 2, _index - 1); |
259 } | 270 } |
260 return new LiteralToken(TokenKind.STRING, _source, _startIndex, _index, s); | 271 return new LiteralToken(TokenKind.STRING, |
| 272 _file.span(_startIndex, _index), s); |
261 } | 273 } |
262 | 274 |
263 Token finishMultilineString(int quote) { | 275 Token finishMultilineString(int quote) { |
264 var buf = <int>[]; | 276 var buf = <int>[]; |
265 while (true) { | 277 while (true) { |
266 int ch = _nextChar(); | 278 int ch = _nextChar(); |
267 if (ch == 0) { | 279 if (ch == 0) { |
268 return _errorToken(); | 280 return _errorToken(); |
269 } else if (ch == quote) { | 281 } else if (ch == quote) { |
270 if (_maybeEatChar(quote)) { | 282 if (_maybeEatChar(quote)) { |
271 if (_maybeEatChar(quote)) { | 283 if (_maybeEatChar(quote)) { |
272 return _makeStringToken(buf, false); | 284 return _makeStringToken(buf, false); |
273 } | 285 } |
274 buf.add(quote); | 286 buf.add(quote); |
275 } | 287 } |
276 buf.add(quote); | 288 buf.add(quote); |
277 } else if (ch == 36/*$*/) { | 289 } else if (ch == TokenChar.BACKSLASH) { |
278 // start of string interp | |
279 _interpStack = InterpStack.push(_interpStack, quote, true); | |
280 return _makeStringToken(buf, true); | |
281 } else if (ch == 92/*\*/) { | |
282 var escapeVal = readEscapeSequence(); | 290 var escapeVal = readEscapeSequence(); |
283 if (escapeVal == -1) { | 291 if (escapeVal == -1) { |
284 return _errorToken("invalid hex escape sequence"); | 292 return _errorToken("invalid hex escape sequence"); |
285 } else { | 293 } else { |
286 buf.add(escapeVal); | 294 buf.add(escapeVal); |
287 } | 295 } |
288 } else { | 296 } else { |
289 buf.add(ch); | 297 buf.add(ch); |
290 } | 298 } |
291 } | 299 } |
292 } | 300 } |
293 | 301 |
294 Token _finishOpenBrace() { | 302 Token _finishOpenBrace() { |
295 if (_interpStack != null) { | |
296 if (_interpStack.depth == -1) { | |
297 _interpStack.depth = 1; | |
298 } else { | |
299 assert(_interpStack.depth >= 0); | |
300 _interpStack.depth += 1; | |
301 } | |
302 } | |
303 return _finishToken(TokenKind.LBRACE); | 303 return _finishToken(TokenKind.LBRACE); |
304 } | 304 } |
305 | 305 |
306 Token _finishCloseBrace() { | 306 Token _finishCloseBrace() { |
307 if (_interpStack != null) { | |
308 _interpStack.depth -= 1; | |
309 assert(_interpStack.depth >= 0); | |
310 } | |
311 return _finishToken(TokenKind.RBRACE); | 307 return _finishToken(TokenKind.RBRACE); |
312 } | 308 } |
313 | 309 |
314 Token finishString(int quote) { | 310 Token finishString(int quote) { |
315 if (_maybeEatChar(quote)) { | 311 if (_maybeEatChar(quote)) { |
316 if (_maybeEatChar(quote)) { | 312 if (_maybeEatChar(quote)) { |
317 // skip an initial newline | 313 // skip an initial newline |
318 _maybeEatChar(10/*'\n'*/); | 314 _maybeEatChar(TokenChar.NEWLINE); |
319 return finishMultilineString(quote); | 315 return finishMultilineString(quote); |
320 } else { | 316 } else { |
321 return _makeStringToken(new List<int>(), false); | 317 return _makeStringToken(new List<int>(), false); |
322 } | 318 } |
323 } | 319 } |
324 return finishStringBody(quote); | 320 return finishStringBody(quote); |
325 } | 321 } |
326 | 322 |
327 Token finishRawString(int quote) { | 323 Token finishRawString(int quote) { |
328 if (_maybeEatChar(quote)) { | 324 if (_maybeEatChar(quote)) { |
(...skipping 23 matching lines...) Expand all Loading... |
352 } | 348 } |
353 } | 349 } |
354 } | 350 } |
355 | 351 |
356 Token finishStringBody(int quote) { | 352 Token finishStringBody(int quote) { |
357 var buf = new List<int>(); | 353 var buf = new List<int>(); |
358 while (true) { | 354 while (true) { |
359 int ch = _nextChar(); | 355 int ch = _nextChar(); |
360 if (ch == quote) { | 356 if (ch == quote) { |
361 return _makeStringToken(buf, false); | 357 return _makeStringToken(buf, false); |
362 } else if (ch == 36/*$*/) { | |
363 // start of string interp | |
364 _interpStack = InterpStack.push(_interpStack, quote, false); | |
365 return _makeStringToken(buf, true); | |
366 } else if (ch == 0) { | 358 } else if (ch == 0) { |
367 return _errorToken(); | 359 return _errorToken(); |
368 } else if (ch == 92/*\*/) { | 360 } else if (ch == TokenChar.BACKSLASH) { |
369 var escapeVal = readEscapeSequence(); | 361 var escapeVal = readEscapeSequence(); |
370 if (escapeVal == -1) { | 362 if (escapeVal == -1) { |
371 return _errorToken("invalid hex escape sequence"); | 363 return _errorToken("invalid hex escape sequence"); |
372 } else { | 364 } else { |
373 buf.add(escapeVal); | 365 buf.add(escapeVal); |
374 } | 366 } |
375 } else { | 367 } else { |
376 buf.add(ch); | 368 buf.add(ch); |
377 } | 369 } |
378 } | 370 } |
379 } | 371 } |
380 | 372 |
381 int readEscapeSequence() { | 373 int readEscapeSequence() { |
382 final ch = _nextChar(); | 374 final ch = _nextChar(); |
383 int hexValue; | 375 int hexValue; |
384 switch (ch) { | 376 switch (ch) { |
385 case 110/*n*/: | 377 case 110/*n*/: |
386 return 0x0a/*'\n'*/; | 378 return TokenChar.NEWLINE; |
387 case 114/*r*/: | 379 case 114/*r*/: |
388 return 0x0d/*'\r'*/; | 380 return TokenChar.RETURN; |
389 case 102/*f*/: | 381 case 102/*f*/: |
390 return 0x0c/*'\f'*/; | 382 return TokenChar.FF; |
391 case 98/*b*/: | 383 case 98/*b*/: |
392 return 0x08/*'\b'*/; | 384 return TokenChar.BACKSPACE; |
393 case 116/*t*/: | 385 case 116/*t*/: |
394 return 0x09/*'\t'*/; | 386 return TokenChar.TAB; |
395 case 118/*v*/: | 387 case 118/*v*/: |
396 return 0x0b/*'\v'*/; | 388 return TokenChar.FF; |
397 case 120/*x*/: | 389 case 120/*x*/: |
398 hexValue = readHex(2); | 390 hexValue = readHex(2); |
399 break; | 391 break; |
400 case 117/*u*/: | 392 case 117/*u*/: |
401 if (_maybeEatChar(123/*{*/)) { | 393 if (_maybeEatChar(TokenChar.LBRACE)) { |
402 hexValue = readHex(); | 394 hexValue = readHex(); |
403 if (!_maybeEatChar(125/*}*/)) { | 395 if (!_maybeEatChar(TokenChar.RBRACE)) { |
404 return -1; | 396 return -1; |
405 } else { | |
406 break; | |
407 } | 397 } |
408 } else { | 398 } else { |
409 hexValue = readHex(4); | 399 hexValue = readHex(4); |
410 break; | |
411 } | 400 } |
| 401 break; |
412 default: return ch; | 402 default: return ch; |
413 } | 403 } |
414 | 404 |
415 if (hexValue == -1) return -1; | 405 if (hexValue == -1) return -1; |
416 | 406 |
417 // According to the Unicode standard the high and low surrogate halves | 407 // According to the Unicode standard the high and low surrogate halves |
418 // used by UTF-16 (U+D800 through U+DFFF) and values above U+10FFFF | 408 // used by UTF-16 (U+D800 through U+DFFF) and values above U+10FFFF |
419 // are not legal Unicode values. | 409 // are not legal Unicode values. |
420 if (hexValue < 0xD800 || hexValue > 0xDFFF && hexValue <= 0xFFFF) { | 410 if (hexValue < 0xD800 || hexValue > 0xDFFF && hexValue <= 0xFFFF) { |
421 return hexValue; | 411 return hexValue; |
422 } else if (hexValue <= 0x10FFFF){ | 412 } else if (hexValue <= 0x10FFFF){ |
423 world.fatal('unicode values greater than 2 bytes not implemented yet'); | 413 messages.error('unicode values greater than 2 bytes not implemented yet', |
| 414 _file.span(_startIndex, _startIndex + 1)); |
424 return -1; | 415 return -1; |
425 } else { | 416 } else { |
426 return -1; | 417 return -1; |
427 } | 418 } |
428 } | 419 } |
429 | 420 |
430 Token finishDot() { | 421 Token finishDot() { |
431 if (TokenizerHelpers.isDigit(_peekChar())) { | 422 if (TokenizerHelpers.isDigit(_peekChar())) { |
432 eatDigits(); | 423 eatDigits(); |
433 return finishNumberExtra(TokenKind.DOUBLE); | 424 return finishNumberExtra(TokenKind.DOUBLE); |
(...skipping 11 matching lines...) Expand all Loading... |
445 } | 436 } |
446 int kind = getIdentifierKind(); | 437 int kind = getIdentifierKind(); |
447 if (kind == TokenKind.IDENTIFIER) { | 438 if (kind == TokenKind.IDENTIFIER) { |
448 return _finishToken(TokenKind.IDENTIFIER); | 439 return _finishToken(TokenKind.IDENTIFIER); |
449 } else { | 440 } else { |
450 return _finishToken(kind); | 441 return _finishToken(kind); |
451 } | 442 } |
452 } | 443 } |
453 } | 444 } |
454 | 445 |
OLD | NEW |