/*
 * GDevelop Core
 * Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
 * reserved. This project is released under the MIT License.
 */
#ifndef GDCORE_EXPRESSIONPARSER2_H
#define GDCORE_EXPRESSIONPARSER2_H

#include <memory>
#include <utility>
#include <vector>

#include "ExpressionParser2Node.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Layout.h"  // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/MakeUnique.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
}  // namespace gd

namespace gd {

/** \brief Parse an expression, returning a tree of node corresponding
 * to the parsed expression.
 *
 * This is a LL(1) parser. This could be extracted to a generic/reusable
 * parser by refactoring out the dependency on gd::MetadataProvider (injecting
 * instead functions to be called to query supported functions).
 *
 * \see gd::ExpressionParserDiagnostic
 * \see gd::ExpressionNode
 */
class GD_CORE_API ExpressionParser2 {
 public:
  ExpressionParser2(const gd::Platform &platform_,
                    const gd::ObjectsContainer &globalObjectsContainer_,
                    const gd::ObjectsContainer &objectsContainer_);
  virtual ~ExpressionParser2(){};

  /**
   * Parse the given expression with the specified type.
   *
   * \param type Type of the expression: "string", "number",
   * type supported by gd::ParameterMetadata::IsObject, types supported by
   * gd::ParameterMetadata::IsExpression or "unknown".
   * \param expression The expression to parse
   * \param objectName Specify the object name, only for the
   * case of "objectvar" type.
   *
   * \return The node representing the expression as a parsed tree.
   */
  std::unique_ptr<ExpressionNode> ParseExpression(
      const gd::String &type,
      const gd::String &expression_,
      const gd::String &objectName = "") {
    expression = expression_;

    currentPosition = 0;
    return Start(type, objectName);
  }

  /**
   * Given an object name (or empty if none) and a behavior name (or empty if
   * none), return the index of the first parameter that is inside the
   * parenthesis: 0, 1 or 2.
   *
   * For example, in an expression like `Object.MyBehavior::Method("hello")`,
   * the parameter "hello" is the second parameter (the first being by
   * convention Object, and the second MyBehavior, also by convention).
   */
  static size_t WrittenParametersFirstIndex(const gd::String &objectName,
                                            const gd::String &behaviorName) {
    // By convention, object is always the first parameter, and behavior the
    // second one.
    return !behaviorName.empty() ? 2 : (!objectName.empty() ? 1 : 0);
  }

 private:
  /** \name Grammar
   * Each method is a part of the grammar.
   */
  ///@{
  std::unique_ptr<ExpressionNode> Start(const gd::String &type,
                                        const gd::String &objectName = "") {
    size_t expressionStartPosition = GetCurrentPosition();
    auto expression = Expression(type, objectName);

    const gd::String &inferredType = expression->type;

    // Check for extra characters at the end of the expression
    if (!IsEndReached()) {
      auto op = gd::make_unique<OperatorNode>(inferredType, ' ');
      op->leftHandSide = std::move(expression);
      op->rightHandSide = ReadUntilEnd("unknown");

      op->rightHandSide->diagnostic = RaiseSyntaxError(
          _("The expression has extra character at the end that should be "
            "removed (or completed if your expression is not finished)."));

      op->location = ExpressionParserLocation(expressionStartPosition,
                                              GetCurrentPosition());
      return std::move(op);
    }

    return expression;
  }

  std::unique_ptr<ExpressionNode> Expression(
      const gd::String &type, const gd::String &objectName = "") {
    SkipAllWhitespaces();

    size_t expressionStartPosition = GetCurrentPosition();
    std::unique_ptr<ExpressionNode> leftHandSide = Term(type, objectName);

    const gd::String &inferredType = leftHandSide->type;

    SkipAllWhitespaces();

    if (IsEndReached()) return leftHandSide;
    if (CheckIfChar(IsExpressionEndingChar)) return leftHandSide;
    if (CheckIfChar(IsExpressionOperator)) {
      auto op = gd::make_unique<OperatorNode>(inferredType, GetCurrentChar());
      op->leftHandSide = std::move(leftHandSide);
      op->diagnostic = ValidateOperator(inferredType, GetCurrentChar());
      SkipChar();
      op->rightHandSide = Expression(inferredType, objectName);

      op->location = ExpressionParserLocation(expressionStartPosition,
                                              GetCurrentPosition());
      return std::move(op);
    }

    if (inferredType == "string") {
      leftHandSide->diagnostic = RaiseSyntaxError(
          "You must add the operator + between texts or expressions. For "
          "example: \"Your name: \" + VariableString(PlayerName).");
    } else if (inferredType == "number") {
      leftHandSide->diagnostic = RaiseSyntaxError(
          "No operator found. Did you forget to enter an operator (like +, -, "
          "* or /) between numbers or expressions?");
    } else {
      leftHandSide->diagnostic = RaiseSyntaxError(
          "More than one term was found. Verify that your expression is "
          "properly written.");
    }

    auto op = gd::make_unique<OperatorNode>(inferredType, ' ');
    op->leftHandSide = std::move(leftHandSide);
    op->rightHandSide = Expression(inferredType, objectName);
    op->location =
        ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
    return std::move(op);
  }

  std::unique_ptr<ExpressionNode> Term(const gd::String &type,
                                       const gd::String &objectName) {
    SkipAllWhitespaces();

    size_t expressionStartPosition = GetCurrentPosition();
    std::unique_ptr<ExpressionNode> factor = Factor(type, objectName);

    const gd::String &inferredType = factor->type;

    SkipAllWhitespaces();

    // This while loop is used instead of a recursion (like in Expression)
    // to guarantee the proper operator precedence. (Expression could also
    // be reworked to use a while loop).
    while (CheckIfChar(IsTermOperator)) {
      auto op = gd::make_unique<OperatorNode>(inferredType, GetCurrentChar());
      op->leftHandSide = std::move(factor);
      op->diagnostic = ValidateOperator(inferredType, GetCurrentChar());
      SkipChar();
      op->rightHandSide = Factor(inferredType, objectName);
      op->location = ExpressionParserLocation(expressionStartPosition,
                                              GetCurrentPosition());
      SkipAllWhitespaces();

      factor = std::move(op);
    }

    return factor;
  };

  std::unique_ptr<ExpressionNode> Factor(const gd::String &type,
                                         const gd::String &objectName) {
    SkipAllWhitespaces();
    size_t expressionStartPosition = GetCurrentPosition();

    if (CheckIfChar(IsQuote)) {
      std::unique_ptr<ExpressionNode> factor = ReadText();
      if (type == "number")
        factor->diagnostic =
            RaiseTypeError(_("You entered a text, but a number was expected."),
                           expressionStartPosition);
      else if (type != "string" && type != "number|string")
        factor->diagnostic = RaiseTypeError(
            _("You entered a text, but this type was expected:") + type,
            expressionStartPosition);

      return factor;
    } else if (CheckIfChar(IsUnaryOperator)) {
      auto unaryOperatorCharacter = GetCurrentChar();
      SkipChar();

      auto operatorOperand = Factor(type, objectName);
      const gd::String &inferredType = operatorOperand->type;

      auto unaryOperator = gd::make_unique<UnaryOperatorNode>(
          inferredType, unaryOperatorCharacter);
      unaryOperator->diagnostic = ValidateUnaryOperator(
          inferredType, unaryOperatorCharacter, expressionStartPosition);
      unaryOperator->factor = std::move(operatorOperand);
      unaryOperator->location = ExpressionParserLocation(
          expressionStartPosition, GetCurrentPosition());

      return std::move(unaryOperator);
    } else if (CheckIfChar(IsNumberFirstChar)) {
      std::unique_ptr<ExpressionNode> factor = ReadNumber();
      if (type == "string")
        factor->diagnostic = RaiseTypeError(
            _("You entered a number, but a text was expected (in quotes)."),
            expressionStartPosition);
      else if (type != "number" && type != "number|string")
        factor->diagnostic = RaiseTypeError(
            _("You entered a number, but this type was expected:") + type,
            expressionStartPosition);

      return factor;
    } else if (CheckIfChar(IsOpeningParenthesis)) {
      SkipChar();
      std::unique_ptr<ExpressionNode> factor = SubExpression(type, objectName);

      if (!CheckIfChar(IsClosingParenthesis)) {
        factor->diagnostic =
            RaiseSyntaxError(_("Missing a closing parenthesis. Add a closing "
                               "parenthesis for each opening parenthesis."));
      }
      SkipIfChar(IsClosingParenthesis);
      return factor;
    } else if (IsIdentifierAllowedChar()) {
      // This is a place where the grammar differs according to the
      // type being expected.
      if (gd::ParameterMetadata::IsExpression("variable", type)) {
        return Variable(type, objectName);
      } else {
        return Identifier(type);
      }
    }

    std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace(type);
    factor->diagnostic = RaiseEmptyError(type, expressionStartPosition);
    return factor;
  }

  std::unique_ptr<SubExpressionNode> SubExpression(
      const gd::String &type, const gd::String &objectName) {
    size_t expressionStartPosition = GetCurrentPosition();

    auto expression = Expression(type, objectName);
    const gd::String &inferredType = expression->type;

    auto subExpression =
        gd::make_unique<SubExpressionNode>(inferredType, std::move(expression));
    subExpression->location =
        ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());

    return std::move(subExpression);
  };

  std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
  Identifier(const gd::String &type) {
    auto identifierAndLocation = ReadIdentifierName();
    gd::String name = identifierAndLocation.name;
    auto nameLocation = identifierAndLocation.location;

    SkipAllWhitespaces();

    // We consider a namespace separator to be allowed here and be part of the
    // function name (or object name, but object names are not allowed to
    // contain a ":"). This is because functions from extensions have their
    // extension name prefix, and separated by the namespace separator. This
    // could maybe be refactored to create different nodes in the future.
    if (IsNamespaceSeparator()) {
      SkipNamespaceSeparator();
      SkipAllWhitespaces();

      auto postNamespaceIdentifierAndLocation = ReadIdentifierName();
      name += NAMESPACE_SEPARATOR;
      name += postNamespaceIdentifierAndLocation.name;
      ExpressionParserLocation completeNameLocation(
          nameLocation.GetStartPosition(),
          postNamespaceIdentifierAndLocation.location.GetEndPosition());
      nameLocation = completeNameLocation;
    }

    if (CheckIfChar(IsOpeningParenthesis)) {
      ExpressionParserLocation openingParenthesisLocation = SkipChar();
      return FreeFunction(type, name, nameLocation, openingParenthesisLocation);
    } else if (CheckIfChar(IsDot)) {
      ExpressionParserLocation dotLocation = SkipChar();
      SkipAllWhitespaces();
      return ObjectFunctionOrBehaviorFunction(
          type, name, nameLocation, dotLocation);
    } else {
      auto identifier = gd::make_unique<IdentifierNode>(name, type);
      if (type == "string") {
        identifier->diagnostic =
            RaiseTypeError(_("You must wrap your text inside double quotes "
                             "(example: \"Hello world\")."),
                           nameLocation.GetStartPosition());
      } else if (type == "number") {
        identifier->diagnostic = RaiseTypeError(
            _("You must enter a number."), nameLocation.GetStartPosition());
      } else if (type == "number|string") {
        identifier->diagnostic = RaiseTypeError(
            _("You must enter a number or a text, wrapped inside double quotes "
              "(example: \"Hello world\")."),
            nameLocation.GetStartPosition());
      } else if (!gd::ParameterMetadata::IsObject(type)) {
        identifier->diagnostic = RaiseTypeError(
            _("You've entered a name, but this type was expected:") + type,
            nameLocation.GetStartPosition());
      }

      identifier->location = ExpressionParserLocation(
          nameLocation.GetStartPosition(), GetCurrentPosition());
      return std::move(identifier);
    }
  }

  std::unique_ptr<VariableNode> Variable(const gd::String &type,
                                         const gd::String &objectName) {
    auto identifierAndLocation = ReadIdentifierName();
    const gd::String &name = identifierAndLocation.name;
    const auto &nameLocation = identifierAndLocation.location;

    auto variable = gd::make_unique<VariableNode>(type, name, objectName);
    variable->child = VariableAccessorOrVariableBracketAccessor();

    variable->location = ExpressionParserLocation(
        nameLocation.GetStartPosition(), GetCurrentPosition());
    variable->nameLocation = nameLocation;
    return std::move(variable);
  }

  std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
  VariableAccessorOrVariableBracketAccessor() {
    size_t childStartPosition = GetCurrentPosition();

    SkipAllWhitespaces();
    if (CheckIfChar(IsOpeningSquareBracket)) {
      SkipChar();
      auto child = gd::make_unique<VariableBracketAccessorNode>(
          Expression("number|string"));

      if (!CheckIfChar(IsClosingSquareBracket)) {
        child->diagnostic =
            RaiseSyntaxError(_("Missing a closing bracket. Add a closing "
                               "bracket for each opening bracket."));
      }
      SkipIfChar(IsClosingSquareBracket);
      child->child = VariableAccessorOrVariableBracketAccessor();
      child->location =
          ExpressionParserLocation(childStartPosition, GetCurrentPosition());

      return std::move(child);
    } else if (CheckIfChar(IsDot)) {
      auto dotLocation = SkipChar();
      SkipAllWhitespaces();

      auto identifierAndLocation = ReadIdentifierName();
      auto child =
          gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
      child->child = VariableAccessorOrVariableBracketAccessor();
      child->nameLocation = identifierAndLocation.location;
      child->dotLocation = dotLocation;
      child->location =
          ExpressionParserLocation(childStartPosition, GetCurrentPosition());

      return std::move(child);
    }

    return std::move(
        std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>());
  }

  std::unique_ptr<FunctionCallNode> FreeFunction(
      const gd::String &type,
      const gd::String &functionFullName,
      const ExpressionParserLocation &identifierLocation,
      const ExpressionParserLocation &openingParenthesisLocation) {
    // TODO: error if trying to use function for type != "number" && != "string"
    // + Test for it

    const gd::ExpressionMetadata &metadata =
        MetadataProvider::GetAnyExpressionMetadata(platform, functionFullName);

    // In case we can't find a valid expression, ensure the node has the type
    // that is requested by the parent, so we avoid putting "unknown" (which
    // would be also correct, but less precise and would prevent completions to
    // be shown to the user)
    const gd::String returnType =
        gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
            ? type
            : metadata.GetReturnType();

    auto parametersNode = Parameters(metadata.parameters);
    auto function =
        gd::make_unique<FunctionCallNode>(returnType,
                                          std::move(parametersNode.parameters),
                                          metadata,
                                          functionFullName);
    function->diagnostic = std::move(parametersNode.diagnostic);
    if (!function->diagnostic)  // TODO: reverse the order of diagnostic?
      function->diagnostic = ValidateFunction(
          type, *function, identifierLocation.GetStartPosition());

    function->location = ExpressionParserLocation(
        identifierLocation.GetStartPosition(), GetCurrentPosition());
    function->functionNameLocation = identifierLocation;
    function->openingParenthesisLocation = openingParenthesisLocation;
    function->closingParenthesisLocation =
        parametersNode.closingParenthesisLocation;

    return std::move(function);
  }

  std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode>
  ObjectFunctionOrBehaviorFunction(
      const gd::String &type,
      const gd::String &objectName,
      const ExpressionParserLocation &objectNameLocation,
      const ExpressionParserLocation &objectNameDotLocation) {
    auto identifierAndLocation = ReadIdentifierName();
    const gd::String &objectFunctionOrBehaviorName = identifierAndLocation.name;
    const auto &objectFunctionOrBehaviorNameLocation =
        identifierAndLocation.location;

    SkipAllWhitespaces();

    if (IsNamespaceSeparator()) {
      ExpressionParserLocation namespaceSeparatorLocation =
          SkipNamespaceSeparator();
      SkipAllWhitespaces();
      return BehaviorFunction(type,
                              objectName,
                              objectFunctionOrBehaviorName,
                              objectNameLocation,
                              objectNameDotLocation,
                              objectFunctionOrBehaviorNameLocation,
                              namespaceSeparatorLocation);
    } else if (CheckIfChar(IsOpeningParenthesis)) {
      ExpressionParserLocation openingParenthesisLocation = SkipChar();

      gd::String objectType =
          GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName);

      const gd::ExpressionMetadata &metadata =
          MetadataProvider::GetObjectAnyExpressionMetadata(
              platform, objectType, objectFunctionOrBehaviorName);

      // In case we can't find a valid expression, ensure the node has the type
      // that is requested by the parent, so we avoid putting "unknown" (which
      // would be also correct, but less precise and would prevent completions
      // to be shown to the user)
      const gd::String returnType =
          gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
              ? type
              : metadata.GetReturnType();

      auto parametersNode = Parameters(metadata.parameters, objectName);
      auto function = gd::make_unique<FunctionCallNode>(
          returnType,
          objectName,
          std::move(parametersNode.parameters),
          metadata,
          objectFunctionOrBehaviorName);
      function->diagnostic = std::move(parametersNode.diagnostic);
      if (!function->diagnostic)  // TODO: reverse the order of diagnostic?
        function->diagnostic = ValidateFunction(
            type, *function, objectNameLocation.GetStartPosition());

      function->location = ExpressionParserLocation(
          objectNameLocation.GetStartPosition(), GetCurrentPosition());
      function->objectNameLocation = objectNameLocation;
      function->objectNameDotLocation = objectNameDotLocation;
      function->functionNameLocation = objectFunctionOrBehaviorNameLocation;
      function->openingParenthesisLocation = openingParenthesisLocation;
      function->closingParenthesisLocation =
          parametersNode.closingParenthesisLocation;
      return std::move(function);
    }

    auto node = gd::make_unique<ObjectFunctionNameNode>(
        type, objectName, objectFunctionOrBehaviorName);
    node->diagnostic = RaiseSyntaxError(
        _("An opening parenthesis (for an object expression), or double colon "
          "(::) was expected (for a behavior expression)."));

    node->location = ExpressionParserLocation(
        objectNameLocation.GetStartPosition(), GetCurrentPosition());
    node->objectNameLocation = objectNameLocation;
    node->objectNameDotLocation = objectNameDotLocation;
    node->objectFunctionOrBehaviorNameLocation =
        objectFunctionOrBehaviorNameLocation;
    return std::move(node);
  }

  std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode> BehaviorFunction(
      const gd::String &type,
      const gd::String &objectName,
      const gd::String &behaviorName,
      const ExpressionParserLocation &objectNameLocation,
      const ExpressionParserLocation &objectNameDotLocation,
      const ExpressionParserLocation &behaviorNameLocation,
      const ExpressionParserLocation &behaviorNameNamespaceSeparatorLocation) {
    auto identifierAndLocation = ReadIdentifierName();
    const gd::String &functionName = identifierAndLocation.name;
    const auto &functionNameLocation = identifierAndLocation.location;

    SkipAllWhitespaces();

    if (CheckIfChar(IsOpeningParenthesis)) {
      ExpressionParserLocation openingParenthesisLocation = SkipChar();

      gd::String behaviorType = GetTypeOfBehavior(
          globalObjectsContainer, objectsContainer, behaviorName);

      const gd::ExpressionMetadata &metadata =
          MetadataProvider::GetBehaviorAnyExpressionMetadata(
              platform, behaviorType, functionName);

      // In case we can't find a valid expression, ensure the node has the type
      // that is requested by the parent, so we avoid putting "unknown" (which
      // would be also correct, but less precise and would prevent completions
      // to be shown to the user)
      const gd::String returnType =
          gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
              ? type
              : metadata.GetReturnType();

      auto parametersNode =
          Parameters(metadata.parameters, objectName, behaviorName);
      auto function = gd::make_unique<FunctionCallNode>(
          returnType,
          objectName,
          behaviorName,
          std::move(parametersNode.parameters),
          metadata,
          functionName);
      function->diagnostic = std::move(parametersNode.diagnostic);
      if (!function->diagnostic)  // TODO: reverse the order of diagnostic?
        function->diagnostic = ValidateFunction(
            type, *function, objectNameLocation.GetStartPosition());

      function->location = ExpressionParserLocation(
          objectNameLocation.GetStartPosition(), GetCurrentPosition());
      function->objectNameLocation = objectNameLocation;
      function->objectNameDotLocation = objectNameDotLocation;
      function->behaviorNameLocation = behaviorNameLocation;
      function->behaviorNameNamespaceSeparatorLocation =
          behaviorNameNamespaceSeparatorLocation;
      function->openingParenthesisLocation = openingParenthesisLocation;
      function->closingParenthesisLocation =
          parametersNode.closingParenthesisLocation;
      function->functionNameLocation = functionNameLocation;
      return std::move(function);
    } else {
      auto node = gd::make_unique<ObjectFunctionNameNode>(
          type, objectName, behaviorName, functionName);
      node->diagnostic = RaiseSyntaxError(
          _("An opening parenthesis was expected here to call a function."));

      node->location = ExpressionParserLocation(
          objectNameLocation.GetStartPosition(), GetCurrentPosition());
      node->objectNameLocation = objectNameLocation;
      node->objectNameDotLocation = objectNameDotLocation;
      node->objectFunctionOrBehaviorNameLocation = behaviorNameLocation;
      node->behaviorNameNamespaceSeparatorLocation =
          behaviorNameNamespaceSeparatorLocation;
      node->behaviorFunctionNameLocation = functionNameLocation;
      return std::move(node);
    }
  }

  // A temporary node that will be integrated into function nodes.
  struct ParametersNode {
    std::vector<std::unique_ptr<ExpressionNode>> parameters;
    std::unique_ptr<gd::ExpressionParserError> diagnostic;
    ExpressionParserLocation closingParenthesisLocation;
  };

  ParametersNode Parameters(
      std::vector<gd::ParameterMetadata> parameterMetadata,
      const gd::String &objectName = "",
      const gd::String &behaviorName = "") {
    std::vector<std::unique_ptr<ExpressionNode>> parameters;
    gd::String lastObjectName = "";

    // By convention, object is always the first parameter, and behavior the
    // second one.
    size_t parameterIndex =
        WrittenParametersFirstIndex(objectName, behaviorName);

    while (!IsEndReached()) {
      SkipAllWhitespaces();

      if (CheckIfChar(IsClosingParenthesis)) {
        auto closingParenthesisLocation = SkipChar();
        return ParametersNode{
            std::move(parameters), nullptr, closingParenthesisLocation};
      } else {
        if (parameterIndex < parameterMetadata.size()) {
          const gd::String &type = parameterMetadata[parameterIndex].GetType();
          if (parameterMetadata[parameterIndex].IsCodeOnly()) {
            // Do nothing, code only parameters are not written in expressions.
          } else if (gd::ParameterMetadata::IsExpression("number", type)) {
            parameters.push_back(Expression("number"));
          } else if (gd::ParameterMetadata::IsExpression("string", type)) {
            parameters.push_back(Expression("string"));
          } else if (gd::ParameterMetadata::IsExpression("variable", type)) {
            parameters.push_back(Expression(
                type, lastObjectName.empty() ? objectName : lastObjectName));
          } else if (gd::ParameterMetadata::IsObject(type)) {
            size_t parameterStartPosition = GetCurrentPosition();
            std::unique_ptr<ExpressionNode> objectExpression = Expression(type);

            // Memorize the last object name. By convention, parameters that
            // require an object (mainly, "objectvar" and "behavior") should be
            // placed after the object in the list of parameters (if possible,
            // just after). Search "lastObjectName" in the codebase for other
            // place where this convention is enforced.
            if (auto identifierNode =
                    dynamic_cast<IdentifierNode *>(objectExpression.get())) {
              lastObjectName = identifierNode->identifierName;
            } else {
              objectExpression->diagnostic =
                  gd::make_unique<ExpressionParserError>(
                      "malformed_object_parameter",
                      _("An object name was expected but something else was "
                        "written. Enter just the name of the object for this "
                        "parameter."),
                      parameterStartPosition,
                      GetCurrentPosition());
            }

            parameters.push_back(std::move(objectExpression));
          } else {
            size_t parameterStartPosition = GetCurrentPosition();
            parameters.push_back(Expression("unknown"));
            parameters.back()->diagnostic =
                gd::make_unique<ExpressionParserError>(
                    "unknown_parameter_type",
                    _("This function is improperly set up. Reach out to the "
                      "extension developer or a GDevelop maintainer to fix "
                      "this issue"),
                    parameterStartPosition,
                    GetCurrentPosition());
          }
        } else {
          size_t parameterStartPosition = GetCurrentPosition();
          parameters.push_back(Expression("unknown"));
          parameters.back()
              ->diagnostic = gd::make_unique<ExpressionParserError>(
              "extra_parameter",
              _("This parameter was not expected by this expression. Remove it "
                "or verify that you've entered the proper expression name."),
              parameterStartPosition,
              GetCurrentPosition());
        }

        SkipAllWhitespaces();
        SkipIfChar(IsParameterSeparator);
        parameterIndex++;
      }
    }

    ExpressionParserLocation invalidClosingParenthesisLocation;
    return ParametersNode{
        std::move(parameters),
        RaiseSyntaxError(_("The list of parameters is not terminated. Add a "
                           "closing parenthesis to end the parameters.")),
        invalidClosingParenthesisLocation};
  }
  ///@}

  /** \name Validators
   * Return a diagnostic if any error is found
   */
  ///@{
  std::unique_ptr<ExpressionParserDiagnostic> ValidateFunction(
      const gd::String &type,
      const gd::FunctionCallNode &function,
      size_t functionStartPosition);

  std::unique_ptr<ExpressionParserDiagnostic> ValidateOperator(
      const gd::String &type, gd::String::value_type operatorChar) {
    if (type == "number") {
      if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
          operatorChar == '*') {
        return gd::make_unique<ExpressionParserDiagnostic>();
      }

      return gd::make_unique<ExpressionParserError>(
          "invalid_operator",
          _("You've used an operator that is not supported. Operator should be "
            "either +, -, / or *."),
          GetCurrentPosition());
    } else if (type == "string") {
      if (operatorChar == '+') {
        return gd::make_unique<ExpressionParserDiagnostic>();
      }

      return gd::make_unique<ExpressionParserError>(
          "invalid_operator",
          _("You've used an operator that is not supported. Only + can be used "
            "to concatenate texts."),
          GetCurrentPosition());
    } else if (gd::ParameterMetadata::IsObject(type)) {
      return gd::make_unique<ExpressionParserError>(
          "invalid_operator",
          _("Operators (+, -, /, *) can't be used with an object name. Remove "
            "the operator."),
          GetCurrentPosition());
    } else if (gd::ParameterMetadata::IsExpression("variable", type)) {
      return gd::make_unique<ExpressionParserError>(
          "invalid_operator",
          _("Operators (+, -, /, *) can't be used in variable names. Remove "
            "the operator from the variable name."),
          GetCurrentPosition());
    }

    return gd::make_unique<ExpressionParserDiagnostic>();
  }

  std::unique_ptr<ExpressionParserDiagnostic> ValidateUnaryOperator(
      const gd::String &type,
      gd::String::value_type operatorChar,
      size_t position) {
    if (type == "number") {
      if (operatorChar == '+' || operatorChar == '-') {
        return gd::make_unique<ExpressionParserDiagnostic>();
      }

      return gd::make_unique<ExpressionParserError>(
          "invalid_operator",
          _("You've used an \"unary\" operator that is not supported. Operator "
            "should be "
            "either + or -."),
          position);
    } else if (type == "string") {
      return gd::make_unique<ExpressionParserError>(
          "invalid_operator",
          _("You've used an operator that is not supported. Only + can be used "
            "to concatenate texts, and must be placed between two texts (or "
            "expressions)."),
          position);
    } else if (gd::ParameterMetadata::IsObject(type)) {
      return gd::make_unique<ExpressionParserError>(
          "invalid_operator",
          _("Operators (+, -) can't be used with an object name. Remove the "
            "operator."),
          position);
    } else if (gd::ParameterMetadata::IsExpression("variable", type)) {
      return gd::make_unique<ExpressionParserError>(
          "invalid_operator",
          _("Operators (+, -) can't be used in variable names. Remove "
            "the operator from the variable name."),
          position);
    }

    return gd::make_unique<ExpressionParserDiagnostic>();
  }
  ///@}

  /** \name Parsing tokens
   * Read tokens or characters
   */
  ///@{
  ExpressionParserLocation SkipChar() {
    size_t startPosition = currentPosition;
    return ExpressionParserLocation(startPosition, ++currentPosition);
  }

  void SkipAllWhitespaces() {
    while (currentPosition < expression.size() &&
           IsWhitespace(expression[currentPosition])) {
      currentPosition++;
    }
  }

  void SkipIfChar(
      const std::function<bool(gd::String::value_type)> &predicate) {
    if (CheckIfChar(predicate)) {
      currentPosition++;
    }
  }

  ExpressionParserLocation SkipNamespaceSeparator() {
    size_t startPosition = currentPosition;
    // Namespace separator is a special kind of delimiter as it is 2 characters
    // long
    if (IsNamespaceSeparator()) {
      currentPosition += NAMESPACE_SEPARATOR.size();
    }

    return ExpressionParserLocation(startPosition, currentPosition);
  }

  bool CheckIfChar(
      const std::function<bool(gd::String::value_type)> &predicate) {
    if (currentPosition >= expression.size()) return false;
    gd::String::value_type character = expression[currentPosition];

    return predicate(character);
  }

  bool IsIdentifierAllowedChar() {
    if (currentPosition >= expression.size()) return false;
    gd::String::value_type character = expression[currentPosition];

    // Quickly compare if the character is a number or ASCII character.
    if ((character >= '0' && character <= '9') ||
        (character >= 'A' && character <= 'Z') ||
        (character >= 'a' && character <= 'z'))
      return true;

    // Otherwise do the full check against separators forbidden in identifiers.
    if (!IsParameterSeparator(character) && !IsDot(character) &&
        !IsQuote(character) && !IsBracket(character) &&
        !IsExpressionOperator(character) && !IsTermOperator(character)) {
      return true;
    }

    return false;
  }

  static bool IsWhitespace(gd::String::value_type character) {
    return character == ' ' || character == '\n' || character == '\r';
  }

  static bool IsParameterSeparator(gd::String::value_type character) {
    return character == ',';
  }

  static bool IsDot(gd::String::value_type character) {
    return character == '.';
  }

  static bool IsQuote(gd::String::value_type character) {
    return character == '"';
  }

  static bool IsBracket(gd::String::value_type character) {
    return character == '(' || character == ')' || character == '[' ||
           character == ']' || character == '{' || character == '}';
  }

  static bool IsOpeningParenthesis(gd::String::value_type character) {
    return character == '(';
  }

  static bool IsClosingParenthesis(gd::String::value_type character) {
    return character == ')';
  }

  static bool IsOpeningSquareBracket(gd::String::value_type character) {
    return character == '[';
  }

  static bool IsClosingSquareBracket(gd::String::value_type character) {
    return character == ']';
  }

  static bool IsExpressionEndingChar(gd::String::value_type character) {
    return character == ',' || IsClosingParenthesis(character) ||
           IsClosingSquareBracket(character);
  }

  static bool IsExpressionOperator(gd::String::value_type character) {
    return character == '+' || character == '-' || character == '<' ||
           character == '>' || character == '?' || character == '^' ||
           character == '=' || character == '\\' || character == ':' ||
           character == '!';
  }

  static bool IsUnaryOperator(gd::String::value_type character) {
    return character == '+' || character == '-';
  }

  static bool IsTermOperator(gd::String::value_type character) {
    return character == '/' || character == '*';
  }

  static bool IsNumberFirstChar(gd::String::value_type character) {
    return character == '.' || (character >= '0' && character <= '9');
  }

  static bool IsNonZeroDigit(gd::String::value_type character) {
    return (character >= '1' && character <= '9');
  }

  static bool IsZeroDigit(gd::String::value_type character) {
    return character == '0';
  }

  bool IsNamespaceSeparator() {
    // Namespace separator is a special kind of delimiter as it is 2 characters
    // long
    return (currentPosition + NAMESPACE_SEPARATOR.size() <= expression.size() &&
            expression.substr(currentPosition, NAMESPACE_SEPARATOR.size()) ==
                NAMESPACE_SEPARATOR);
  }

  bool IsEndReached() { return currentPosition >= expression.size(); }

  // A temporary node used when reading an identifier
  struct IdentifierAndLocation {
    gd::String name;
    ExpressionParserLocation location;
  };

  IdentifierAndLocation ReadIdentifierName() {
    gd::String name;
    size_t startPosition = currentPosition;
    while (currentPosition < expression.size() &&
           (IsIdentifierAllowedChar()
            // Allow whitespace in identifier name for compatibility
            || expression[currentPosition] == ' ')) {
      name += expression[currentPosition];
      currentPosition++;
    }

    // Trim whitespace at the end (we allow them for compatibility inside
    // the name, but after the last character that is not whitespace, they
    // should be ignore again).
    if (!name.empty() && IsWhitespace(name[name.size() - 1])) {
      size_t lastCharacterPos = name.size() - 1;
      while (lastCharacterPos < name.size() &&
             IsWhitespace(name[lastCharacterPos])) {
        lastCharacterPos--;
      }
      if ((lastCharacterPos + 1) < name.size()) {
        name.erase(lastCharacterPos + 1);
      }
    }

    IdentifierAndLocation identifierAndLocation{
        name,
        // The location is ignoring the trailing whitespace (only whitespace
        // inside the identifier are allowed for compatibility).
        ExpressionParserLocation(startPosition, startPosition + name.size())};
    return identifierAndLocation;
  }

  std::unique_ptr<TextNode> ReadText();

  std::unique_ptr<NumberNode> ReadNumber();

  std::unique_ptr<EmptyNode> ReadUntilWhitespace(gd::String type) {
    size_t startPosition = GetCurrentPosition();
    gd::String text;
    while (currentPosition < expression.size() &&
           !IsWhitespace(expression[currentPosition])) {
      text += expression[currentPosition];
      currentPosition++;
    }

    auto node = gd::make_unique<EmptyNode>(type, text);
    node->location =
        ExpressionParserLocation(startPosition, GetCurrentPosition());
    return node;
  }

  std::unique_ptr<EmptyNode> ReadUntilEnd(gd::String type) {
    size_t startPosition = GetCurrentPosition();
    gd::String text;
    while (currentPosition < expression.size()) {
      text += expression[currentPosition];
      currentPosition++;
    }

    auto node = gd::make_unique<EmptyNode>(type, text);
    node->location =
        ExpressionParserLocation(startPosition, GetCurrentPosition());
    return node;
  }

  size_t GetCurrentPosition() { return currentPosition; }

  gd::String::value_type GetCurrentChar() {
    if (currentPosition < expression.size()) {
      return expression[currentPosition];
    }

    return '\n';  // Should not arise, unless GetCurrentChar was called when
                  // IsEndReached() is true (which is a logical error).
  }
  ///@}

  /** \name Raising errors
   * Helpers to attach errors to nodes
   */
  ///@{
  std::unique_ptr<ExpressionParserError> RaiseSyntaxError(
      const gd::String &message) {
    return std::move(gd::make_unique<ExpressionParserError>(
        "syntax_error", message, GetCurrentPosition()));
  }

  std::unique_ptr<ExpressionParserError> RaiseTypeError(
      const gd::String &message, size_t beginningPosition) {
    return std::move(gd::make_unique<ExpressionParserError>(
        "type_error", message, beginningPosition, GetCurrentPosition()));
  }

  std::unique_ptr<ExpressionParserError> RaiseEmptyError(
      const gd::String &type, size_t beginningPosition) {
    gd::String message;
    if (type == "number") {
      message = _("You must enter a number or a valid expression call.");
    } else if (type == "string") {
      message = _(
          "You must enter a text (between quotes) or a valid expression call.");
    } else if (gd::ParameterMetadata::IsExpression("variable", type)) {
      message = _("You must enter a variable name.");
    } else if (gd::ParameterMetadata::IsObject(type)) {
      message = _("You must enter a valid object name.");
    } else {
      message = _("You must enter a valid expression.");
    }

    return std::move(RaiseTypeError(message, beginningPosition));
  }
  ///@}

  gd::String expression;
  std::size_t currentPosition;

  const gd::Platform &platform;
  const gd::ObjectsContainer &globalObjectsContainer;
  const gd::ObjectsContainer &objectsContainer;

  static gd::String NAMESPACE_SEPARATOR;
};

}  // namespace gd

#endif  // GDCORE_EXPRESSIONPARSER2_H
