Expr AtomicExpr()

in MySQL.Data/src/X/Protocol/X/ExprParser.cs [837:1027]


    Expr AtomicExpr()
    { // constant, identifier, variable, function call, etc
      if (this.tokenPos >= this.tokens.Count)
      {
        throw new ArgumentException("No more tokens when expecting one at token pos " + this.tokenPos);
      }
      Token t = this.tokens[this.tokenPos];
      this.tokenPos++; // consume
      switch (t.type)
      {
        case TokenType.EROTEME:
        case TokenType.COLON:
          {
            string placeholderName;
            if (CurrentTokenTypeEquals(TokenType.LNUM_INT))
            {
              // int pos = Integer.valueOf(consumeToken(TokenType.LNUM_INT));
              // return Expr.CreateBuilder().setType(Expr.Type.PLACEHOLDER).setPosition(pos).Build();
              placeholderName = ConsumeToken(TokenType.LNUM_INT);
            }
            else if (CurrentTokenTypeEquals(TokenType.IDENT))
            {
              placeholderName = ConsumeToken(TokenType.IDENT);
            }
            else if (t.type == TokenType.EROTEME)
            {
              placeholderName = this.positionalPlaceholderCount.ToString();
            }
            else
            {
              throw new ArgumentException("Invalid placeholder name at token pos " + this.tokenPos);
            }
            placeholderName = placeholderName.ToLowerInvariant();
            Expr placeholder = new Expr();
            placeholder.Type = Expr.Types.Type.Placeholder;
            if (this.placeholderNameToPosition.ContainsKey(placeholderName))
            {
              placeholder.Position = (uint)this.placeholderNameToPosition[placeholderName];
            }
            else
            {
              placeholder.Position = (uint)this.positionalPlaceholderCount;
              this.placeholderNameToPosition.Add(placeholderName, this.positionalPlaceholderCount);
              this.positionalPlaceholderCount++;
            }
            return placeholder;
          }
        case TokenType.LPAREN:
          Expr e = GetExpr();
          ConsumeToken(TokenType.RPAREN);
          return e;
        case TokenType.LSQBRACKET:
          {
            Mysqlx.Expr.Array builder = new Mysqlx.Expr.Array();
            ParseCommaSeparatedList(() =>
            {
              return GetExpr();
            }).ForEach(f => builder.Value.Add(f));
            ConsumeToken(TokenType.RSQBRACKET);
            return new Expr() { Type = Expr.Types.Type.Array, Array = builder };
          }
        case TokenType.LCURLY:  // JSON object
          {
            Mysqlx.Expr.Object builder = new Mysqlx.Expr.Object();
            if (CurrentTokenTypeEquals(TokenType.LSTRING))
            {
              ParseCommaSeparatedList(() =>
              {
                string key = ConsumeToken(TokenType.LSTRING);
                ConsumeToken(TokenType.COLON);
                Expr value = GetExpr();
                Mysqlx.Expr.Object.Types.ObjectField objectField = new Mysqlx.Expr.Object.Types.ObjectField();
                objectField.Key = key;
                objectField.Value = value;
                return objectField;
              }).ForEach(f => builder.Fld.Add(f));
            }
            ConsumeToken(TokenType.RCURLY);
            return new Expr() { Type = Expr.Types.Type.Object, Object = builder };
          }
        case TokenType.CAST:
          {
            ConsumeToken(TokenType.LPAREN);
            Operator builder = new Operator();
            builder.Name = TokenType.CAST.ToString().ToLowerInvariant();
            builder.Param.Add(GetExpr());
            ConsumeToken(TokenType.AS);
            StringBuilder typeStr = new StringBuilder(this.tokens[this.tokenPos].value.ToUpperInvariant());
            // ensure next token is a valid type argument to CAST
            if (CurrentTokenTypeEquals(TokenType.DECIMAL))
            {
              this.tokenPos++;
              if (CurrentTokenTypeEquals(TokenType.LPAREN))
              {
                typeStr.Append(ConsumeToken(TokenType.LPAREN));
                typeStr.Append(ConsumeToken(TokenType.LNUM_INT));
                if (CurrentTokenTypeEquals(TokenType.COMMA))
                {
                  typeStr.Append(ConsumeToken(TokenType.COMMA));
                  typeStr.Append(ConsumeToken(TokenType.LNUM_INT));
                }
                typeStr.Append(ConsumeToken(TokenType.RPAREN));
              }
            }
            else if (CurrentTokenTypeEquals(TokenType.CHAR) || CurrentTokenTypeEquals(TokenType.BINARY))
            {
              this.tokenPos++;
              if (CurrentTokenTypeEquals(TokenType.LPAREN))
              {
                typeStr.Append(ConsumeToken(TokenType.LPAREN));
                typeStr.Append(ConsumeToken(TokenType.LNUM_INT));
                typeStr.Append(ConsumeToken(TokenType.RPAREN));
              }
            }
            else if (CurrentTokenTypeEquals(TokenType.UNSIGNED) || CurrentTokenTypeEquals(TokenType.SIGNED))
            {
              this.tokenPos++;
              if (CurrentTokenTypeEquals(TokenType.INTEGER))
              {
                // don't add optional INTEGER to type string argument
                ConsumeToken(TokenType.INTEGER);
              }
            }
            else if (CurrentTokenTypeEquals(TokenType.JSON) || CurrentTokenTypeEquals(TokenType.DATE) || CurrentTokenTypeEquals(TokenType.DATETIME) ||
                  CurrentTokenTypeEquals(TokenType.TIME))
            {
              this.tokenPos++;
            }
            else
            {
              throw new ArgumentException("Expected valid CAST type argument at " + this.tokenPos);
            }
            ConsumeToken(TokenType.RPAREN);
            // TODO charset?
            builder.Param.Add(ExprUtil.BuildLiteralScalar(Encoding.UTF8.GetBytes(typeStr.ToString())));
            return new Expr() { Type = Expr.Types.Type.Operator, Operator = builder };
          }
        case TokenType.PLUS:
        case TokenType.MINUS:
          {
            if (CurrentTokenTypeEquals(TokenType.LNUM_INT) || CurrentTokenTypeEquals(TokenType.LNUM_DOUBLE))
            {
              // unary operators are handled inline making positive or negative numeric literals
              this.tokens[this.tokenPos].value = t.value + this.tokens[this.tokenPos].value;
              return AtomicExpr();
            }
          }
          return BuildUnaryOp(t.value, AtomicExpr());
        case TokenType.NOT:
        case TokenType.NEG:
        case TokenType.BANG:
          return BuildUnaryOp(t.value, AtomicExpr());
        case TokenType.LSTRING:
          return ExprUtil.BuildLiteralScalar(t.value);
        case TokenType.NULL:
          return ExprUtil.BuildLiteralNullScalar();
        case TokenType.LNUM_INT:
          return ExprUtil.BuildLiteralScalar(long.Parse(t.value));
        case TokenType.LNUM_DOUBLE:
          return ExprUtil.BuildLiteralScalar(double.Parse(t.value));
        case TokenType.TRUE:
        case TokenType.FALSE:
          return ExprUtil.BuildLiteralScalar(t.type == TokenType.TRUE);
        case TokenType.AT:
          return DocumentField();
        case TokenType.STAR:
          // special "0-ary" consideration of "*" as an operator (for COUNT(*), etc)
          return StarOperator();
        case TokenType.IDENT:
          this.tokenPos--; // stay on the identifier
          // check for function call which may be: func(...) or schema.func(...)
          if (NextTokenTypeEquals(TokenType.LPAREN)
                  || (PosTokenTypeEquals(this.tokenPos + 1, TokenType.DOT) && PosTokenTypeEquals(this.tokenPos + 2, TokenType.IDENT) && PosTokenTypeEquals(
                          this.tokenPos + 3, TokenType.LPAREN)))
          {
            return ParseFunctionCall();
          }
          else
          {
            if (this.allowRelationalColumns)
            {
              return ParseColumnIdentifier();
            }
            else
            {
              return DocumentField();
            }
          }
      }
      throw new ArgumentException("Cannot find atomic expression at token pos: " + (this.tokenPos - 1));
    }