10 #include "GDCore/Events/Parsers/ExpressionParser2Node.h"
11 #include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
12 #include "GDCore/Tools/MakeUnique.h"
14 #include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
15 #include "GDCore/Project/ProjectScopedContainers.h"
16 #include "GDCore/Project/VariablesContainersList.h"
17 #include "GDCore/Project/VariablesContainer.h"
21 class ObjectsContainer;
22 class VariablesContainer;
24 class ParameterMetadata;
25 class ExpressionMetadata;
26 class VariablesContainersList;
27 class ProjectScopedContainers;
44 : platform(platform_),
45 projectScopedContainers(projectScopedContainers_),
47 childType(Type::Unknown),
48 forbidsUsageOfBracketsBecauseParentIsObject(
false),
49 currentParameterExtraInfo(&extraInfo_),
51 variableObjectNameLocation() {};
63 node.Visit(validator);
88 const std::vector<ExpressionParserError*> &
90 return deprecationWarnings;
96 node.expression->Visit(*
this);
98 void OnVisitOperatorNode(OperatorNode& node)
override {
103 node.leftHandSide->Visit(*
this);
104 const Type leftType = childType;
106 if (leftType == Type::Number) {
107 if (node.op ==
' ') {
108 RaiseError(gd::ExpressionParserError::ErrorType::SyntaxError,
109 "No operator found. Did you forget to enter an operator (like +, -, "
110 "* or /) between numbers or expressions?", node.rightHandSide->location);
113 else if (leftType == Type::String) {
114 if (node.op ==
' ') {
115 RaiseError(gd::ExpressionParserError::ErrorType::SyntaxError,
116 "You must add the operator + between texts or expressions. For "
117 "example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
119 else if (node.op !=
'+') {
121 _(
"You've used an operator that is not supported. Only + can be used "
122 "to concatenate texts."),
123 ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
125 }
else if (leftType == Type::Object) {
127 _(
"Operators (+, -, /, *) can't be used with an object name. Remove "
129 node.rightHandSide->location);
130 }
else if (leftType == Type::Variable || leftType == Type::LegacyVariable) {
132 _(
"Operators (+, -, /, *) can't be used in variable names. Remove "
133 "the operator from the variable name."),
134 node.rightHandSide->location);
140 parentType = ShouldTypeBeRefined(parentType) ? leftType : parentType;
141 node.rightHandSide->Visit(*
this);
142 const Type rightType = childType;
149 childType = ShouldTypeBeRefined(parentType) ? (ShouldTypeBeRefined(leftType) ? leftType : rightType) : parentType;
151 void OnVisitUnaryOperatorNode(UnaryOperatorNode& node)
override {
152 ReportAnyError(node);
153 node.factor->Visit(*
this);
154 const Type rightType = childType;
156 if (rightType == Type::Number) {
157 if (node.op !=
'+' && node.op !=
'-') {
161 _(
"You've used an \"unary\" operator that is not supported. Operator "
166 }
else if (rightType == Type::String) {
168 _(
"You've used an operator that is not supported. Only + can be used "
169 "to concatenate texts, and must be placed between two texts (or "
172 }
else if (rightType == Type::Object) {
174 _(
"Operators (+, -) can't be used with an object name. Remove the "
177 }
else if (rightType == Type::Variable || rightType == Type::LegacyVariable) {
179 _(
"Operators (+, -) can't be used in variable names. Remove "
180 "the operator from the variable name."),
184 void OnVisitNumberNode(NumberNode& node)
override {
185 ReportAnyError(node);
186 childType = Type::Number;
187 if (parentType == Type::String) {
189 _(
"You entered a number, but a text was expected (in quotes)."),
191 }
else if (parentType != Type::Number &&
192 parentType != Type::NumberOrString) {
193 RaiseTypeError(_(
"You entered a number, but this type was expected:") +
194 " " + TypeToString(parentType),
198 void OnVisitTextNode(TextNode& node)
override {
199 ReportAnyError(node);
200 childType = Type::String;
201 if (parentType == Type::Number) {
202 RaiseTypeError(_(
"You entered a text, but a number was expected."),
204 }
else if (parentType != Type::String &&
205 parentType != Type::NumberOrString) {
206 RaiseTypeError(_(
"You entered a text, but this type was expected:") +
207 " " + TypeToString(parentType),
211 void OnVisitVariableNode(VariableNode& node)
override {
212 ReportAnyError(node);
214 if (parentType == Type::Variable ||
215 parentType == Type::VariableOrProperty ||
216 parentType == Type::VariableOrPropertyOrParameter) {
217 childType = parentType;
219 CheckVariableExistence(node.location, node.name, node.child !=
nullptr);
221 node.child->Visit(*
this);
223 }
else if (parentType == Type::LegacyVariable) {
224 childType = parentType;
227 node.child->Visit(*
this);
229 }
else if (parentType == Type::String || parentType == Type::Number ||
230 parentType == Type::NumberOrString) {
232 childType = parentType;
234 const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
235 const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
236 const auto& propertiesContainerList = projectScopedContainers.GetPropertiesContainersList();
238 forbidsUsageOfBracketsBecauseParentIsObject =
false;
239 projectScopedContainers.MatchIdentifierWithName<
void>(node.name,
242 variableObjectName = node.name;
243 variableObjectNameLocation = node.nameLocation;
246 forbidsUsageOfBracketsBecauseParentIsObject =
true;
252 RaiseTypeError(_(
"Accessing a child variable of a property is not possible - just write the property name."),
257 RaiseTypeError(_(
"Accessing a child variable of a parameter is not possible - just write the parameter name."),
261 RaiseTypeError(_(
"No object, variable or property with this name found."),
266 node.child->Visit(*
this);
269 forbidsUsageOfBracketsBecauseParentIsObject =
false;
271 RaiseTypeError(_(
"You entered a variable, but this type was expected:") +
272 " " + TypeToString(parentType),
276 node.child->Visit(*
this);
280 void OnVisitVariableAccessorNode(VariableAccessorNode& node)
override {
281 ReportAnyError(node);
283 if (!variableObjectName.empty()) {
284 ValidateObjectVariableOrVariableOrProperty(variableObjectName,
285 variableObjectNameLocation,
286 node.name, node.nameLocation);
287 variableObjectName =
"";
291 forbidsUsageOfBracketsBecauseParentIsObject =
false;
294 node.child->Visit(*
this);
297 void OnVisitVariableBracketAccessorNode(
298 VariableBracketAccessorNode& node)
override {
299 ReportAnyError(node);
301 variableObjectName =
"";
302 if (forbidsUsageOfBracketsBecauseParentIsObject) {
303 RaiseError(gd::ExpressionParserError::ErrorType::BracketsNotAllowedForObjects,
304 _(
"You can't use the brackets to access an object variable. "
305 "Use a dot followed by the variable name, like this: "
306 "`MyObject.MyVariable`."),
309 forbidsUsageOfBracketsBecauseParentIsObject =
false;
311 Type currentParentType = parentType;
312 Type currentChildType = childType;
313 parentType = Type::NumberOrString;
314 auto parentParameterExtraInfo = currentParameterExtraInfo;
315 currentParameterExtraInfo =
nullptr;
316 node.expression->Visit(*
this);
317 currentParameterExtraInfo = parentParameterExtraInfo;
318 parentType = currentParentType;
319 childType = currentChildType;
322 node.child->Visit(*
this);
325 void OnVisitIdentifierNode(IdentifierNode& node)
override {
326 ReportAnyError(node);
327 if (parentType == Type::String) {
328 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
331 RaiseUnknownIdentifierError(_(
"You must wrap your text inside double quotes "
332 "(example: \"Hello world\")."),
336 else if (parentType == Type::Number) {
337 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
339 RaiseUnknownIdentifierError(_(
"You must enter a number."), node.location);
342 else if (parentType == Type::NumberOrString) {
343 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
346 RaiseUnknownIdentifierError(
347 _(
"You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
350 }
else if (parentType == Type::Variable ||
351 parentType == Type::VariableOrProperty ||
352 parentType == Type::VariableOrPropertyOrParameter) {
353 CheckVariableExistence(node.location, node.identifierName, !node.childIdentifierName.empty());
354 }
else if (parentType != Type::Object &&
355 parentType != Type::LegacyVariable) {
358 _(
"You've entered a name, but this type was expected:") +
" " + TypeToString(parentType),
360 childType = parentType;
362 childType = parentType;
365 void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node)
override {
366 ReportAnyError(node);
368 void OnVisitFunctionCallNode(FunctionCallNode& node)
override {
369 childType = ValidateFunction(node);
371 void OnVisitEmptyNode(EmptyNode& node)
override {
372 ReportAnyError(node);
374 if (parentType == Type::Number) {
375 message = _(
"You must enter a number or a valid expression call.");
376 }
else if (parentType == Type::String) {
378 "You must enter a text (between quotes) or a valid expression call.");
379 }
else if (parentType == Type::Variable || parentType == Type::LegacyVariable) {
380 message = _(
"You must enter a variable name.");
381 }
else if (parentType == Type::Object) {
382 message = _(
"You must enter a valid object name.");
385 message = _(
"You must enter a valid expression.");
387 RaiseTypeError(message, node.location);
388 childType = Type::Empty;
402 VariableOrPropertyOrParameter
405 bool ValidateObjectVariableOrVariableOrProperty(
const gd::IdentifierNode& identifier);
406 bool ValidateObjectVariableOrVariableOrProperty(
412 void CheckVariableExistence(
const ExpressionParserLocation &location,
414 if (!currentParameterExtraInfo ||
415 *currentParameterExtraInfo !=
"AllowUndeclaredVariable") {
416 projectScopedContainers.MatchIdentifierWithName<
void>(
420 RaiseVariableNameCollisionError(
421 _(
"This variable has the same name as an object. Consider "
422 "renaming one or the other."),
430 if (parentType != Type::VariableOrProperty &&
431 parentType != Type::VariableOrPropertyOrParameter) {
432 RaiseVariableNameCollisionError(
433 _(
"This variable has the same name as a property. Consider "
434 "renaming one or the other."),
436 }
else if (hasChild) {
437 RaiseMalformedVariableParameter(
438 _(
"Properties can't have children."), location, name);
443 if (parentType != Type::VariableOrPropertyOrParameter) {
444 RaiseVariableNameCollisionError(
445 _(
"This variable has the same name as a parameter. Consider "
446 "renaming one or the other."),
448 }
else if (hasChild) {
449 RaiseMalformedVariableParameter(
450 _(
"Properties can't have children."), location, name);
455 RaiseUndeclaredVariableError(
456 _(
"No variable with this name found."), location,
462 void ReportAnyError(
const ExpressionNode& node,
bool isFatal =
true) {
463 if (node.diagnostic) {
467 allErrors.push_back(node.diagnostic.get());
469 fatalErrors.push_back(node.diagnostic.get());
474 void RaiseError(gd::ExpressionParserError::ErrorType type,
476 const ExpressionParserLocation &location,
bool isFatal =
true,
479 auto diagnostic = gd::make_unique<ExpressionParserError>(
480 type, message, location, actualValue, objectName);
481 allErrors.push_back(diagnostic.get());
483 fatalErrors.push_back(diagnostic.get());
488 supplementalErrors.push_back(std::move(diagnostic));
491 void RaiseUnknownIdentifierError(
const gd::String &message,
492 const ExpressionParserLocation &location) {
493 RaiseError(gd::ExpressionParserError::ErrorType::UnknownIdentifier, message,
497 void RaiseUndeclaredVariableError(
const gd::String &message,
498 const ExpressionParserLocation &location,
501 RaiseError(gd::ExpressionParserError::ErrorType::UndeclaredVariable,
502 message, location,
true, variableName, objectName);
505 void RaiseVariableNameCollisionError(
const gd::String &message,
506 const ExpressionParserLocation &location,
509 RaiseError(gd::ExpressionParserError::ErrorType::VariableNameCollision,
510 message, location,
false, variableName, objectName);
513 void RaiseMalformedVariableParameter(
const gd::String &message,
514 const ExpressionParserLocation &location,
516 RaiseError(gd::ExpressionParserError::ErrorType::MalformedVariableParameter,
517 message, location,
true, variableName,
"");
520 void RaiseTypeError(
const gd::String &message,
521 const ExpressionParserLocation &location,
522 bool isFatal =
true) {
523 RaiseError(gd::ExpressionParserError::ErrorType::MismatchedType, message,
527 void RaiseOperatorError(
const gd::String &message,
528 const ExpressionParserLocation &location) {
529 RaiseError(gd::ExpressionParserError::ErrorType::InvalidOperator, message,
534 if (variableType == gd::Variable::Number) {
535 childType = Type::Number;
536 }
else if (variableType == gd::Variable::String) {
537 childType = Type::String;
543 static bool ShouldTypeBeRefined(
Type type) {
544 return (type == Type::Unknown || type == Type::NumberOrString);
552 static const gd::String numberOrStringTypeString;
554 static const gd::String legacyVariableTypeString;
559 std::vector<ExpressionParserError*> fatalErrors;
560 std::vector<ExpressionParserError*> allErrors;
561 std::vector<ExpressionParserError*> deprecationWarnings;
562 std::vector<std::unique_ptr<ExpressionParserError>> supplementalErrors;
565 bool forbidsUsageOfBracketsBecauseParentIsObject;
The interface for any worker class ("visitor" pattern) that want to interact with the nodes of a pars...
Definition: ExpressionParser2NodeWorker.h:36
Validate that an expression is properly written by returning any error attached to the nodes during p...
Definition: ExpressionValidator.h:38
const std::vector< ExpressionParserError * > & GetFatalErrors()
Get only the fatal errors.
Definition: ExpressionValidator.h:72
const std::vector< ExpressionParserError * > & GetDeprecationWarnings()
Get all deprecation warnings.
Definition: ExpressionValidator.h:89
const std::vector< ExpressionParserError * > & GetAllErrors()
Get all the errors.
Definition: ExpressionValidator.h:81
static bool HasNoErrors(const gd::Platform &platform, const gd::ProjectScopedContainers &projectScopedContainers, const gd::String &rootType, gd::ExpressionNode &node)
Helper function to check if a given node does not contain any error including non-fatal ones.
Definition: ExpressionValidator.h:58
Holds references to variables, objects, properties and other containers.
Definition: ProjectScopedContainers.h:36
String represents an UTF8 encoded string.
Definition: String.h:33
Type
Definition: Variable.h:32
Definition: CommonTools.h:24
Type
Type of JSON value.
Definition: rapidjson.h:603
The base node, from which all nodes in the tree of an expression inherits from.
Definition: ExpressionParser2Node.h:101
Definition: ExpressionParser2Node.h:25
A function call node (either free function, object function or object behavior function)....
Definition: ExpressionParser2Node.h:372
An identifier node, usually representing an object or a variable with an optional function name or ch...
Definition: ExpressionParser2Node.h:205
Definition: ExpressionParser2Node.h:116