@Override
  public SQLSelectQuery query() {
    if (lexer.token() == (Token.LPAREN)) {
      lexer.nextToken();

      SQLSelectQuery select = query();
      accept(Token.RPAREN);

      return queryRest(select);
    }

    MySqlSelectQueryBlock queryBlock = new MySqlSelectQueryBlock();

    if (lexer.token() == Token.SELECT) {
      lexer.nextToken();

      if (lexer.token() == Token.HINT) {
        this.exprParser.parseHints(queryBlock.getHints());
      }

      if (lexer.token() == Token.COMMENT) {
        lexer.nextToken();
      }

      if (lexer.token() == (Token.DISTINCT)) {
        queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT);
        lexer.nextToken();
      } else if (identifierEquals("DISTINCTROW")) {
        queryBlock.setDistionOption(SQLSetQuantifier.DISTINCTROW);
        lexer.nextToken();
      } else if (lexer.token() == (Token.ALL)) {
        queryBlock.setDistionOption(SQLSetQuantifier.ALL);
        lexer.nextToken();
      }

      if (identifierEquals("HIGH_PRIORITY")) {
        queryBlock.setHignPriority(true);
        lexer.nextToken();
      }

      if (identifierEquals("STRAIGHT_JOIN")) {
        queryBlock.setStraightJoin(true);
        lexer.nextToken();
      }

      if (identifierEquals("SQL_SMALL_RESULT")) {
        queryBlock.setSmallResult(true);
        lexer.nextToken();
      }

      if (identifierEquals("SQL_BIG_RESULT")) {
        queryBlock.setBigResult(true);
        lexer.nextToken();
      }

      if (identifierEquals("SQL_BUFFER_RESULT")) {
        queryBlock.setBufferResult(true);
        lexer.nextToken();
      }

      if (identifierEquals("SQL_CACHE")) {
        queryBlock.setCache(true);
        lexer.nextToken();
      }

      if (identifierEquals("SQL_NO_CACHE")) {
        queryBlock.setCache(false);
        lexer.nextToken();
      }

      if (identifierEquals("SQL_CALC_FOUND_ROWS")) {
        queryBlock.setCalcFoundRows(true);
        lexer.nextToken();
      }

      parseSelectList(queryBlock);

      argsList = parseIntoArgs();
    }

    parseFrom(queryBlock);

    parseWhere(queryBlock);

    parseGroupBy(queryBlock);

    queryBlock.setOrderBy(this.exprParser.parseOrderBy());

    if (lexer.token() == Token.LIMIT) {
      queryBlock.setLimit(parseLimit());
    }

    if (lexer.token() == Token.PROCEDURE) {
      lexer.nextToken();
      throw new ParserException("TODO");
    }

    parseInto(queryBlock);

    if (lexer.token() == Token.FOR) {
      lexer.nextToken();
      accept(Token.UPDATE);

      queryBlock.setForUpdate(true);
    }

    if (lexer.token() == Token.LOCK) {
      lexer.nextToken();
      accept(Token.IN);
      acceptIdentifier("SHARE");
      acceptIdentifier("MODE");
      queryBlock.setLockInShareMode(true);
    }

    return queryRest(queryBlock);
  }