next method

  1. @override
Token next({
  1. 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)) {
        var oldIndex = _index;
        var oldStartIndex = _startIndex;

        _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);
        } else {
          // Didn't find a CSS directive or margin directive so the @name is
          // probably the Less definition '@name: value_variable_definition'.
          _startIndex = oldStartIndex;
          _index = oldIndex;
        }
      }
      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 preceded 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();
  }
}