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;
43 const gd::String &rootObjectName_ = emptyParameterExtraInfo,
44 const gd::String &extraInfo_ = emptyParameterExtraInfo)
45 : platform(platform_),
46 projectScopedContainers(projectScopedContainers_),
48 rootObjectName(rootObjectName_),
49 childType(Type::Unknown),
50 forbidsUsageOfBracketsBecauseParentIsObject(
false),
51 currentParameterExtraInfo(&extraInfo_),
53 variableObjectNameLocation() {};
65 node.Visit(validator);
90 const std::vector<ExpressionParserError*> &
92 return deprecationWarnings;
98 node.expression->Visit(*
this);
100 void OnVisitOperatorNode(OperatorNode& node)
override {
101 ReportAnyError(node);
105 node.leftHandSide->Visit(*
this);
106 const Type leftType = childType;
108 if (parentType == Type::Variable || parentType == Type::ObjectVariable ||
109 parentType == Type::LegacyVariable) {
111 _(
"Operators (+, -, /, *) can't be used in variable names. Remove "
112 "the operator from the variable name."),
113 node.rightHandSide->location);
114 }
else if (leftType == Type::Number) {
115 if (node.op ==
' ') {
116 RaiseError(gd::ExpressionParserError::ErrorType::SyntaxError,
117 "No operator found. Did you forget to enter an operator (like +, -, "
118 "* or /) between numbers or expressions?", node.rightHandSide->location);
120 }
else if (leftType == Type::String) {
121 if (node.op ==
' ') {
122 RaiseError(gd::ExpressionParserError::ErrorType::SyntaxError,
123 "You must add the operator + between texts or expressions. For "
124 "example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
126 else if (node.op !=
'+') {
128 _(
"You've used an operator that is not supported. Only + can be used "
129 "to concatenate texts."),
130 ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
132 }
else if (leftType == Type::Object) {
134 _(
"Operators (+, -, /, *) can't be used with an object name. Remove "
136 node.rightHandSide->location);
142 parentType = ShouldTypeBeRefined(parentType) ? leftType : parentType;
143 node.rightHandSide->Visit(*
this);
144 const Type rightType = childType;
151 childType = ShouldTypeBeRefined(parentType) ? (ShouldTypeBeRefined(leftType) ? leftType : rightType) : parentType;
153 void OnVisitUnaryOperatorNode(UnaryOperatorNode& node)
override {
154 ReportAnyError(node);
155 node.factor->Visit(*
this);
156 const Type rightType = childType;
158 if (parentType == Type::Variable || parentType == Type::ObjectVariable ||
159 parentType == Type::LegacyVariable) {
161 _(
"Operators (+, -) can't be used in variable names. Remove "
162 "the operator from the variable name."),
164 }
else if (rightType == Type::Number) {
165 if (node.op !=
'+' && node.op !=
'-') {
169 _(
"You've used an \"unary\" operator that is not supported. Operator "
174 }
else if (rightType == Type::String) {
176 _(
"You've used an operator that is not supported. Only + can be used "
177 "to concatenate texts, and must be placed between two texts (or "
180 }
else if (rightType == Type::Object) {
182 _(
"Operators (+, -) can't be used with an object name. Remove the "
187 void OnVisitNumberNode(NumberNode& node)
override {
188 ReportAnyError(node);
189 childType = Type::Number;
190 if (parentType == Type::String) {
192 _(
"You entered a number, but a text was expected (in quotes)."),
194 }
else if (parentType != Type::Number &&
195 parentType != Type::NumberOrString) {
196 RaiseTypeError(_(
"You entered a number, but this type was expected:") +
197 " " + TypeToString(parentType),
201 void OnVisitTextNode(TextNode& node)
override {
202 ReportAnyError(node);
203 childType = Type::String;
204 if (parentType == Type::Number) {
205 RaiseTypeError(_(
"You entered a text, but a number was expected."),
207 }
else if (parentType != Type::String &&
208 parentType != Type::NumberOrString) {
209 RaiseTypeError(_(
"You entered a text, but this type was expected:") +
210 " " + TypeToString(parentType),
214 void OnVisitVariableNode(VariableNode& node)
override {
215 ReportAnyError(node);
216 parentVariable =
nullptr;
217 variableChildDepth = 0;
219 if (parentType == Type::Variable ||
220 parentType == Type::VariableOrProperty ||
221 parentType == Type::VariableOrPropertyOrParameter) {
222 childType = parentType;
224 bool isRootVariableDeclared = CheckVariableExistence(
225 node.location, node.name, node.child !=
nullptr);
227 if (isRootVariableDeclared) {
228 const auto &variable =
229 projectScopedContainers.GetVariablesContainersList().Get(
231 parentVariable = &variable;
233 node.child->Visit(*
this);
235 }
else if (parentType == Type::ObjectVariable) {
236 childType = parentType;
238 if (!rootObjectName.empty()) {
239 ValidateObjectVariableOrVariableOrProperty(
240 rootObjectName, node.nameLocation, node.name, node.nameLocation,
241 false, !!node.child);
243 const auto &objectsContainersList =
244 projectScopedContainers.GetObjectsContainersList();
245 auto variableExistence =
246 objectsContainersList.HasObjectOrGroupWithVariableNamed(
247 rootObjectName, node.name);
248 if (variableExistence == gd::ObjectsContainersList::Exists) {
249 const auto &objectVariable =
250 objectsContainersList
251 .GetObjectOrGroupVariablesContainer(rootObjectName)
254 parentVariable = &objectVariable;
256 ValidateLastChildVariable(objectVariable, node.nameLocation);
262 node.child->Visit(*
this);
264 }
else if (parentType == Type::LegacyVariable) {
265 childType = parentType;
268 node.child->Visit(*
this);
270 }
else if (parentType == Type::String || parentType == Type::Number ||
271 parentType == Type::NumberOrString) {
273 childType = parentType;
275 const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
276 const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
277 const auto& propertiesContainerList = projectScopedContainers.GetPropertiesContainersList();
279 forbidsUsageOfBracketsBecauseParentIsObject =
false;
280 projectScopedContainers.MatchIdentifierWithName<
void>(node.name,
283 variableObjectName = node.name;
284 variableObjectNameLocation = node.nameLocation;
287 forbidsUsageOfBracketsBecauseParentIsObject =
true;
290 const auto &variable =
291 projectScopedContainers.GetVariablesContainersList().Get(
294 parentVariable = &variable;
296 ValidateLastChildVariable(variable, node.location);
301 RaiseTypeError(_(
"Accessing a child variable of a property is not possible - just write the property name."),
306 RaiseTypeError(_(
"Accessing a child variable of a parameter is not possible - just write the parameter name."),
310 RaiseTypeError(_(
"No object, variable or property with this name found."),
315 node.child->Visit(*
this);
318 forbidsUsageOfBracketsBecauseParentIsObject =
false;
320 RaiseTypeError(_(
"You entered a variable, but this type was expected:") +
321 " " + TypeToString(parentType),
325 node.child->Visit(*
this);
329 void OnVisitVariableAccessorNode(VariableAccessorNode& node)
override {
330 ReportAnyError(node);
332 if (!variableObjectName.empty()) {
333 ValidateObjectVariableOrVariableOrProperty(
334 variableObjectName, variableObjectNameLocation, node.name,
335 node.nameLocation,
true, !!node.child);
337 const auto &objectsContainersList =
338 projectScopedContainers.GetObjectsContainersList();
339 auto variableExistence =
340 objectsContainersList.HasObjectOrGroupWithVariableNamed(
341 variableObjectName, node.name);
342 if (variableExistence == gd::ObjectsContainersList::Exists) {
343 const auto &objectVariable =
344 objectsContainersList
345 .GetObjectOrGroupVariablesContainer(variableObjectName)
348 parentVariable = &objectVariable;
350 parentVariable =
nullptr;
353 parentVariable =
nullptr;
355 variableChildDepth = 0;
356 variableObjectName =
"";
357 }
else if (parentVariable) {
358 const bool isChildVariableDeclared = ValidateChildVariable(
359 *parentVariable, node.name, node.nameLocation,
false);
360 if (isChildVariableDeclared) {
361 const auto &childVariable = parentVariable->GetChild(node.name);
363 parentVariable = &childVariable;
364 variableChildDepth++;
366 ValidateLastChildVariable(childVariable, node.nameLocation);
367 parentVariable =
nullptr;
368 variableChildDepth = 0;
372 parentVariable =
nullptr;
373 variableChildDepth = 0;
378 forbidsUsageOfBracketsBecauseParentIsObject =
false;
381 node.child->Visit(*
this);
384 void OnVisitVariableBracketAccessorNode(
385 VariableBracketAccessorNode& node)
override {
386 ReportAnyError(node);
388 variableObjectName =
"";
389 parentVariable =
nullptr;
390 variableChildDepth = 0;
391 if (forbidsUsageOfBracketsBecauseParentIsObject) {
392 RaiseError(gd::ExpressionParserError::ErrorType::BracketsNotAllowedForObjects,
393 _(
"You can't use the brackets to access an object variable. "
394 "Use a dot followed by the variable name, like this: "
395 "`MyObject.MyVariable`."),
398 forbidsUsageOfBracketsBecauseParentIsObject =
false;
400 Type currentParentType = parentType;
401 Type currentChildType = childType;
402 parentType = Type::NumberOrString;
403 auto parentParameterExtraInfo = currentParameterExtraInfo;
404 currentParameterExtraInfo =
nullptr;
405 node.expression->Visit(*
this);
406 currentParameterExtraInfo = parentParameterExtraInfo;
407 parentType = currentParentType;
408 childType = currentChildType;
411 node.child->Visit(*
this);
414 void OnVisitIdentifierNode(IdentifierNode& node)
override {
415 ReportAnyError(node);
416 if (parentType == Type::String) {
417 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
420 RaiseUnknownIdentifierError(_(
"You must wrap your text inside double quotes "
421 "(example: \"Hello world\")."),
425 else if (parentType == Type::Number) {
426 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
428 RaiseUnknownIdentifierError(_(
"You must enter a number."), node.location);
431 else if (parentType == Type::NumberOrString) {
432 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
435 RaiseUnknownIdentifierError(
436 _(
"You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
439 }
else if (parentType == Type::Variable ||
440 parentType == Type::VariableOrProperty ||
441 parentType == Type::VariableOrPropertyOrParameter) {
442 bool isRootVariableDeclared =
443 CheckVariableExistence(node.location, node.identifierName,
444 !node.childIdentifierName.empty());
445 if (isRootVariableDeclared && !node.childIdentifierName.empty()) {
446 ValidateObjectVariableOrVariableOrProperty(
447 node.identifierName, node.identifierNameLocation,
448 node.childIdentifierName, node.childIdentifierNameLocation,
false);
450 }
else if (parentType == Type::ObjectVariable) {
451 childType = parentType;
452 if (!rootObjectName.empty()) {
453 ValidateObjectVariableOrVariableOrProperty(
454 rootObjectName, node.identifierNameLocation, node.identifierName,
455 node.identifierNameLocation,
false,
456 !node.childIdentifierName.empty());
458 const auto &objectsContainersList =
459 projectScopedContainers.GetObjectsContainersList();
460 auto variableExistence =
461 objectsContainersList.HasObjectOrGroupWithVariableNamed(
462 rootObjectName, node.identifierName);
463 if (variableExistence == gd::ObjectsContainersList::Exists) {
464 const auto &objectVariable =
465 objectsContainersList
466 .GetObjectOrGroupVariablesContainer(rootObjectName)
467 ->Get(node.identifierName);
468 if (!node.childIdentifierName.empty()) {
469 const bool isChildVariableDeclared =
470 ValidateChildVariable(objectVariable, node.childIdentifierName,
471 node.childIdentifierNameLocation,
false);
472 if (isChildVariableDeclared) {
473 const auto &childVariable =
474 objectVariable.GetChild(node.childIdentifierName);
475 ValidateLastChildVariable(childVariable,
476 node.childIdentifierNameLocation);
482 }
else if (parentType != Type::Object &&
483 parentType != Type::LegacyVariable) {
486 _(
"You've entered a name, but this type was expected:") +
" " + TypeToString(parentType),
488 childType = parentType;
490 childType = parentType;
493 void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node)
override {
494 ReportAnyError(node);
496 void OnVisitFunctionCallNode(FunctionCallNode& node)
override {
497 childType = ValidateFunction(node);
499 void OnVisitEmptyNode(EmptyNode& node)
override {
500 ReportAnyError(node);
502 if (parentType == Type::Number) {
503 message = _(
"You must enter a number or a valid expression call.");
504 }
else if (parentType == Type::String) {
506 "You must enter a text (between quotes) or a valid expression call.");
507 }
else if (parentType == Type::Variable ||
508 parentType == Type::ObjectVariable ||
509 parentType == Type::LegacyVariable) {
510 message = _(
"You must enter a variable name.");
511 }
else if (parentType == Type::Object) {
512 message = _(
"You must enter a valid object name.");
515 message = _(
"You must enter a valid expression.");
517 RaiseTypeError(message, node.location);
518 childType = Type::Empty;
533 VariableOrPropertyOrParameter
536 bool ValidateObjectVariableOrVariableOrProperty(
const gd::IdentifierNode& identifier);
537 bool ValidateObjectVariableOrVariableOrProperty(
542 const bool isUndeclaredVariableFatal,
543 const bool hasMoreChildren =
false);
544 bool ValidateChildVariable(
547 const bool isUndeclaredVariableFatal);
548 void ValidateLastChildVariable(
552 bool CheckVariableExistence(
const ExpressionParserLocation &location,
554 if (!currentParameterExtraInfo ||
555 *currentParameterExtraInfo !=
"AllowUndeclaredVariable") {
556 bool isRootVariableDeclared =
false;
557 projectScopedContainers.MatchIdentifierWithName<
void>(
561 RaiseVariableNameCollisionError(
562 _(
"This variable has the same name as an object. Consider "
563 "renaming one or the other."),
568 isRootVariableDeclared =
true;
572 if (parentType != Type::VariableOrProperty &&
573 parentType != Type::VariableOrPropertyOrParameter) {
574 RaiseVariableNameCollisionError(
575 _(
"This variable has the same name as a property. Consider "
576 "renaming one or the other."),
578 }
else if (hasChild) {
579 RaiseMalformedVariableParameter(
580 _(
"Properties can't have children."), location, name);
585 if (parentType != Type::VariableOrPropertyOrParameter) {
586 RaiseVariableNameCollisionError(
587 _(
"This variable has the same name as a parameter. Consider "
588 "renaming one or the other."),
590 }
else if (hasChild) {
591 RaiseMalformedVariableParameter(
592 _(
"Properties can't have children."), location, name);
597 RaiseUndeclaredVariableError(
598 _(
"No variable with this name found."), location,
601 return isRootVariableDeclared;
606 void ReportAnyError(
const ExpressionNode& node,
bool isFatal =
true) {
607 if (node.diagnostic) {
611 allErrors.push_back(node.diagnostic.get());
613 fatalErrors.push_back(node.diagnostic.get());
618 void RaiseError(gd::ExpressionParserError::ErrorType type,
620 const ExpressionParserLocation &location,
bool isFatal =
true,
623 auto diagnostic = gd::make_unique<ExpressionParserError>(
624 type, message, location, actualValue, objectName);
625 allErrors.push_back(diagnostic.get());
627 fatalErrors.push_back(diagnostic.get());
632 supplementalErrors.push_back(std::move(diagnostic));
635 void RaiseUnknownIdentifierError(
const gd::String &message,
636 const ExpressionParserLocation &location) {
637 RaiseError(gd::ExpressionParserError::ErrorType::UnknownIdentifier, message,
641 void RaiseUndeclaredVariableError(
const gd::String &message,
642 const ExpressionParserLocation &location,
645 const bool isUndeclaredVariableFatal =
true) {
646 RaiseError(gd::ExpressionParserError::ErrorType::UndeclaredVariable,
647 message, location, isUndeclaredVariableFatal, variableName, objectName);
650 void RaiseVariableNameCollisionError(
const gd::String &message,
651 const ExpressionParserLocation &location,
654 RaiseError(gd::ExpressionParserError::ErrorType::VariableNameCollision,
655 message, location,
false, variableName, objectName);
658 void RaiseMalformedVariableParameter(
const gd::String &message,
659 const ExpressionParserLocation &location,
661 RaiseError(gd::ExpressionParserError::ErrorType::MalformedVariableParameter,
662 message, location,
true, variableName,
"");
665 void RaiseTypeError(
const gd::String &message,
666 const ExpressionParserLocation &location,
667 bool isFatal =
true) {
668 RaiseError(gd::ExpressionParserError::ErrorType::MismatchedType, message,
672 void RaiseOperatorError(
const gd::String &message,
673 const ExpressionParserLocation &location) {
674 RaiseError(gd::ExpressionParserError::ErrorType::InvalidOperator, message,
679 if (variableType == gd::Variable::Number) {
680 childType = Type::Number;
681 }
else if (variableType == gd::Variable::String) {
682 childType = Type::String;
688 static bool ShouldTypeBeRefined(
Type type) {
689 return (type == Type::Unknown || type == Type::NumberOrString);
697 static const gd::String numberOrStringTypeString;
699 static const gd::String legacyVariableTypeString;
706 static const gd::String emptyParameterExtraInfo;
708 std::vector<ExpressionParserError*> fatalErrors;
709 std::vector<ExpressionParserError*> allErrors;
710 std::vector<ExpressionParserError*> deprecationWarnings;
711 std::vector<std::unique_ptr<ExpressionParserError>> supplementalErrors;
716 bool forbidsUsageOfBracketsBecauseParentIsObject;
720 size_t variableChildDepth = 0;
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:74
const std::vector< ExpressionParserError * > & GetDeprecationWarnings()
Get all deprecation warnings.
Definition: ExpressionValidator.h:91
const std::vector< ExpressionParserError * > & GetAllErrors()
Get all the errors.
Definition: ExpressionValidator.h:83
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:60
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