6 #ifndef GDCORE_EXPRESSIONVALIDATOR_H
7 #define GDCORE_EXPRESSIONVALIDATOR_H
11 #include "GDCore/Events/Parsers/ExpressionParser2Node.h"
12 #include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
13 #include "GDCore/Tools/MakeUnique.h"
15 #include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
16 #include "GDCore/Project/ProjectScopedContainers.h"
17 #include "GDCore/Project/VariablesContainersList.h"
21 class ObjectsContainer;
22 class VariablesContainer;
24 class ParameterMetadata;
25 class ExpressionMetadata;
26 class VariablesContainersList;
27 class ProjectScopedContainers;
43 : platform(platform_),
44 projectScopedContainers(projectScopedContainers_),
46 childType(Type::Unknown),
47 forbidsUsageOfBracketsBecauseParentIsObject(
false) {};
59 node.Visit(validator);
84 node.expression->Visit(*
this);
86 void OnVisitOperatorNode(OperatorNode& node)
override {
91 node.leftHandSide->Visit(*
this);
92 const Type leftType = childType;
94 if (leftType == Type::Number) {
96 RaiseError(
"syntax_error",
97 "No operator found. Did you forget to enter an operator (like +, -, "
98 "* or /) between numbers or expressions?", node.rightHandSide->location);
101 else if (leftType == Type::String) {
102 if (node.op ==
' ') {
103 RaiseError(
"syntax_error",
104 "You must add the operator + between texts or expressions. For "
105 "example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
107 else if (node.op !=
'+') {
109 _(
"You've used an operator that is not supported. Only + can be used "
110 "to concatenate texts."),
111 ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
113 }
else if (leftType == Type::Object) {
115 _(
"Operators (+, -, /, *) can't be used with an object name. Remove "
117 node.rightHandSide->location);
118 }
else if (leftType == Type::Variable) {
120 _(
"Operators (+, -, /, *) can't be used in variable names. Remove "
121 "the operator from the variable name."),
122 node.rightHandSide->location);
128 parentType = ShouldTypeBeRefined(parentType) ? leftType : parentType;
129 node.rightHandSide->Visit(*
this);
130 const Type rightType = childType;
137 childType = ShouldTypeBeRefined(parentType) ? (ShouldTypeBeRefined(leftType) ? leftType : rightType) : parentType;
139 void OnVisitUnaryOperatorNode(UnaryOperatorNode& node)
override {
140 ReportAnyError(node);
141 node.factor->Visit(*
this);
142 const Type rightType = childType;
144 if (rightType == Type::Number) {
145 if (node.op !=
'+' && node.op !=
'-') {
149 _(
"You've used an \"unary\" operator that is not supported. Operator "
154 }
else if (rightType == Type::String) {
156 _(
"You've used an operator that is not supported. Only + can be used "
157 "to concatenate texts, and must be placed between two texts (or "
160 }
else if (rightType == Type::Object) {
162 _(
"Operators (+, -) can't be used with an object name. Remove the "
165 }
else if (rightType == Type::Variable) {
167 _(
"Operators (+, -) can't be used in variable names. Remove "
168 "the operator from the variable name."),
172 void OnVisitNumberNode(NumberNode& node)
override {
173 ReportAnyError(node);
174 childType = Type::Number;
175 if (parentType == Type::String) {
177 _(
"You entered a number, but a text was expected (in quotes)."),
179 }
else if (parentType != Type::Number &&
180 parentType != Type::NumberOrString) {
181 RaiseTypeError(_(
"You entered a number, but this type was expected:") +
182 " " + TypeToString(parentType),
186 void OnVisitTextNode(TextNode& node)
override {
187 ReportAnyError(node);
188 childType = Type::String;
189 if (parentType == Type::Number) {
190 RaiseTypeError(_(
"You entered a text, but a number was expected."),
192 }
else if (parentType != Type::String &&
193 parentType != Type::NumberOrString) {
194 RaiseTypeError(_(
"You entered a text, but this type was expected:") +
195 " " + TypeToString(parentType),
199 void OnVisitVariableNode(VariableNode& node)
override {
200 ReportAnyError(node);
202 if (parentType == Type::Variable) {
203 childType = Type::Variable;
206 node.child->Visit(*
this);
208 }
else if (parentType == Type::String || parentType == Type::Number || parentType == Type::NumberOrString) {
210 childType = parentType;
212 const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
213 const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
214 const auto& propertiesContainerList = projectScopedContainers.GetPropertiesContainersList();
216 forbidsUsageOfBracketsBecauseParentIsObject =
false;
217 projectScopedContainers.MatchIdentifierWithName<
void>(node.name,
223 forbidsUsageOfBracketsBecauseParentIsObject =
true;
229 RaiseTypeError(_(
"Accessing a child variable of a property is not possible - just write the property name."),
234 RaiseTypeError(_(
"Accessing a child variable of a parameter is not possible - just write the parameter name."),
238 RaiseTypeError(_(
"No object, variable or property with this name found."),
243 node.child->Visit(*
this);
246 forbidsUsageOfBracketsBecauseParentIsObject =
false;
248 RaiseTypeError(_(
"You entered a variable, but this type was expected:") +
249 " " + TypeToString(parentType),
253 node.child->Visit(*
this);
257 void OnVisitVariableAccessorNode(VariableAccessorNode& node)
override {
258 ReportAnyError(node);
262 forbidsUsageOfBracketsBecauseParentIsObject =
false;
265 node.child->Visit(*
this);
268 void OnVisitVariableBracketAccessorNode(
269 VariableBracketAccessorNode& node)
override {
270 ReportAnyError(node);
272 if (forbidsUsageOfBracketsBecauseParentIsObject) {
273 RaiseError(
"brackets_not_allowed_for_objects",
274 _(
"You can't use the brackets to access an object variable. "
275 "Use a dot followed by the variable name, like this: "
276 "`MyObject.MyVariable`."),
279 forbidsUsageOfBracketsBecauseParentIsObject =
false;
281 Type currentParentType = parentType;
282 parentType = Type::NumberOrString;
283 node.expression->Visit(*
this);
284 parentType = currentParentType;
287 node.child->Visit(*
this);
290 void OnVisitIdentifierNode(IdentifierNode& node)
override {
291 ReportAnyError(node);
292 if (parentType == Type::String) {
293 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
296 RaiseTypeError(_(
"You must wrap your text inside double quotes "
297 "(example: \"Hello world\")."),
301 else if (parentType == Type::Number) {
302 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
305 _(
"You must enter a number."), node.location);
308 else if (parentType == Type::NumberOrString) {
309 if (!ValidateObjectVariableOrVariableOrProperty(node)) {
313 _(
"You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
317 else if (parentType != Type::Object && parentType != Type::Variable) {
320 _(
"You've entered a name, but this type was expected:") +
" " + TypeToString(parentType),
322 childType = parentType;
324 childType = parentType;
327 void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node)
override {
328 ReportAnyError(node);
330 void OnVisitFunctionCallNode(FunctionCallNode& node)
override {
331 childType = ValidateFunction(node);
333 void OnVisitEmptyNode(EmptyNode& node)
override {
334 ReportAnyError(node);
336 if (parentType == Type::Number) {
337 message = _(
"You must enter a number or a valid expression call.");
338 }
else if (parentType == Type::String) {
340 "You must enter a text (between quotes) or a valid expression call.");
341 }
else if (parentType == Type::Variable) {
342 message = _(
"You must enter a variable name.");
343 }
else if (parentType == Type::Object) {
344 message = _(
"You must enter a valid object name.");
347 message = _(
"You must enter a valid expression.");
349 RaiseTypeError(message, node.location);
350 childType = Type::Empty;
354 enum Type {Unknown = 0, Number, String, NumberOrString, Variable, Object, Empty};
356 bool ValidateObjectVariableOrVariableOrProperty(
const gd::IdentifierNode& identifier);
358 void ReportAnyError(
const ExpressionNode& node,
bool isFatal =
true) {
359 if (node.diagnostic && node.diagnostic->IsError()) {
363 allErrors.push_back(node.diagnostic.get());
365 fatalErrors.push_back(node.diagnostic.get());
371 const gd::String &message,
const ExpressionParserLocation &location,
bool isFatal =
true) {
372 auto diagnostic = gd::make_unique<ExpressionParserError>(
373 type, message, location);
374 allErrors.push_back(diagnostic.get());
376 fatalErrors.push_back(diagnostic.get());
381 supplementalErrors.push_back(std::move(diagnostic));
385 const gd::String &message,
const ExpressionParserLocation &location,
bool isFatal =
true) {
386 RaiseError(
"type_error", message, location, isFatal);
389 void RaiseOperatorError(
390 const gd::String &message,
const ExpressionParserLocation &location) {
391 RaiseError(
"invalid_operator", message, location);
394 void ReadChildTypeFromVariable(gd::Variable::Type variableType) {
395 if (variableType == gd::Variable::Number) {
396 childType = Type::Number;
397 }
else if (variableType == gd::Variable::String) {
398 childType = Type::String;
404 static bool ShouldTypeBeRefined(
Type type) {
405 return (type == Type::Unknown || type == Type::NumberOrString);
413 static const gd::String numberOrStringTypeString;
419 std::vector<ExpressionParserDiagnostic*> fatalErrors;
420 std::vector<ExpressionParserDiagnostic*> allErrors;
421 std::vector<std::unique_ptr<ExpressionParserDiagnostic>> supplementalErrors;
424 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< ExpressionParserDiagnostic * > & GetAllErrors()
Get all the errors.
Definition: ExpressionValidator.h:77
const std::vector< ExpressionParserDiagnostic * > & GetFatalErrors()
Get only the fatal errors.
Definition: ExpressionValidator.h:68
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:54
Holds references to variables, objects, properties and other containers.
Definition: ProjectScopedContainers.h:30
String represents an UTF8 encoded string.
Definition: String.h:31
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:93
A function call node (either free function, object function or object behavior function)....
Definition: ExpressionParser2Node.h:364
An identifier node, usually representing an object or a variable with an optional function name or ch...
Definition: ExpressionParser2Node.h:197
Definition: ExpressionParser2Node.h:108