Files
GDevelop/Core/GDCore/Events/Parsers/ExpressionParser2.h
D8H 75bfa5cb45 Unify variable instructions and handle local variables (#6459)
- Global or scene variables can be used with a unique action and condition.
- Object variables can be used with a unique action and condition.
- Variables need to be declared following the same logic as the new expression syntax.
- Local variable can be declared on events
- Extensions have their own variables
- Show a diagnostic report when a preview is launched and there are missing scene variables, object variables or behaviors.
  - This is especially useful if external events are shared between several scenes.
2024-05-20 23:14:07 +02:00

743 lines
28 KiB
C++

/*
* 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/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GrammarTerminals.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
using namespace gd::GrammarTerminals;
/** \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::ExpressionParserError
* \see gd::ExpressionNode
*/
class GD_CORE_API ExpressionParser2 {
public:
ExpressionParser2();
virtual ~ExpressionParser2(){};
/**
* Parse the given expression into a tree of nodes.
*
* \param expression The expression to parse.
*
* \return The node representing the expression as a parsed tree.
*/
std::unique_ptr<ExpressionNode> ParseExpression(
const gd::String &expression_) {
expression = expression_;
currentPosition = 0;
return Start();
}
/**
* 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() {
size_t expressionStartPosition = GetCurrentPosition();
auto expression = Expression();
// Check for extra characters at the end of the expression
if (!IsEndReached()) {
auto op = gd::make_unique<OperatorNode>(' ');
op->leftHandSide = std::move(expression);
op->rightHandSide = ReadUntilEnd();
op->rightHandSide->parent = op.get();
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() {
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> leftHandSide = Term();
SkipAllWhitespaces();
if (IsEndReached()) return leftHandSide;
if (CheckIfChar(IsExpressionEndingChar)) return leftHandSide;
if (CheckIfChar(IsExpressionOperator)) {
auto op = gd::make_unique<OperatorNode>(GetCurrentChar());
op->leftHandSide = std::move(leftHandSide);
op->leftHandSide->parent = op.get();
op->diagnostic = ValidateOperator(GetCurrentChar());
SkipChar();
op->rightHandSide = Expression();
op->rightHandSide->parent = op.get();
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
return std::move(op);
}
leftHandSide->diagnostic = RaiseSyntaxError(
"More than one term was found. Verify that your expression is "
"properly written.");
auto op = gd::make_unique<OperatorNode>(' ');
op->leftHandSide = std::move(leftHandSide);
op->leftHandSide->parent = op.get();
op->rightHandSide = Expression();
op->rightHandSide->parent = op.get();
op->location =
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
return std::move(op);
}
std::unique_ptr<ExpressionNode> Term() {
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> factor = Factor();
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>(GetCurrentChar());
op->leftHandSide = std::move(factor);
op->leftHandSide->parent = op.get();
op->diagnostic = ValidateOperator(GetCurrentChar());
SkipChar();
op->rightHandSide = Factor();
op->rightHandSide->parent = op.get();
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
SkipAllWhitespaces();
factor = std::move(op);
}
return factor;
};
std::unique_ptr<ExpressionNode> Factor() {
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
if (CheckIfChar(IsQuote)) {
std::unique_ptr<ExpressionNode> factor = ReadText();
return factor;
} else if (CheckIfChar(IsUnaryOperator)) {
auto unaryOperatorCharacter = GetCurrentChar();
SkipChar();
auto operatorOperand = Factor();
auto unaryOperator = gd::make_unique<UnaryOperatorNode>(
unaryOperatorCharacter);
unaryOperator->diagnostic = ValidateUnaryOperator(
unaryOperatorCharacter, expressionStartPosition);
unaryOperator->factor = std::move(operatorOperand);
unaryOperator->factor->parent = unaryOperator.get();
unaryOperator->location = ExpressionParserLocation(
expressionStartPosition, GetCurrentPosition());
return std::move(unaryOperator);
} else if (CheckIfChar(IsNumberFirstChar)) {
std::unique_ptr<ExpressionNode> factor = ReadNumber();
return factor;
} else if (CheckIfChar(IsOpeningParenthesis)) {
SkipChar();
std::unique_ptr<ExpressionNode> factor = SubExpression();
if (!CheckIfChar(IsClosingParenthesis)) {
factor->diagnostic =
RaiseSyntaxError(_("Missing a closing parenthesis. Add a closing "
"parenthesis for each opening parenthesis."));
}
SkipIfChar(IsClosingParenthesis);
return factor;
} else if (CheckIfChar(IsAllowedInIdentifier)) {
return Identifier();
}
std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace();
return factor;
}
std::unique_ptr<SubExpressionNode> SubExpression() {
size_t expressionStartPosition = GetCurrentPosition();
auto expression = Expression();
auto subExpression =
gd::make_unique<SubExpressionNode>(std::move(expression));
subExpression->location =
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
return std::move(subExpression);
};
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
Identifier() {
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(name, nameLocation, openingParenthesisLocation);
} else if (CheckIfChar(IsDot)) {
ExpressionParserLocation dotLocation = SkipChar();
SkipAllWhitespaces();
return ObjectFunctionOrBehaviorFunctionOrVariable(
name, nameLocation, dotLocation);
} else if (CheckIfChar(IsOpeningSquareBracket)) {
return Variable(name, nameLocation);
} else {
auto identifier = gd::make_unique<IdentifierNode>(name);
identifier->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
identifier->identifierNameLocation = identifier->location;
return std::move(identifier);
}
}
std::unique_ptr<VariableNode> Variable(const gd::String &name, gd::ExpressionParserLocation nameLocation) {
auto variable = gd::make_unique<VariableNode>(name);
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
variable->child = VariableAccessorOrVariableBracketAccessor();
variable->child->parent = variable.get();
}
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());
child->expression->parent = child.get();
if (!CheckIfChar(IsClosingSquareBracket)) {
child->diagnostic =
RaiseSyntaxError(_("Missing a closing bracket. Add a closing "
"bracket for each opening bracket."));
}
SkipIfChar(IsClosingSquareBracket);
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
}
child->location =
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
return std::move(child);
} else if (CheckIfChar(IsDot)) {
auto dotLocation = SkipChar();
SkipAllWhitespaces();
auto identifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
auto child =
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
if (identifierAndLocation.name.empty()) {
child->diagnostic = RaiseSyntaxError(_("A name should be entered after the dot."));
}
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
}
child->nameLocation = identifierAndLocation.location;
child->dotLocation = dotLocation;
child->location =
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
return std::move(child);
}
// Should never happen, unless a node called this function without checking if the current character
// was a dot or an opening bracket - this means there is an error in the grammar.
auto unrecognisedNode = gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>();
unrecognisedNode->diagnostic = RaiseSyntaxError(_("A dot or bracket was expected here."));
return std::move(unrecognisedNode);
}
std::unique_ptr<FunctionCallNode> FreeFunction(
const gd::String &functionFullName,
const ExpressionParserLocation &identifierLocation,
const ExpressionParserLocation &openingParenthesisLocation) {
// TODO: error if trying to use function for type != "number" && != "string"
// + Test for it
auto function =
gd::make_unique<FunctionCallNode>(functionFullName);
auto parametersNode = Parameters(function.get());
function->parameters = std::move(parametersNode.parameters);
function->diagnostic = std::move(parametersNode.diagnostic);
function->location = ExpressionParserLocation(
identifierLocation.GetStartPosition(), GetCurrentPosition());
function->functionNameLocation = identifierLocation;
function->openingParenthesisLocation = openingParenthesisLocation;
function->closingParenthesisLocation =
parametersNode.closingParenthesisLocation;
return std::move(function);
}
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
ObjectFunctionOrBehaviorFunctionOrVariable(
const gd::String &parentIdentifier,
const ExpressionParserLocation &parentIdentifierLocation,
const ExpressionParserLocation &parentIdentifierDotLocation) {
auto childIdentifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
const gd::String &childIdentifierName = childIdentifierAndLocation.name;
const auto &childIdentifierNameLocation =
childIdentifierAndLocation.location;
std::unique_ptr<gd::ExpressionParserError> emptyNameError = childIdentifierName.empty() ?
RaiseSyntaxError(_("A name should be entered after the dot.")) : nullptr;
SkipAllWhitespaces();
if (IsNamespaceSeparator()) {
ExpressionParserLocation namespaceSeparatorLocation =
SkipNamespaceSeparator();
SkipAllWhitespaces();
auto behaviorFunction = BehaviorFunction(parentIdentifier,
childIdentifierName,
parentIdentifierLocation,
parentIdentifierDotLocation,
childIdentifierNameLocation,
namespaceSeparatorLocation);
if (emptyNameError) behaviorFunction->diagnostic = std::move(emptyNameError);
return std::move(behaviorFunction);
} else if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
auto function = gd::make_unique<FunctionCallNode>(
parentIdentifier,
childIdentifierName);
auto parametersNode = Parameters(function.get(), parentIdentifier);
function->parameters = std::move(parametersNode.parameters),
function->diagnostic = emptyNameError ? std::move(emptyNameError) : std::move(parametersNode.diagnostic);
function->location = ExpressionParserLocation(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
function->objectNameLocation = parentIdentifierLocation;
function->objectNameDotLocation = parentIdentifierDotLocation;
function->functionNameLocation = childIdentifierNameLocation;
function->openingParenthesisLocation = openingParenthesisLocation;
function->closingParenthesisLocation =
parametersNode.closingParenthesisLocation;
return std::move(function);
} else if (CheckIfChar(IsDot) || CheckIfChar(IsOpeningSquareBracket)) {
auto variable = gd::make_unique<VariableNode>(parentIdentifier);
variable->diagnostic = std::move(emptyNameError);
auto child =
gd::make_unique<VariableAccessorNode>(childIdentifierName);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
child->nameLocation = childIdentifierNameLocation;
child->dotLocation = parentIdentifierDotLocation;
child->location = ExpressionParserLocation(
parentIdentifierDotLocation.GetStartPosition(), GetCurrentPosition());
variable->child = std::move(child);
variable->child->parent = variable.get();
variable->location = ExpressionParserLocation(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
variable->nameLocation = parentIdentifierLocation;
return std::move(variable);
}
auto node = gd::make_unique<IdentifierNode>(
parentIdentifier, childIdentifierName);
node->location = ExpressionParserLocation(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
node->identifierNameLocation = parentIdentifierLocation;
node->identifierNameDotLocation = parentIdentifierDotLocation;
node->childIdentifierNameLocation = childIdentifierNameLocation;
node->diagnostic = std::move(emptyNameError);
return std::move(node);
}
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode> BehaviorFunction(
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();
auto function = gd::make_unique<FunctionCallNode>(
objectName,
behaviorName,
functionName);
auto parametersNode =
Parameters(function.get(), objectName, behaviorName);
function->parameters = std::move(parametersNode.parameters);
function->diagnostic = std::move(parametersNode.diagnostic);
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>(
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(
FunctionCallNode *functionCallNode,
const gd::String &objectName = "",
const gd::String &behaviorName = "") {
std::vector<std::unique_ptr<ExpressionNode>> parameters;
gd::String lastObjectName = "";
bool previousCharacterIsParameterSeparator = false;
while (!IsEndReached()) {
SkipAllWhitespaces();
if (CheckIfChar(IsClosingParenthesis) && !previousCharacterIsParameterSeparator) {
auto closingParenthesisLocation = SkipChar();
return ParametersNode{
std::move(parameters), nullptr, closingParenthesisLocation};
}
bool isEmptyParameter = CheckIfChar(IsParameterSeparator)
|| (CheckIfChar(IsClosingParenthesis) && previousCharacterIsParameterSeparator);
auto parameter = isEmptyParameter ? gd::make_unique<EmptyNode>() : Expression();
parameter->parent = functionCallNode;
parameters.push_back(std::move(parameter));
SkipAllWhitespaces();
previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
SkipIfChar(IsParameterSeparator);
}
ExpressionParserLocation invalidClosingParenthesisLocation;
return ParametersNode{
std::move(parameters),
RaiseSyntaxError(_("The list of parameters is not terminated. Add a "
"closing parenthesis to end the parameters.")),
invalidClosingParenthesisLocation};
}
///@}
std::unique_ptr<ExpressionParserError> ValidateOperator(
gd::String::value_type operatorChar) {
if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
operatorChar == '*') {
return std::unique_ptr<ExpressionParserError>(nullptr);
}
return gd::make_unique<ExpressionParserError>(
gd::ExpressionParserError::ErrorType::InvalidOperator,
_("You've used an operator that is not supported. Operator should be "
"either +, -, / or *."),
GetCurrentPosition());
}
std::unique_ptr<ExpressionParserError> ValidateUnaryOperator(
gd::String::value_type operatorChar,
size_t position) {
if (operatorChar == '+' || operatorChar == '-') {
return std::unique_ptr<ExpressionParserError>(nullptr);
}
return gd::make_unique<ExpressionParserError>(
gd::ExpressionParserError::ErrorType::InvalidOperator,
_("You've used an \"unary\" operator that is not supported. Operator "
"should be "
"either + or -."),
position);
}
///@}
/** \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 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(bool allowDeprecatedSpacesInName = true) {
gd::String name;
size_t startPosition = currentPosition;
while (currentPosition < expression.size() &&
(CheckIfChar(IsAllowedInIdentifier)
// Allow whitespace in identifier name for compatibility
|| (allowDeprecatedSpacesInName && 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() {
size_t startPosition = GetCurrentPosition();
gd::String text;
while (currentPosition < expression.size() &&
!IsWhitespace(expression[currentPosition])) {
text += expression[currentPosition];
currentPosition++;
}
auto node = gd::make_unique<EmptyNode>(text);
node->location =
ExpressionParserLocation(startPosition, GetCurrentPosition());
return node;
}
std::unique_ptr<EmptyNode> ReadUntilEnd() {
size_t startPosition = GetCurrentPosition();
gd::String text;
while (currentPosition < expression.size()) {
text += expression[currentPosition];
currentPosition++;
}
auto node = gd::make_unique<EmptyNode>(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>(
gd::ExpressionParserError::ErrorType::SyntaxError, message,
GetCurrentPosition()));
}
std::unique_ptr<ExpressionParserError> RaiseTypeError(
const gd::String &message, size_t beginningPosition) {
return std::move(gd::make_unique<ExpressionParserError>(
gd::ExpressionParserError::ErrorType::MismatchedType, message,
beginningPosition, GetCurrentPosition()));
}
///@}
gd::String expression;
std::size_t currentPosition;
static gd::String NAMESPACE_SEPARATOR;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONPARSER2_H