GDevelop Core
Core library for developing platforms and tools compatible with GDevelop.
ExpressionValidator.h
1 /*
2  * GDevelop Core
3  * Copyright 2008-present Florian Rival ([email protected]). All rights
4  * reserved. This project is released under the MIT License.
5  */
6 #pragma once
7 
8 #include <memory>
9 #include <vector>
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"
17 
18 namespace gd {
19 class Expression;
20 class ObjectsContainer;
21 class VariablesContainer;
22 class Platform;
23 class VariablesContainersList;
24 class ProjectScopedContainers;
25 } // namespace gd
26 
27 namespace gd {
28 
36  public:
37  ExpressionValidator(const gd::Platform &platform_,
38  const gd::ProjectScopedContainers & projectScopedContainers_,
39  const gd::String &rootType_,
40  const gd::String &rootObjectName_ = emptyParameterExtraInfo,
41  const gd::String &extraInfo_ = emptyParameterExtraInfo)
42  : platform(platform_),
43  projectScopedContainers(projectScopedContainers_),
44  parentType(StringToType(gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(rootType_))),
45  rootObjectName(rootObjectName_),
46  childType(Type::Unknown),
47  forbidsUsageOfBracketsBecauseParentIsObject(false),
48  currentParameterExtraInfo(extraInfo_),
49  variableObjectName(),
50  variableObjectNameLocation() {};
51  virtual ~ExpressionValidator(){};
52 
57  static bool HasNoErrors(const gd::Platform &platform,
58  const gd::ProjectScopedContainers & projectScopedContainers,
59  const gd::String &rootType,
60  gd::ExpressionNode& node) {
61  gd::ExpressionValidator validator(platform, projectScopedContainers, rootType);
62  node.Visit(validator);
63  return validator.GetAllErrors().empty();
64  }
65 
71  const std::vector<ExpressionParserError*>& GetFatalErrors() {
72  return fatalErrors;
73  };
74 
80  const std::vector<ExpressionParserError*>& GetAllErrors() {
81  return allErrors;
82  };
83 
87  const std::vector<ExpressionParserError*> &
89  return deprecationWarnings;
90  };
91 
92  protected:
93  void OnVisitSubExpressionNode(SubExpressionNode& node) override {
94  isDirectChildOfVariableBracketAccessorNode = false;
95  ReportAnyError(node);
96  node.expression->Visit(*this);
97  }
98  void OnVisitOperatorNode(OperatorNode& node) override {
99  isDirectChildOfVariableBracketAccessorNode = false;
100  ReportAnyError(node);
101 
102  // The "required" type ("parentType") will be used when visiting the first operand.
103  // Note that it may be refined thanks to this first operand (see later).
104  node.leftHandSide->Visit(*this);
105  const Type leftType = childType; // Store the type of the first operand.
106 
107  if (parentType == Type::Variable ||
108  parentType == Type::VariableOrProperty ||
109  parentType == Type::VariableOrPropertyOrParameter ||
110  parentType == Type::ObjectVariable ||
111  parentType == Type::LegacyVariable) {
112  RaiseOperatorError(
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);
121  }
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);
127  }
128  else if (node.op != '+') {
129  RaiseOperatorError(
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()));
133  }
134  } else if (leftType == Type::Object) {
135  RaiseOperatorError(
136  _("Operators (+, -, /, *) can't be used with an object name. Remove "
137  "the operator."),
138  node.rightHandSide->location);
139  }
140 
141  // The "required" type ("parentType") of the second operator is decided by:
142  // - the parent type. Unless it can (`number|string`) or should (`unknown`) be refined, then:
143  // - the first operand.
144  parentType = ShouldTypeBeRefined(parentType) ? leftType : parentType;
145  node.rightHandSide->Visit(*this);
146  const Type rightType = childType;
147 
148  // The type of the overall operator ("childType") is decided by:
149  // - the parent type. Unless it can (`number|string`) or should (`unknown`) be refined, then:
150  // - the first operand. Unless it can (`number|string`) or should (`unknown`) be refined, then:
151  // - the right operand (which got visited knowing the type of the first operand, so it's
152  // equal or strictly more precise than the left operand).
153  childType = ShouldTypeBeRefined(parentType) ? (ShouldTypeBeRefined(leftType) ? leftType : rightType) : parentType;
154  }
155  void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
156  isDirectChildOfVariableBracketAccessorNode = false;
157  ReportAnyError(node);
158  node.factor->Visit(*this);
159  const Type rightType = childType;
160 
161  if (parentType == Type::Variable ||
162  parentType == Type::VariableOrProperty ||
163  parentType == Type::VariableOrPropertyOrParameter ||
164  parentType == Type::ObjectVariable ||
165  parentType == Type::LegacyVariable) {
166  RaiseTypeError(
167  _("Operators (+, -) can't be used in variable names. Remove "
168  "the operator from the variable name."),
169  node.location);
170  } else if (rightType == Type::Number) {
171  if (node.op != '+' && node.op != '-') {
172  // This is actually a dead code because the parser takes them as
173  // binary operations with an empty left side which makes as much sense.
174  RaiseTypeError(
175  _("You've used an \"unary\" operator that is not supported. Operator "
176  "should be "
177  "either + or -."),
178  node.location);
179  }
180  } else if (rightType == Type::String) {
181  RaiseTypeError(
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 "
184  "expressions)."),
185  node.location);
186  } else if (rightType == Type::Object) {
187  RaiseTypeError(
188  _("Operators (+, -) can't be used with an object name. Remove the "
189  "operator."),
190  node.location);
191  }
192  }
193  void OnVisitNumberNode(NumberNode& node) override {
194  isDirectChildOfVariableBracketAccessorNode = false;
195  ReportAnyError(node);
196  childType = Type::Number;
197  if (parentType == Type::String) {
198  RaiseTypeError(
199  _("You entered a number, but a text was expected (in quotes)."),
200  node.location);
201  } else if (parentType == Type::Variable ||
202  parentType == Type::VariableOrProperty ||
203  parentType == Type::VariableOrPropertyOrParameter ||
204  parentType == Type::ObjectVariable ||
205  parentType == Type::LegacyVariable) {
206  RaiseTypeError(
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]."),
210  node.location);
211  } else if (parentType != Type::Number &&
212  parentType != Type::NumberOrString) {
213  RaiseTypeError(_("You entered a number, but this type was expected:") +
214  " " + TypeToString(parentType),
215  node.location);
216  }
217  }
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."),
224  node.location);
225  } else if (parentType == Type::Variable ||
226  parentType == Type::VariableOrProperty ||
227  parentType == Type::VariableOrPropertyOrParameter ||
228  parentType == Type::ObjectVariable ||
229  parentType == Type::LegacyVariable) {
230  RaiseTypeError(
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\"]."),
234  node.location);
235  } else if (parentType != Type::String &&
236  parentType != Type::NumberOrString) {
237  RaiseTypeError(_("You entered a text, but this type was expected:") +
238  " " + TypeToString(parentType),
239  node.location);
240  }
241  }
242  void OnVisitVariableNode(VariableNode& node) override {
243  isDirectChildOfVariableBracketAccessorNode = false;
244  ReportAnyError(node);
245  parentVariable = nullptr;
246  variableChildDepth = 0;
247 
248  if (parentType == Type::Variable ||
249  parentType == Type::VariableOrProperty ||
250  parentType == Type::VariableOrPropertyOrParameter) {
251  childType = parentType;
252 
253  bool isRootVariableDeclared = CheckVariableExistence(
254  node.location, node.name, node.child != nullptr);
255  if (node.child) {
256  if (isRootVariableDeclared) {
257  const auto &variable =
258  projectScopedContainers.GetVariablesContainersList().Get(
259  node.name);
260  parentVariable = &variable;
261  }
262  node.child->Visit(*this);
263  }
264  } else if (parentType == Type::ObjectVariable) {
265  childType = parentType;
266 
267  if (!rootObjectName.empty()) {
268  ValidateObjectVariableOrVariableOrProperty(
269  rootObjectName, node.nameLocation, node.name, node.nameLocation,
270  false, !!node.child);
271 
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)
281  ->Get(node.name);
282  if (node.child) {
283  parentVariable = &objectVariable;
284  } else {
285  ValidateLastChildVariable(objectVariable, node.nameLocation);
286  }
287  }
288  rootObjectName = "";
289  }
290  if (node.child) {
291  node.child->Visit(*this);
292  }
293  } else if (parentType == Type::LegacyVariable) {
294  childType = parentType;
295 
296  if (node.child) {
297  node.child->Visit(*this);
298  }
299  } else if (parentType == Type::String || parentType == Type::Number ||
300  parentType == Type::NumberOrString) {
301  // The node represents a variable or an object variable in an expression waiting for its *value* to be returned.
302  childType = parentType;
303 
304  const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
305  const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
306  const auto& propertiesContainerList = projectScopedContainers.GetPropertiesContainersList();
307 
308  forbidsUsageOfBracketsBecauseParentIsObject = false;
309  projectScopedContainers.MatchIdentifierWithName<void>(node.name,
310  [&]() {
311  // This represents an object.
312  variableObjectName = node.name;
313  variableObjectNameLocation = node.nameLocation;
314  // While understood by the parser, it's forbidden to use the bracket notation just after
315  // an object name (`MyObject["MyVariable"]`).
316  forbidsUsageOfBracketsBecauseParentIsObject = true;
317  }, [&]() {
318  // This is a variable.
319  const auto &variable =
320  projectScopedContainers.GetVariablesContainersList().Get(
321  node.name);
322  if (node.child) {
323  parentVariable = &variable;
324  } else {
325  ValidateLastChildVariable(variable, node.location);
326  }
327  }, [&]() {
328  // This is a property.
329  // Being in this node implies that there is at least a child - which is not supported for properties.
330  RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
331  node.location);
332  }, [&]() {
333  // This is a parameter.
334  // Being in this node implies that there is at least a child - which is not supported for parameters.
335  RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
336  node.location);
337  }, [&]() {
338  // This is something else.
339  RaiseTypeError(_("No object, variable or property with this name found."),
340  node.location);
341  });
342 
343  if (node.child) {
344  node.child->Visit(*this);
345  }
346 
347  forbidsUsageOfBracketsBecauseParentIsObject = false;
348  } else {
349  RaiseTypeError(_("You entered a variable, but this type was expected:") +
350  " " + TypeToString(parentType),
351  node.location);
352 
353  if (node.child) {
354  node.child->Visit(*this);
355  }
356  }
357  }
358  void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
359  isDirectChildOfVariableBracketAccessorNode = false;
360  ReportAnyError(node);
361  // TODO Also check child-variables existence on a path with only VariableAccessor to raise non-fatal errors.
362  if (!variableObjectName.empty()) {
363  ValidateObjectVariableOrVariableOrProperty(
364  variableObjectName, variableObjectNameLocation, node.name,
365  node.nameLocation, true, !!node.child);
366 
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)
376  ->Get(node.name);
377  if (node.child) {
378  parentVariable = &objectVariable;
379  } else {
380  parentVariable = nullptr;
381  }
382  } else {
383  parentVariable = nullptr;
384  }
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);
392  if (node.child) {
393  parentVariable = &childVariable;
394  variableChildDepth++;
395  } else {
396  ValidateLastChildVariable(childVariable, node.nameLocation);
397  parentVariable = nullptr;
398  variableChildDepth = 0;
399  }
400  }
401  else {
402  parentVariable = nullptr;
403  variableChildDepth = 0;
404  }
405  }
406  // In the case we accessed an object variable (`MyObject.MyVariable`),
407  // brackets can now be used (`MyObject.MyVariable["MyChildVariable"]` is now valid).
408  forbidsUsageOfBracketsBecauseParentIsObject = false;
409 
410  if (node.child) {
411  node.child->Visit(*this);
412  }
413  }
414  void OnVisitVariableBracketAccessorNode(
415  VariableBracketAccessorNode& node) override {
416  ReportAnyError(node);
417 
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`."),
426  node.location);
427  }
428  forbidsUsageOfBracketsBecauseParentIsObject = false;
429 
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;
441 
442  if (node.child) {
443  node.child->Visit(*this);
444  }
445  }
446  void OnVisitIdentifierNode(IdentifierNode& node) override {
447  isDirectChildOfVariableBracketAccessorNode = false;
448  ReportAnyError(node);
449  if (parentType == Type::String) {
450  if (!ValidateObjectVariableOrVariableOrProperty(node)) {
451  // The identifier is not a variable, so either the variable is not properly declared
452  // or it's a text without quotes.
453  RaiseUnknownIdentifierError(_("You must wrap your text inside double quotes "
454  "(example: \"Hello world\")."),
455  node.location);
456  }
457  }
458  else if (parentType == Type::Number) {
459  if (!ValidateObjectVariableOrVariableOrProperty(node)) {
460  // The identifier is not a variable, so the variable is not properly declared.
461  RaiseUnknownIdentifierError(_("You must enter a number."), node.location);
462  }
463  }
464  else if (parentType == Type::NumberOrString) {
465  if (!ValidateObjectVariableOrVariableOrProperty(node)) {
466  // The identifier is not a variable, so either the variable is not properly declared
467  // or it's a text without quotes.
468  RaiseUnknownIdentifierError(
469  _("You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
470  node.location);
471  }
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);
482  }
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());
490 
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);
510  }
511  }
512  }
513  rootObjectName = "";
514  }
515  } else if (parentType != Type::Object &&
516  parentType != Type::LegacyVariable) {
517  // It can't happen.
518  RaiseTypeError(
519  _("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
520  node.location);
521  childType = parentType;
522  } else {
523  childType = parentType;
524  }
525  }
526  void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
527  isDirectChildOfVariableBracketAccessorNode = false;
528  ReportAnyError(node);
529  }
530  void OnVisitFunctionCallNode(FunctionCallNode& node) override {
531  isDirectChildOfVariableBracketAccessorNode = false;
532  childType = ValidateFunction(node);
533  }
534  void OnVisitEmptyNode(EmptyNode& node) override {
535  ReportAnyError(node);
536  gd::String message;
537  if (parentType == Type::Number) {
538  message = _("You must enter a number or a valid expression call.");
539  } else if (parentType == Type::String) {
540  message = _(
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.");
552  } else {
553  // It can't happen.
554  message = _("You must enter a valid expression.");
555  }
556  RaiseTypeError(message, node.location);
557  childType = Type::Empty;
558  }
559 
560 private:
561  enum Type {
562  Unknown = 0,
563  Number,
564  String,
565  NumberOrString,
566  Variable,
567  ObjectVariable,
568  LegacyVariable,
569  Object,
570  Empty,
571  VariableOrProperty,
572  VariableOrPropertyOrParameter
573  };
574  Type ValidateFunction(const gd::FunctionCallNode& function);
575  bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier);
576  bool ValidateObjectVariableOrVariableOrProperty(
577  const gd::String &identifierName,
578  const gd::ExpressionParserLocation identifierNameLocation,
579  const gd::String &childIdentifierName,
580  const gd::ExpressionParserLocation childIdentifierNameLocation,
581  const bool isUndeclaredVariableFatal,
582  const bool hasMoreChildren = false);
583  bool ValidateChildVariable(
584  const gd::Variable &parentVariable, const gd::String &childVariableName,
585  const gd::ExpressionParserLocation childNameLocation,
586  const bool isUndeclaredVariableFatal);
587  void ValidateLastChildVariable(
588  const gd::Variable &lastChildVariable,
589  const gd::ExpressionParserLocation childNameLocation);
590 
591  bool CheckVariableExistence(const ExpressionParserLocation &location,
592  const gd::String &name, bool hasChild) {
593  if (currentParameterExtraInfo != "AllowUndeclaredVariable") {
594  bool isRootVariableDeclared = false;
595  projectScopedContainers.MatchIdentifierWithName<void>(
596  name,
597  [&]() {
598  // This represents an object.
599  RaiseVariableNameCollisionError(
600  _("This variable has the same name as an object. Consider "
601  "renaming one or the other."),
602  location, name);
603  },
604  [&]() {
605  // This is a variable.
606  isRootVariableDeclared = true;
607  },
608  [&]() {
609  // This is a property.
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."),
615  location, name);
616  } else if (hasChild) {
617  RaiseMalformedVariableParameter(
618  _("Properties can't have children."), location, name);
619  }
620  },
621  [&]() {
622  // This is a parameter.
623  if (parentType != Type::VariableOrPropertyOrParameter) {
624  RaiseVariableNameCollisionError(
625  _("This variable has the same name as a parameter. Consider "
626  "renaming one or the other."),
627  location, name);
628  } else if (hasChild) {
629  RaiseMalformedVariableParameter(
630  _("Properties can't have children."), location, name);
631  }
632  },
633  [&]() {
634  // This is something else.
635  RaiseUndeclaredVariableError(
636  _("No variable with this name found."), location,
637  name);
638  });
639  return isRootVariableDeclared;
640  }
641  return false;
642  }
643 
644  void ReportAnyError(const ExpressionNode& node, bool isFatal = true) {
645  if (node.diagnostic) {
646  // Syntax errors are holden by the AST nodes.
647  // It's fine to give pointers on them as the AST live longer than errors
648  // handling.
649  allErrors.push_back(node.diagnostic.get());
650  if (isFatal) {
651  fatalErrors.push_back(node.diagnostic.get());
652  }
653  }
654  }
655 
656  void RaiseError(gd::ExpressionParserError::ErrorType type,
657  const gd::String &message,
658  const ExpressionParserLocation &location, bool isFatal = true,
659  const gd::String &actualValue = "",
660  const gd::String &objectName = "") {
661  auto diagnostic = gd::make_unique<ExpressionParserError>(
662  type, message, location, actualValue, objectName);
663  allErrors.push_back(diagnostic.get());
664  if (isFatal) {
665  fatalErrors.push_back(diagnostic.get());
666  }
667  // Errors found by the validator are not holden by the AST nodes.
668  // They must be owned by the validator to keep living while errors are
669  // handled by the caller.
670  supplementalErrors.push_back(std::move(diagnostic));
671  }
672 
673  void RaiseUnknownIdentifierError(const gd::String &message,
674  const ExpressionParserLocation &location) {
675  RaiseError(gd::ExpressionParserError::ErrorType::UnknownIdentifier, message,
676  location);
677  }
678 
679  void RaiseUndeclaredVariableError(const gd::String &message,
680  const ExpressionParserLocation &location,
681  const gd::String &variableName,
682  const gd::String &objectName = "",
683  const bool isUndeclaredVariableFatal = true) {
684  RaiseError(gd::ExpressionParserError::ErrorType::UndeclaredVariable,
685  message, location, isUndeclaredVariableFatal, variableName, objectName);
686  }
687 
688  void RaiseVariableNameCollisionError(const gd::String &message,
689  const ExpressionParserLocation &location,
690  const gd::String &variableName,
691  const gd::String &objectName = "") {
692  RaiseError(gd::ExpressionParserError::ErrorType::VariableNameCollision,
693  message, location, false, variableName, objectName);
694  }
695 
696  void RaiseMalformedVariableParameter(const gd::String &message,
697  const ExpressionParserLocation &location,
698  const gd::String &variableName) {
699  RaiseError(gd::ExpressionParserError::ErrorType::MalformedVariableParameter,
700  message, location, true, variableName, "");
701  }
702 
703  void RaiseTypeError(const gd::String &message,
704  const ExpressionParserLocation &location,
705  bool isFatal = true) {
706  RaiseError(gd::ExpressionParserError::ErrorType::MismatchedType, message,
707  location, isFatal);
708  }
709 
710  void RaiseOperatorError(const gd::String &message,
711  const ExpressionParserLocation &location) {
712  RaiseError(gd::ExpressionParserError::ErrorType::InvalidOperator, message,
713  location);
714  }
715 
716  void ReadChildTypeFromVariable(gd::Variable::Type variableType) {
717  if (variableType == gd::Variable::Number) {
718  childType = Type::Number;
719  } else if (variableType == gd::Variable::String) {
720  childType = Type::String;
721  } else {
722  // Nothing - we don't know the precise type (this could be used as a string or as a number).
723  }
724  }
725 
726  static bool ShouldTypeBeRefined(Type type) {
727  return (type == Type::Unknown || type == Type::NumberOrString);
728  }
729 
730  static Type StringToType(const gd::String &type);
731  static const gd::String &TypeToString(Type type);
732  static const gd::String unknownTypeString;
733  static const gd::String numberTypeString;
734  static const gd::String stringTypeString;
735  static const gd::String numberOrStringTypeString;
736  static const gd::String variableTypeString;
737  static const gd::String legacyVariableTypeString;
738  static const gd::String objectTypeString;
739  static const gd::String identifierTypeString;
740  static const gd::String emptyTypeString;
741  // Used as the default for the `extraInfo_` constructor argument: a long-lived
742  // empty string, so that storing &extraInfo_ in currentParameterExtraInfo
743  // never dangles when no explicit extraInfo is provided.
744  static const gd::String emptyParameterExtraInfo;
745 
746  std::vector<ExpressionParserError*> fatalErrors;
747  std::vector<ExpressionParserError*> allErrors;
748  std::vector<ExpressionParserError*> deprecationWarnings;
749  std::vector<std::unique_ptr<ExpressionParserError>> supplementalErrors;
750  Type childType;
751  Type parentType;
753  gd::String rootObjectName;
754  bool forbidsUsageOfBracketsBecauseParentIsObject;
755  gd::String variableObjectName;
756  gd::ExpressionParserLocation variableObjectNameLocation;
757  const gd::Variable *parentVariable = nullptr;
758  size_t variableChildDepth = 0;
759  gd::String currentParameterExtraInfo;
760  bool isDirectChildOfVariableBracketAccessorNode = false;
761  const gd::Platform &platform;
762  const gd::ProjectScopedContainers &projectScopedContainers;
763 };
764 
765 } // namespace gd
766 
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
Base class for implementing a platform.
Definition: Platform.h:42
Holds references to variables, objects, properties and other containers.
Definition: ProjectScopedContainers.h:36
String represents an UTF8 encoded string.
Definition: String.h:33
static const gd::String & GetExpressionPrimitiveValueType(const gd::String &parameterType)
Return the expression type from the parameter type. Declinations of "number" and "string" types (like...
Definition: ValueTypeMetadata.cpp:42
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