10 #include "GDCore/Events/Parsers/ExpressionParser2Node.h"
11 #include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
12 #include "GDCore/Tools/MakeUnique.h"
14 #include "GDCore/Project/ProjectScopedContainers.h"
15 #include "GDCore/Project/VariablesContainersList.h"
16 #include "GDCore/Project/VariablesContainer.h"
20 class ObjectsContainer;
21 class VariablesContainer;
23 class VariablesContainersList;
24 class ProjectScopedContainers;
40 const gd::String &rootObjectName_ = emptyParameterExtraInfo,
41 const gd::String &extraInfo_ = emptyParameterExtraInfo)
42 : platform(platform_),
43 projectScopedContainers(projectScopedContainers_),
45 rootObjectName(rootObjectName_),
46 childType(Type::Unknown),
47 forbidsUsageOfBracketsBecauseParentIsObject(
false),
48 currentParameterExtraInfo(extraInfo_),
50 variableObjectNameLocation() {};
62 node.Visit(validator);
87 const std::vector<ExpressionParserError*> &
89 return deprecationWarnings;
94 isDirectChildOfVariableBracketAccessorNode =
false;
96 node.expression->Visit(*
this);
98 void OnVisitOperatorNode(OperatorNode& node)
override {
99 isDirectChildOfVariableBracketAccessorNode =
false;
100 ReportAnyError(node);
104 node.leftHandSide->Visit(*
this);
105 const Type leftType = childType;
107 if (parentType == Type::Variable ||
108 parentType == Type::VariableOrProperty ||
109 parentType == Type::VariableOrPropertyOrParameter ||
110 parentType == Type::ObjectVariable ||
111 parentType == Type::LegacyVariable) {
113 _(
"Operators (+, -, /, *) can't be used in variable names. Remove "
114 "the operator from the variable name."),
115 node.rightHandSide->location);
116 }
else if (leftType == Type::Number) {
117 if (node.op ==
' ') {
118 RaiseError(gd::ExpressionParserError::ErrorType::SyntaxError,
119 "No operator found. Did you forget to enter an operator (like +, -, "
120 "* or /) between numbers or expressions?", node.rightHandSide->location);
122 }
else if (leftType == Type::String) {
123 if (node.op ==
' ') {
124 RaiseError(gd::ExpressionParserError::ErrorType::SyntaxError,
125 "You must add the operator + between texts or expressions. For "
126 "example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
128 else if (node.op !=
'+') {
130 _(
"You've used an operator that is not supported. Only + can be used "
131 "to concatenate texts."),
132 ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
134 }
else if (leftType == Type::Object) {
136 _(
"Operators (+, -, /, *) can't be used with an object name. Remove "
138 node.rightHandSide->location);
144 parentType = ShouldTypeBeRefined(parentType) ? leftType : parentType;
145 node.rightHandSide->Visit(*
this);
146 const Type rightType = childType;
153 childType = ShouldTypeBeRefined(parentType) ? (ShouldTypeBeRefined(leftType) ? leftType : rightType) : parentType;
155 void OnVisitUnaryOperatorNode(UnaryOperatorNode& node)
override {
156 isDirectChildOfVariableBracketAccessorNode =
false;
157 ReportAnyError(node);
158 node.factor->Visit(*
this);
159 const Type rightType = childType;
161 if (parentType == Type::Variable ||
162 parentType == Type::VariableOrProperty ||
163 parentType == Type::VariableOrPropertyOrParameter ||
164 parentType == Type::ObjectVariable ||
165 parentType == Type::LegacyVariable) {
167 _(
"Operators (+, -) can't be used in variable names. Remove "
168 "the operator from the variable name."),
170 }
else if (rightType == Type::Number) {
171 if (node.op !=
'+' && node.op !=
'-') {
175 _(
"You've used an \"unary\" operator that is not supported. Operator "
180 }
else if (rightType == Type::String) {
182 _(
"You've used an operator that is not supported. Only + can be used "
183 "to concatenate texts, and must be placed between two texts (or "
186 }
else if (rightType == Type::Object) {
188 _(
"Operators (+, -) can't be used with an object name. Remove the "
193 void OnVisitNumberNode(NumberNode& node)
override {
194 isDirectChildOfVariableBracketAccessorNode =
false;
195 ReportAnyError(node);
196 childType = Type::Number;
197 if (parentType == Type::String) {
199 _(
"You entered a number, but a text was expected (in quotes)."),
201 }
else if (parentType == Type::Variable ||
202 parentType == Type::VariableOrProperty ||
203 parentType == Type::VariableOrPropertyOrParameter ||
204 parentType == Type::ObjectVariable ||
205 parentType == Type::LegacyVariable) {
207 _(
"The variable name looks like you're building an expression or a "
208 "formula. You can only use this for structure or arrays, for "
209 "example: Score[3]."),
211 }
else if (parentType != Type::Number &&
212 parentType != Type::NumberOrString) {
213 RaiseTypeError(_(
"You entered a number, but this type was expected:") +
214 " " + TypeToString(parentType),
218 void OnVisitTextNode(TextNode& node)
override {
219 isDirectChildOfVariableBracketAccessorNode =
false;
220 ReportAnyError(node);
221 childType = Type::String;
222 if (parentType == Type::Number) {
223 RaiseTypeError(_(
"You entered a text, but a number was expected."),
225 }
else if (parentType == Type::Variable ||
226 parentType == Type::VariableOrProperty ||
227 parentType == Type::VariableOrPropertyOrParameter ||
228 parentType == Type::ObjectVariable ||
229 parentType == Type::LegacyVariable) {
231 _(
"The variable name looks like you're building an expression or a "
232 "formula. You can only use this for structure or arrays, for "
233 "example: Score[\"Player1\"]."),
235 }
else if (parentType != Type::String &&
236 parentType != Type::NumberOrString) {
237 RaiseTypeError(_(
"You entered a text, but this type was expected:") +
238 " " + TypeToString(parentType),
242 void OnVisitVariableNode(VariableNode& node)
override {
243 isDirectChildOfVariableBracketAccessorNode =
false;
244 ReportAnyError(node);
245 parentVariable =
nullptr;
246 variableChildDepth = 0;
248 if (parentType == Type::Variable ||
249 parentType == Type::VariableOrProperty ||
250 parentType == Type::VariableOrPropertyOrParameter) {
251 childType = parentType;
253 bool isRootVariableDeclared = CheckVariableExistence(
254 node.location, node.name, node.child !=
nullptr);
256 if (isRootVariableDeclared) {
257 const auto &variable =
258 projectScopedContainers.GetVariablesContainersList().Get(
260 parentVariable = &variable;
262 node.child->Visit(*
this);
264 }
else if (parentType == Type::ObjectVariable) {
265 childType = parentType;
267 if (!rootObjectName.empty()) {
268 ValidateObjectVariableOrVariableOrProperty(
269 rootObjectName, node.nameLocation, node.name, node.nameLocation,
270 false, !!node.child);
272 const auto &objectsContainersList =
273 projectScopedContainers.GetObjectsContainersList();
274 auto variableExistence =
275 objectsContainersList.HasObjectOrGroupWithVariableNamed(
276 rootObjectName, node.name);
277 if (variableExistence == gd::ObjectsContainersList::Exists) {
278 const auto &objectVariable =
279 objectsContainersList
280 .GetObjectOrGroupVariablesContainer(rootObjectName)
283 parentVariable = &objectVariable;
285 ValidateLastChildVariable(objectVariable, node.nameLocation);
291 node.child->Visit(*
this);
293 }
else if (parentType == Type::LegacyVariable) {
294 childType = parentType;
297 node.child->Visit(*
this);
299 }
else if (parentType == Type::String || parentType == Type::Number ||
300 parentType == Type::NumberOrString) {
302 childType = parentType;
304 const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
305 const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
306 const auto& propertiesContainerList = projectScopedContainers.GetPropertiesContainersList();
308 forbidsUsageOfBracketsBecauseParentIsObject =
false;
309 projectScopedContainers.MatchIdentifierWithName<
void>(node.name,
312 variableObjectName = node.name;
313 variableObjectNameLocation = node.nameLocation;
316 forbidsUsageOfBracketsBecauseParentIsObject =
true;
319 const auto &variable =
320 projectScopedContainers.GetVariablesContainersList().Get(
323 parentVariable = &variable;
325 ValidateLastChildVariable(variable, node.location);
330 RaiseTypeError(_(
"Accessing a child variable of a property is not possible - just write the property name."),
335 RaiseTypeError(_(
"Accessing a child variable of a parameter is not possible - just write the parameter name."),
339 RaiseTypeError(_(
"No object, variable or property with this name found."),
344 node.child->Visit(*
this);
347 forbidsUsageOfBracketsBecauseParentIsObject =
false;
349 RaiseTypeError(_(
"You entered a variable, but this type was expected:") +
350 " " + TypeToString(parentType),
354 node.child->Visit(*
this);
358 void OnVisitVariableAccessorNode(VariableAccessorNode& node)
override {
359 isDirectChildOfVariableBracketAccessorNode =
false;
360 ReportAnyError(node);
362 if (!variableObjectName.empty()) {
363 ValidateObjectVariableOrVariableOrProperty(
364 variableObjectName, variableObjectNameLocation, node.name,
365 node.nameLocation,
true, !!node.child);
367 const auto &objectsContainersList =
368 projectScopedContainers.GetObjectsContainersList();
369 auto variableExistence =
370 objectsContainersList.HasObjectOrGroupWithVariableNamed(
371 variableObjectName, node.name);
372 if (variableExistence == gd::ObjectsContainersList::Exists) {
373 const auto &objectVariable =
374 objectsContainersList
375 .GetObjectOrGroupVariablesContainer(variableObjectName)
378 parentVariable = &objectVariable;
380 parentVariable =
nullptr;
383 parentVariable =
nullptr;
385 variableChildDepth = 0;
386 variableObjectName =
"";
387 }
else if (parentVariable) {
388 const bool isChildVariableDeclared = ValidateChildVariable(
389 *parentVariable, node.name, node.nameLocation,
false);
390 if (isChildVariableDeclared) {
391 const auto &childVariable = parentVariable->GetChild(node.name);
393 parentVariable = &childVariable;
394 variableChildDepth++;
396 ValidateLastChildVariable(childVariable, node.nameLocation);
397 parentVariable =
nullptr;
398 variableChildDepth = 0;
402 parentVariable =
nullptr;
403 variableChildDepth = 0;
408 forbidsUsageOfBracketsBecauseParentIsObject =
false;
411 node.child->Visit(*
this);
414 void OnVisitVariableBracketAccessorNode(
415 VariableBracketAccessorNode& node)
override {
416 ReportAnyError(node);
418 variableObjectName =
"";
419 parentVariable =
nullptr;
420 variableChildDepth = 0;
421 if (forbidsUsageOfBracketsBecauseParentIsObject) {
422 RaiseError(gd::ExpressionParserError::ErrorType::BracketsNotAllowedForObjects,
423 _(
"You can't use the brackets to access an object variable. "
424 "Use a dot followed by the variable name, like this: "
425 "`MyObject.MyVariable`."),
428 forbidsUsageOfBracketsBecauseParentIsObject =
false;
430 Type currentParentType = parentType;
431 Type currentChildType = childType;
432 parentType = Type::NumberOrString;
433 auto parentParameterExtraInfo = currentParameterExtraInfo;
434 currentParameterExtraInfo =
"";
435 isDirectChildOfVariableBracketAccessorNode =
true;
436 node.expression->Visit(*
this);
437 isDirectChildOfVariableBracketAccessorNode =
false;
438 currentParameterExtraInfo = parentParameterExtraInfo;
439 parentType = currentParentType;
440 childType = currentChildType;
443 node.child->Visit(*
this);
446 void OnVisitIdentifierNode(IdentifierNode& node)
override {
447 isDirectChildOfVariableBracketAccessorNode =
false;
448 ReportAnyError(node);
449 if (parentType == Type::String) {
450 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
453 RaiseUnknownIdentifierError(_(
"You must wrap your text inside double quotes "
454 "(example: \"Hello world\")."),
458 else if (parentType == Type::Number) {
459 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
461 RaiseUnknownIdentifierError(_(
"You must enter a number."), node.location);
464 else if (parentType == Type::NumberOrString) {
465 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
468 RaiseUnknownIdentifierError(
469 _(
"You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
472 }
else if (parentType == Type::Variable ||
473 parentType == Type::VariableOrProperty ||
474 parentType == Type::VariableOrPropertyOrParameter) {
475 bool isRootVariableDeclared =
476 CheckVariableExistence(node.location, node.identifierName,
477 !node.childIdentifierName.empty());
478 if (isRootVariableDeclared) {
479 ValidateObjectVariableOrVariableOrProperty(
480 node.identifierName, node.identifierNameLocation,
481 node.childIdentifierName, node.childIdentifierNameLocation,
false);
483 }
else if (parentType == Type::ObjectVariable) {
484 childType = parentType;
485 if (!rootObjectName.empty()) {
486 ValidateObjectVariableOrVariableOrProperty(
487 rootObjectName, node.identifierNameLocation, node.identifierName,
488 node.identifierNameLocation,
false,
489 !node.childIdentifierName.empty());
491 const auto &objectsContainersList =
492 projectScopedContainers.GetObjectsContainersList();
493 auto variableExistence =
494 objectsContainersList.HasObjectOrGroupWithVariableNamed(
495 rootObjectName, node.identifierName);
496 if (variableExistence == gd::ObjectsContainersList::Exists) {
497 const auto &objectVariable =
498 objectsContainersList
499 .GetObjectOrGroupVariablesContainer(rootObjectName)
500 ->Get(node.identifierName);
501 if (!node.childIdentifierName.empty()) {
502 const bool isChildVariableDeclared =
503 ValidateChildVariable(objectVariable, node.childIdentifierName,
504 node.childIdentifierNameLocation,
false);
505 if (isChildVariableDeclared) {
506 const auto &childVariable =
507 objectVariable.GetChild(node.childIdentifierName);
508 ValidateLastChildVariable(childVariable,
509 node.childIdentifierNameLocation);
515 }
else if (parentType != Type::Object &&
516 parentType != Type::LegacyVariable) {
519 _(
"You've entered a name, but this type was expected:") +
" " + TypeToString(parentType),
521 childType = parentType;
523 childType = parentType;
526 void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node)
override {
527 isDirectChildOfVariableBracketAccessorNode =
false;
528 ReportAnyError(node);
530 void OnVisitFunctionCallNode(FunctionCallNode& node)
override {
531 isDirectChildOfVariableBracketAccessorNode =
false;
532 childType = ValidateFunction(node);
534 void OnVisitEmptyNode(EmptyNode& node)
override {
535 ReportAnyError(node);
537 if (parentType == Type::Number) {
538 message = _(
"You must enter a number or a valid expression call.");
539 }
else if (parentType == Type::String) {
541 "You must enter a text (between quotes) or a valid expression call.");
542 }
else if (parentType == Type::Variable ||
543 parentType == Type::VariableOrProperty ||
544 parentType == Type::VariableOrPropertyOrParameter ||
545 parentType == Type::ObjectVariable ||
546 parentType == Type::LegacyVariable) {
547 message = _(
"You must enter a variable name.");
548 }
else if (parentType == Type::Object) {
549 message = _(
"You must enter a valid object name.");
550 }
else if (isDirectChildOfVariableBracketAccessorNode) {
551 message = _(
"You must enter a valid expression inside the brackets.");
554 message = _(
"You must enter a valid expression.");
556 RaiseTypeError(message, node.location);
557 childType = Type::Empty;
572 VariableOrPropertyOrParameter
575 bool ValidateObjectVariableOrVariableOrProperty(
const gd::IdentifierNode& identifier);
576 bool ValidateObjectVariableOrVariableOrProperty(
581 const bool isUndeclaredVariableFatal,
582 const bool hasMoreChildren =
false);
583 bool ValidateChildVariable(
586 const bool isUndeclaredVariableFatal);
587 void ValidateLastChildVariable(
591 bool CheckVariableExistence(
const ExpressionParserLocation &location,
593 if (currentParameterExtraInfo !=
"AllowUndeclaredVariable") {
594 bool isRootVariableDeclared =
false;
595 projectScopedContainers.MatchIdentifierWithName<
void>(
599 RaiseVariableNameCollisionError(
600 _(
"This variable has the same name as an object. Consider "
601 "renaming one or the other."),
606 isRootVariableDeclared =
true;
610 if (parentType != Type::VariableOrProperty &&
611 parentType != Type::VariableOrPropertyOrParameter) {
612 RaiseVariableNameCollisionError(
613 _(
"This variable has the same name as a property. Consider "
614 "renaming one or the other."),
616 }
else if (hasChild) {
617 RaiseMalformedVariableParameter(
618 _(
"Properties can't have children."), location, name);
623 if (parentType != Type::VariableOrPropertyOrParameter) {
624 RaiseVariableNameCollisionError(
625 _(
"This variable has the same name as a parameter. Consider "
626 "renaming one or the other."),
628 }
else if (hasChild) {
629 RaiseMalformedVariableParameter(
630 _(
"Properties can't have children."), location, name);
635 RaiseUndeclaredVariableError(
636 _(
"No variable with this name found."), location,
639 return isRootVariableDeclared;
644 void ReportAnyError(
const ExpressionNode& node,
bool isFatal =
true) {
645 if (node.diagnostic) {
649 allErrors.push_back(node.diagnostic.get());
651 fatalErrors.push_back(node.diagnostic.get());
656 void RaiseError(gd::ExpressionParserError::ErrorType type,
658 const ExpressionParserLocation &location,
bool isFatal =
true,
661 auto diagnostic = gd::make_unique<ExpressionParserError>(
662 type, message, location, actualValue, objectName);
663 allErrors.push_back(diagnostic.get());
665 fatalErrors.push_back(diagnostic.get());
670 supplementalErrors.push_back(std::move(diagnostic));
673 void RaiseUnknownIdentifierError(
const gd::String &message,
674 const ExpressionParserLocation &location) {
675 RaiseError(gd::ExpressionParserError::ErrorType::UnknownIdentifier, message,
679 void RaiseUndeclaredVariableError(
const gd::String &message,
680 const ExpressionParserLocation &location,
683 const bool isUndeclaredVariableFatal =
true) {
684 RaiseError(gd::ExpressionParserError::ErrorType::UndeclaredVariable,
685 message, location, isUndeclaredVariableFatal, variableName, objectName);
688 void RaiseVariableNameCollisionError(
const gd::String &message,
689 const ExpressionParserLocation &location,
692 RaiseError(gd::ExpressionParserError::ErrorType::VariableNameCollision,
693 message, location,
false, variableName, objectName);
696 void RaiseMalformedVariableParameter(
const gd::String &message,
697 const ExpressionParserLocation &location,
699 RaiseError(gd::ExpressionParserError::ErrorType::MalformedVariableParameter,
700 message, location,
true, variableName,
"");
703 void RaiseTypeError(
const gd::String &message,
704 const ExpressionParserLocation &location,
705 bool isFatal =
true) {
706 RaiseError(gd::ExpressionParserError::ErrorType::MismatchedType, message,
710 void RaiseOperatorError(
const gd::String &message,
711 const ExpressionParserLocation &location) {
712 RaiseError(gd::ExpressionParserError::ErrorType::InvalidOperator, message,
717 if (variableType == gd::Variable::Number) {
718 childType = Type::Number;
719 }
else if (variableType == gd::Variable::String) {
720 childType = Type::String;
726 static bool ShouldTypeBeRefined(
Type type) {
727 return (type == Type::Unknown || type == Type::NumberOrString);
735 static const gd::String numberOrStringTypeString;
737 static const gd::String legacyVariableTypeString;
744 static const gd::String emptyParameterExtraInfo;
746 std::vector<ExpressionParserError*> fatalErrors;
747 std::vector<ExpressionParserError*> allErrors;
748 std::vector<ExpressionParserError*> deprecationWarnings;
749 std::vector<std::unique_ptr<ExpressionParserError>> supplementalErrors;
754 bool forbidsUsageOfBracketsBecauseParentIsObject;
758 size_t variableChildDepth = 0;
760 bool isDirectChildOfVariableBracketAccessorNode =
false;
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:35
const std::vector< ExpressionParserError * > & GetFatalErrors()
Get only the fatal errors.
Definition: ExpressionValidator.h:71
const std::vector< ExpressionParserError * > & GetDeprecationWarnings()
Get all deprecation warnings.
Definition: ExpressionValidator.h:88
const std::vector< ExpressionParserError * > & GetAllErrors()
Get all the errors.
Definition: ExpressionValidator.h:80
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:57
Holds references to variables, objects, properties and other containers.
Definition: ProjectScopedContainers.h:36
String represents an UTF8 encoded string.
Definition: String.h:33
Defines a variable which can be used by an object, a layout or a project.
Definition: Variable.h:29
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