| 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 |