next method
Token
next(
{ - bool unicodeRange = false,
})
override
Implementation
@override
Token next({bool unicodeRange = false}) {
// keep track of our starting position
_startIndex = _index;
int ch;
ch = _nextChar();
switch (ch) {
case TokenChar.NEWLINE:
case TokenChar.RETURN:
case TokenChar.SPACE:
case TokenChar.TAB:
return finishWhitespace();
case TokenChar.END_OF_FILE:
return _finishToken(TokenKind.END_OF_FILE);
case TokenChar.AT:
var peekCh = _peekChar();
if (TokenizerHelpers.isIdentifierStart(peekCh)) {
_startIndex = _index;
ch = _nextChar();
finishIdentifier();
// Is it a directive?
var tokId = TokenKind.matchDirectives(_text, _startIndex, _index - _startIndex);
if (tokId == -1) {
// No, is it a margin directive?
tokId = TokenKind.matchMarginDirectives(_text, _startIndex, _index - _startIndex);
}
if (tokId != -1) {
return _finishToken(tokId);
}
}
return _finishToken(TokenKind.AT);
case TokenChar.DOT:
var start = _startIndex; // Start where the dot started.
if (maybeEatDigit()) {
// looks like a number dot followed by digit(s).
var number = finishNumber();
if (number.kind == TokenKind.INTEGER) {
// It's a number but it's preceeded by a dot, so make it a double.
_startIndex = start;
return _finishToken(TokenKind.DOUBLE);
} else {
// Don't allow dot followed by a double (e.g, '..1').
return _errorToken();
}
}
// It's really a dot.
return _finishToken(TokenKind.DOT);
case TokenChar.LPAREN:
return _finishToken(TokenKind.LPAREN);
case TokenChar.RPAREN:
return _finishToken(TokenKind.RPAREN);
case TokenChar.LBRACE:
return _finishToken(TokenKind.LBRACE);
case TokenChar.RBRACE:
return _finishToken(TokenKind.RBRACE);
case TokenChar.LBRACK:
return _finishToken(TokenKind.LBRACK);
case TokenChar.RBRACK:
if (_maybeEatChar(TokenChar.RBRACK) && _maybeEatChar(TokenChar.GREATER)) {
// ]]>
return next();
}
return _finishToken(TokenKind.RBRACK);
case TokenChar.HASH:
return _finishToken(TokenKind.HASH);
case TokenChar.PLUS:
if (_nextCharsAreNumber(ch)) return finishNumber();
return _finishToken(TokenKind.PLUS);
case TokenChar.MINUS:
if (inSelectorExpression || unicodeRange) {
// If parsing in pseudo function expression then minus is an operator
// not part of identifier e.g., interval value range (e.g. U+400-4ff)
// or minus operator in selector expression.
return _finishToken(TokenKind.MINUS);
} else if (_nextCharsAreNumber(ch)) {
return finishNumber();
} else if (TokenizerHelpers.isIdentifierStart(ch)) {
return finishIdentifier();
}
return _finishToken(TokenKind.MINUS);
case TokenChar.GREATER:
return _finishToken(TokenKind.GREATER);
case TokenChar.TILDE:
if (_maybeEatChar(TokenChar.EQUALS)) {
return _finishToken(TokenKind.INCLUDES); // ~=
}
return _finishToken(TokenKind.TILDE);
case TokenChar.ASTERISK:
if (_maybeEatChar(TokenChar.EQUALS)) {
return _finishToken(TokenKind.SUBSTRING_MATCH); // *=
}
return _finishToken(TokenKind.ASTERISK);
case TokenChar.AMPERSAND:
return _finishToken(TokenKind.AMPERSAND);
case TokenChar.NAMESPACE:
if (_maybeEatChar(TokenChar.EQUALS)) {
return _finishToken(TokenKind.DASH_MATCH); // |=
}
return _finishToken(TokenKind.NAMESPACE);
case TokenChar.COLON:
return _finishToken(TokenKind.COLON);
case TokenChar.COMMA:
return _finishToken(TokenKind.COMMA);
case TokenChar.SEMICOLON:
return _finishToken(TokenKind.SEMICOLON);
case TokenChar.PERCENT:
return _finishToken(TokenKind.PERCENT);
case TokenChar.SINGLE_QUOTE:
return _finishToken(TokenKind.SINGLE_QUOTE);
case TokenChar.DOUBLE_QUOTE:
return _finishToken(TokenKind.DOUBLE_QUOTE);
case TokenChar.SLASH:
if (_maybeEatChar(TokenChar.ASTERISK)) return finishMultiLineComment();
return _finishToken(TokenKind.SLASH);
case TokenChar.LESS: // <!--
if (_maybeEatChar(TokenChar.BANG)) {
if (_maybeEatChar(TokenChar.MINUS) && _maybeEatChar(TokenChar.MINUS)) {
return finishHtmlComment();
} else if (_maybeEatChar(TokenChar.LBRACK) &&
_maybeEatChar(CDATA_NAME[0]) &&
_maybeEatChar(CDATA_NAME[1]) &&
_maybeEatChar(CDATA_NAME[2]) &&
_maybeEatChar(CDATA_NAME[3]) &&
_maybeEatChar(CDATA_NAME[4]) &&
_maybeEatChar(TokenChar.LBRACK)) {
// <![CDATA[
return next();
}
}
return _finishToken(TokenKind.LESS);
case TokenChar.EQUALS:
return _finishToken(TokenKind.EQUALS);
case TokenChar.CARET:
if (_maybeEatChar(TokenChar.EQUALS)) {
return _finishToken(TokenKind.PREFIX_MATCH); // ^=
}
return _finishToken(TokenKind.CARET);
case TokenChar.DOLLAR:
if (_maybeEatChar(TokenChar.EQUALS)) {
return _finishToken(TokenKind.SUFFIX_MATCH); // $=
}
return _finishToken(TokenKind.DOLLAR);
case TokenChar.BANG:
return finishIdentifier();
default:
// TODO(jmesserly): this is used for IE8 detection; I'm not sure it's
// appropriate outside of a few specific places; certainly shouldn't
// be parsed in selectors.
if (!inSelector && ch == TokenChar.BACKSLASH) {
return _finishToken(TokenKind.BACKSLASH);
}
if (unicodeRange) {
// Three types of unicode ranges:
// - single code point (e.g. U+416)
// - interval value range (e.g. U+400-4ff)
// - range where trailing ‘?’ characters imply ‘any digit value’
// (e.g. U+4??)
if (maybeEatHexDigit()) {
var t = finishHexNumber();
// Any question marks then it's a HEX_RANGE not HEX_NUMBER.
if (maybeEatQuestionMark()) finishUnicodeRange();
return t;
} else if (maybeEatQuestionMark()) {
// HEX_RANGE U+N???
return finishUnicodeRange();
} else {
return _errorToken();
}
} else if (_inString && (ch == UNICODE_U || ch == UNICODE_LOWER_U) && (_peekChar() == UNICODE_PLUS)) {
// `_inString` is misleading. We actually DON'T want to enter this
// block while tokenizing a string, but the parser sets this value to
// false while it IS consuming tokens within a string.
//
// Unicode range: U+uNumber[-U+uNumber]
// uNumber = 0..10FFFF
_nextChar(); // Skip +
_startIndex = _index; // Starts at the number
return _finishToken(TokenKind.UNICODE_RANGE);
} else if (varDef(ch)) {
return _finishToken(TokenKind.VAR_DEFINITION);
} else if (varUsage(ch)) {
return _finishToken(TokenKind.VAR_USAGE);
} else if (TokenizerHelpers.isIdentifierStart(ch)) {
return finishIdentifier();
} else if (TokenizerHelpers.isDigit(ch)) {
return finishNumber();
}
return _errorToken();
}
}