| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 class Tokenizer extends lang.TokenizerBase { | 5 class Tokenizer extends CSSTokenizerBase { |
| 6 TokenKind cssTokens; | 6 TokenKind cssTokens; |
| 7 | 7 |
| 8 bool _selectorParsing; | 8 bool _selectorParsing; |
| 9 | 9 |
| 10 Tokenizer(lang.SourceFile source, bool skipWhitespace, [int index = 0]) | 10 Tokenizer(SourceFile source, bool skipWhitespace, [int index = 0]) |
| 11 : super(source, skipWhitespace, index), _selectorParsing = false { | 11 : super(source, skipWhitespace, index), _selectorParsing = false { |
| 12 cssTokens = new TokenKind(); | 12 cssTokens = new TokenKind(); |
| 13 } | 13 } |
| 14 | 14 |
| 15 lang.Token next() { | 15 int get startIndex() => _startIndex; |
| 16 |
| 17 Token next() { |
| 16 // keep track of our starting position | 18 // keep track of our starting position |
| 17 _startIndex = _index; | 19 _startIndex = _index; |
| 18 | 20 |
| 19 if (_interpStack != null && _interpStack.depth == 0) { | 21 if (_interpStack != null && _interpStack.depth == 0) { |
| 20 var istack = _interpStack; | 22 var istack = _interpStack; |
| 21 _interpStack = _interpStack.pop(); | 23 _interpStack = _interpStack.pop(); |
| 22 | 24 |
| 23 /* TODO(terry): Enable for variable and string interpolation. | 25 /* TODO(terry): Enable for variable and string interpolation. |
| 24 * if (istack.isMultiline) { | 26 * if (istack.isMultiline) { |
| 25 * return finishMultilineStringBody(istack.quote); | 27 * return finishMultilineStringBody(istack.quote); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 40 case cssTokens.tokens[TokenKind.RETURN]: | 42 case cssTokens.tokens[TokenKind.RETURN]: |
| 41 return finishWhitespace(); | 43 return finishWhitespace(); |
| 42 case cssTokens.tokens[TokenKind.END_OF_FILE]: | 44 case cssTokens.tokens[TokenKind.END_OF_FILE]: |
| 43 return _finishToken(TokenKind.END_OF_FILE); | 45 return _finishToken(TokenKind.END_OF_FILE); |
| 44 case cssTokens.tokens[TokenKind.AT]: | 46 case cssTokens.tokens[TokenKind.AT]: |
| 45 return _finishToken(TokenKind.AT); | 47 return _finishToken(TokenKind.AT); |
| 46 case cssTokens.tokens[TokenKind.DOT]: | 48 case cssTokens.tokens[TokenKind.DOT]: |
| 47 int start = _startIndex; // Start where the dot started. | 49 int start = _startIndex; // Start where the dot started. |
| 48 if (maybeEatDigit()) { | 50 if (maybeEatDigit()) { |
| 49 // looks like a number dot followed by digit(s). | 51 // looks like a number dot followed by digit(s). |
| 50 lang.Token num = finishNumber(); | 52 Token number = finishNumber(); |
| 51 if (num.kind == TokenKind.INTEGER) { | 53 if (number.kind == TokenKind.INTEGER) { |
| 52 // It's a number but it's preceeded by a dot, so make it a double. | 54 // It's a number but it's preceeded by a dot, so make it a double. |
| 53 _startIndex = start; | 55 _startIndex = start; |
| 54 return _finishToken(TokenKind.DOUBLE); | 56 return _finishToken(TokenKind.DOUBLE); |
| 55 } else { | 57 } else { |
| 56 // Don't allow dot followed by a double (e.g, '..1'). | 58 // Don't allow dot followed by a double (e.g, '..1'). |
| 57 return _errorToken(); | 59 return _errorToken(); |
| 58 } | 60 } |
| 59 } else { | 61 } else { |
| 60 // It's really a dot. | 62 // It's really a dot. |
| 61 return _finishToken(TokenKind.DOT); | 63 return _finishToken(TokenKind.DOT); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 77 case cssTokens.tokens[TokenKind.PLUS]: | 79 case cssTokens.tokens[TokenKind.PLUS]: |
| 78 if (maybeEatDigit()) { | 80 if (maybeEatDigit()) { |
| 79 return finishNumber(); | 81 return finishNumber(); |
| 80 } else { | 82 } else { |
| 81 return _finishToken(TokenKind.PLUS); | 83 return _finishToken(TokenKind.PLUS); |
| 82 } | 84 } |
| 83 case cssTokens.tokens[TokenKind.MINUS]: | 85 case cssTokens.tokens[TokenKind.MINUS]: |
| 84 if (maybeEatDigit()) { | 86 if (maybeEatDigit()) { |
| 85 return finishNumber(); | 87 return finishNumber(); |
| 86 } else if (TokenizerHelpers.isIdentifierStart(ch)) { | 88 } else if (TokenizerHelpers.isIdentifierStart(ch)) { |
| 87 return this.finishIdentifier(); | 89 return this.finishIdentifier(ch); |
| 88 } else { | 90 } else { |
| 89 return _finishToken(TokenKind.MINUS); | 91 return _finishToken(TokenKind.MINUS); |
| 90 } | 92 } |
| 91 case cssTokens.tokens[TokenKind.GREATER]: | 93 case cssTokens.tokens[TokenKind.GREATER]: |
| 92 return _finishToken(TokenKind.GREATER); | 94 return _finishToken(TokenKind.GREATER); |
| 93 case cssTokens.tokens[TokenKind.TILDE]: | 95 case cssTokens.tokens[TokenKind.TILDE]: |
| 94 if (_maybeEatChar(cssTokens.tokens[TokenKind.EQUALS])) { | 96 if (_maybeEatChar(cssTokens.tokens[TokenKind.EQUALS])) { |
| 95 return _finishToken(TokenKind.INCLUDES); // ~= | 97 return _finishToken(TokenKind.INCLUDES); // ~= |
| 96 } else { | 98 } else { |
| 97 return _finishToken(TokenKind.TILDE); | 99 return _finishToken(TokenKind.TILDE); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 144 } else { | 146 } else { |
| 145 return _finishToken(TokenKind.CARET); | 147 return _finishToken(TokenKind.CARET); |
| 146 } | 148 } |
| 147 case cssTokens.tokens[TokenKind.DOLLAR]: | 149 case cssTokens.tokens[TokenKind.DOLLAR]: |
| 148 if (_maybeEatChar(cssTokens.tokens[TokenKind.EQUALS])) { | 150 if (_maybeEatChar(cssTokens.tokens[TokenKind.EQUALS])) { |
| 149 return _finishToken(TokenKind.SUFFIX_MATCH); // $= | 151 return _finishToken(TokenKind.SUFFIX_MATCH); // $= |
| 150 } else { | 152 } else { |
| 151 return _finishToken(TokenKind.DOLLAR); | 153 return _finishToken(TokenKind.DOLLAR); |
| 152 } | 154 } |
| 153 case cssTokens.tokens[TokenKind.BANG]: | 155 case cssTokens.tokens[TokenKind.BANG]: |
| 154 lang.Token tok = finishIdentifier(); | 156 Token tok = finishIdentifier(ch); |
| 155 return (tok == null) ? _finishToken(TokenKind.BANG) : tok; | 157 return (tok == null) ? _finishToken(TokenKind.BANG) : tok; |
| 156 default: | 158 default: |
| 157 if (TokenizerHelpers.isIdentifierStart(ch)) { | 159 if (TokenizerHelpers.isIdentifierStart(ch)) { |
| 158 return this.finishIdentifier(); | 160 return this.finishIdentifier(ch); |
| 159 } else if (isDigit(ch)) { | 161 } else if (isDigit(ch)) { |
| 160 return this.finishNumber(); | 162 return this.finishNumber(); |
| 161 } else { | 163 } else { |
| 162 return _errorToken(); | 164 return _errorToken(); |
| 163 } | 165 } |
| 164 } | 166 } |
| 165 } | 167 } |
| 166 | 168 |
| 167 // TODO(jmesserly): we need a way to emit human readable error messages from | 169 // TODO(jmesserly): we need a way to emit human readable error messages from |
| 168 // the tokenizer. | 170 // the tokenizer. |
| 169 lang.Token _errorToken() { | 171 Token _errorToken([String message = null]) { |
| 170 return _finishToken(TokenKind.ERROR); | 172 return _finishToken(TokenKind.ERROR); |
| 171 } | 173 } |
| 172 | 174 |
| 173 int getIdentifierKind() { | 175 int getIdentifierKind() { |
| 174 // Is the identifier a unit type? | 176 // Is the identifier a unit type? |
| 175 int tokId = TokenKind.matchUnits(_text, _startIndex, _index - _startIndex); | 177 int tokId = TokenKind.matchUnits(_text, _startIndex, _index - _startIndex); |
| 176 if (tokId == -1) { | 178 if (tokId == -1) { |
| 177 // No, is it a directive? | 179 // No, is it a directive? |
| 178 tokId = TokenKind.matchDirectives(_text, _startIndex, _index - _startIndex
); | 180 tokId = TokenKind.matchDirectives(_text, _startIndex, _index - _startIndex
); |
| 179 } | 181 } |
| 180 if (tokId == -1) { | 182 if (tokId == -1) { |
| 181 tokId = (_text.substring(_startIndex, _index) == '!important') ? | 183 tokId = (_text.substring(_startIndex, _index) == '!important') ? |
| 182 TokenKind.IMPORTANT : -1; | 184 TokenKind.IMPORTANT : -1; |
| 183 } | 185 } |
| 184 | 186 |
| 185 return tokId >= 0 ? tokId : TokenKind.IDENTIFIER; | 187 return tokId >= 0 ? tokId : TokenKind.IDENTIFIER; |
| 186 } | 188 } |
| 187 | 189 |
| 188 // Need to override so CSS version of isIdentifierPart is used. | 190 // Need to override so CSS version of isIdentifierPart is used. |
| 189 lang.Token finishIdentifier() { | 191 Token finishIdentifier(int ch) { |
| 190 while (_index < _text.length) { | 192 while (_index < _text.length) { |
| 191 // if (!TokenizerHelpers.isIdentifierPart(_text.charCodeAt(_index++))) { | 193 // if (!TokenizerHelpers.isIdentifierPart(_text.charCodeAt(_index++))) { |
| 192 if (!TokenizerHelpers.isIdentifierPart(_text.charCodeAt(_index))) { | 194 if (!TokenizerHelpers.isIdentifierPart(_text.charCodeAt(_index))) { |
| 193 // _index--; | 195 // _index--; |
| 194 break; | 196 break; |
| 195 } else { | 197 } else { |
| 196 _index += 1; | 198 _index += 1; |
| 197 } | 199 } |
| 198 } | 200 } |
| 199 if (_interpStack != null && _interpStack.depth == -1) { | 201 if (_interpStack != null && _interpStack.depth == -1) { |
| 200 _interpStack.depth = 0; | 202 _interpStack.depth = 0; |
| 201 } | 203 } |
| 202 int kind = getIdentifierKind(); | 204 int kind = getIdentifierKind(); |
| 203 if (kind == TokenKind.IDENTIFIER) { | 205 if (kind == TokenKind.IDENTIFIER) { |
| 204 return _finishToken(TokenKind.IDENTIFIER); | 206 return _finishToken(TokenKind.IDENTIFIER); |
| 205 } else { | 207 } else { |
| 206 return _finishToken(kind); | 208 return _finishToken(kind); |
| 207 } | 209 } |
| 208 } | 210 } |
| 209 | 211 |
| 210 lang.Token finishImportant() { | 212 Token finishImportant() { |
| 211 | 213 |
| 212 } | 214 } |
| 213 | 215 |
| 214 lang.Token finishNumber() { | 216 Token finishNumber() { |
| 215 eatDigits(); | 217 eatDigits(); |
| 216 | 218 |
| 217 if (_peekChar() == 46/*.*/) { | 219 if (_peekChar() == 46/*.*/) { |
| 218 // Handle the case of 1.toString(). | 220 // Handle the case of 1.toString(). |
| 219 _nextChar(); | 221 _nextChar(); |
| 220 if (isDigit(_peekChar())) { | 222 if (isDigit(_peekChar())) { |
| 221 eatDigits(); | 223 eatDigits(); |
| 222 return _finishToken(TokenKind.DOUBLE); | 224 return _finishToken(TokenKind.DOUBLE); |
| 223 } else { | 225 } else { |
| 224 _index -= 1; | 226 _index -= 1; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 247 } | 249 } |
| 248 | 250 |
| 249 bool maybeEatHexDigit() { | 251 bool maybeEatHexDigit() { |
| 250 if (_index < _text.length && isHexDigit(_text.charCodeAt(_index))) { | 252 if (_index < _text.length && isHexDigit(_text.charCodeAt(_index))) { |
| 251 _index += 1; | 253 _index += 1; |
| 252 return true; | 254 return true; |
| 253 } | 255 } |
| 254 return false; | 256 return false; |
| 255 } | 257 } |
| 256 | 258 |
| 257 lang.Token finishMultiLineComment() { | 259 Token finishMultiLineComment() { |
| 258 while (true) { | 260 while (true) { |
| 259 int ch = _nextChar(); | 261 int ch = _nextChar(); |
| 260 if (ch == 0) { | 262 if (ch == 0) { |
| 261 return _finishToken(TokenKind.INCOMPLETE_COMMENT); | 263 return _finishToken(TokenKind.INCOMPLETE_COMMENT); |
| 262 } else if (ch == 42/*'*'*/) { | 264 } else if (ch == 42/*'*'*/) { |
| 263 if (_maybeEatChar(47/*'/'*/)) { | 265 if (_maybeEatChar(47/*'/'*/)) { |
| 264 if (_skipWhitespace) { | 266 if (_skipWhitespace) { |
| 265 return next(); | 267 return next(); |
| 266 } else { | 268 } else { |
| 267 return _finishToken(TokenKind.COMMENT); | 269 return _finishToken(TokenKind.COMMENT); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 279 } | 281 } |
| 280 } | 282 } |
| 281 } | 283 } |
| 282 } | 284 } |
| 283 return _errorToken(); | 285 return _errorToken(); |
| 284 } | 286 } |
| 285 | 287 |
| 286 } | 288 } |
| 287 | 289 |
| 288 /** Static helper methods. */ | 290 /** Static helper methods. */ |
| 291 /** Static helper methods. */ |
| 289 class TokenizerHelpers { | 292 class TokenizerHelpers { |
| 290 static bool isIdentifierStart(int c) => | 293 |
| 291 lang.TokenizerHelpers.isIdentifierStart(c) || c == 95 /*_*/ || | 294 static bool isIdentifierStart(int c) { |
| 292 c == 45; /*-*/ | 295 return ((c >= 97/*a*/ && c <= 122/*z*/) || (c >= 65/*A*/ && c <= 90/*Z*/) || |
| 296 c == 95/*_*/ || c == 45 /*-*/); |
| 297 } |
| 293 | 298 |
| 294 static bool isDigit(int c) => lang.TokenizerHelpers.isDigit(c); | 299 static bool isDigit(int c) { |
| 300 return (c >= 48/*0*/ && c <= 57/*9*/); |
| 301 } |
| 295 | 302 |
| 296 static bool isHexDigit(int c) => lang.TokenizerHelpers.isHexDigit(c); | 303 static bool isHexDigit(int c) { |
| 304 return (isDigit(c) || (c >= 97/*a*/ && c <= 102/*f*/) || (c >= 65/*A*/ && c
<= 70/*F*/)); |
| 305 } |
| 297 | 306 |
| 298 static bool isWhitespace(int c) => lang.TokenizerHelpers.isWhitespace(c); | 307 static bool isWhitespace(int c) { |
| 308 return (c == 32/*' '*/ || c == 9/*'\t'*/ || c == 10/*'\n'*/ || c == 13/*'\r'
*/); |
| 309 } |
| 299 | 310 |
| 300 static bool isIdentifierPart(int c) => | 311 static bool isIdentifierPart(int c) { |
| 301 lang.TokenizerHelpers.isIdentifierPart(c) || c == 45 /*-*/; | 312 return (isIdentifierStart(c) || isDigit(c) || c == 45 /*-*/); |
| 313 } |
| 314 |
| 315 static bool isInterpIdentifierPart(int c) { |
| 316 return (isIdentifierStart(c) || isDigit(c)); |
| 317 } |
| 302 } | 318 } |
| 319 |
| OLD | NEW |