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/Extensions/Metadata/ExpressionMetadata.h"
15 #include "GDCore/Project/ProjectScopedContainers.h"
16 #include "GDCore/Project/VariablesContainersList.h"
17 #include "GDCore/Project/VariablesContainer.h"
18 
19 namespace gd {
20 class Expression;
21 class ObjectsContainer;
22 class VariablesContainer;
23 class Platform;
24 class ParameterMetadata;
25 class ExpressionMetadata;
26 class VariablesContainersList;
27 class ProjectScopedContainers;
28 } // namespace gd
29 
30 namespace gd {
31 
39  public:
40  ExpressionValidator(const gd::Platform &platform_,
41  const gd::ProjectScopedContainers & projectScopedContainers_,
42  const gd::String &rootType_,
43  const gd::String &rootObjectName_ = emptyParameterExtraInfo,
44  const gd::String &extraInfo_ = emptyParameterExtraInfo)
45  : platform(platform_),
46  projectScopedContainers(projectScopedContainers_),
47  parentType(StringToType(gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(rootType_))),
48  rootObjectName(rootObjectName_),
49  childType(Type::Unknown),
50  forbidsUsageOfBracketsBecauseParentIsObject(false),
51  currentParameterExtraInfo(&extraInfo_),
52  variableObjectName(),
53  variableObjectNameLocation() {};
54  virtual ~ExpressionValidator(){};
55 
60  static bool HasNoErrors(const gd::Platform &platform,
61  const gd::ProjectScopedContainers & projectScopedContainers,
62  const gd::String &rootType,
63  gd::ExpressionNode& node) {
64  gd::ExpressionValidator validator(platform, projectScopedContainers, rootType);
65  node.Visit(validator);
66  return validator.GetAllErrors().empty();
67  }
68 
74  const std::vector<ExpressionParserError*>& GetFatalErrors() {
75  return fatalErrors;
76  };
77 
83  const std::vector<ExpressionParserError*>& GetAllErrors() {
84  return allErrors;
85  };
86 
90  const std::vector<ExpressionParserError*> &
92  return deprecationWarnings;
93  };
94 
95  protected:
96  void OnVisitSubExpressionNode(SubExpressionNode& node) override {
97  ReportAnyError(node);
98  node.expression->Visit(*this);
99  }
100  void OnVisitOperatorNode(OperatorNode& node) override {
101  ReportAnyError(node);
102 
103  // The "required" type ("parentType") will be used when visiting the first operand.
104  // Note that it may be refined thanks to this first operand (see later).
105  node.leftHandSide->Visit(*this);
106  const Type leftType = childType; // Store the type of the first operand.
107 
108  if (parentType == Type::Variable || parentType == Type::ObjectVariable ||
109  parentType == Type::LegacyVariable) {
110  RaiseOperatorError(
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);
119  }
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);
125  }
126  else if (node.op != '+') {
127  RaiseOperatorError(
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()));
131  }
132  } else if (leftType == Type::Object) {
133  RaiseOperatorError(
134  _("Operators (+, -, /, *) can't be used with an object name. Remove "
135  "the operator."),
136  node.rightHandSide->location);
137  }
138 
139  // The "required" type ("parentType") of the second operator is decided by:
140  // - the parent type. Unless it can (`number|string`) or should (`unknown`) be refined, then:
141  // - the first operand.
142  parentType = ShouldTypeBeRefined(parentType) ? leftType : parentType;
143  node.rightHandSide->Visit(*this);
144  const Type rightType = childType;
145 
146  // The type of the overall operator ("childType") is decided by:
147  // - the parent type. Unless it can (`number|string`) or should (`unknown`) be refined, then:
148  // - the first operand. Unless it can (`number|string`) or should (`unknown`) be refined, then:
149  // - the right operand (which got visited knowing the type of the first operand, so it's
150  // equal or strictly more precise than the left operand).
151  childType = ShouldTypeBeRefined(parentType) ? (ShouldTypeBeRefined(leftType) ? leftType : rightType) : parentType;
152  }
153  void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
154  ReportAnyError(node);
155  node.factor->Visit(*this);
156  const Type rightType = childType;
157 
158  if (parentType == Type::Variable || parentType == Type::ObjectVariable ||
159  parentType == Type::LegacyVariable) {
160  RaiseTypeError(
161  _("Operators (+, -) can't be used in variable names. Remove "
162  "the operator from the variable name."),
163  node.location);
164  } else if (rightType == Type::Number) {
165  if (node.op != '+' && node.op != '-') {
166  // This is actually a dead code because the parser takes them as
167  // binary operations with an empty left side which makes as much sense.
168  RaiseTypeError(
169  _("You've used an \"unary\" operator that is not supported. Operator "
170  "should be "
171  "either + or -."),
172  node.location);
173  }
174  } else if (rightType == Type::String) {
175  RaiseTypeError(
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 "
178  "expressions)."),
179  node.location);
180  } else if (rightType == Type::Object) {
181  RaiseTypeError(
182  _("Operators (+, -) can't be used with an object name. Remove the "
183  "operator."),
184  node.location);
185  }
186  }
187  void OnVisitNumberNode(NumberNode& node) override {
188  ReportAnyError(node);
189  childType = Type::Number;
190  if (parentType == Type::String) {
191  RaiseTypeError(
192  _("You entered a number, but a text was expected (in quotes)."),
193  node.location);
194  } else if (parentType != Type::Number &&
195  parentType != Type::NumberOrString) {
196  RaiseTypeError(_("You entered a number, but this type was expected:") +
197  " " + TypeToString(parentType),
198  node.location);
199  }
200  }
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."),
206  node.location);
207  } else if (parentType != Type::String &&
208  parentType != Type::NumberOrString) {
209  RaiseTypeError(_("You entered a text, but this type was expected:") +
210  " " + TypeToString(parentType),
211  node.location);
212  }
213  }
214  void OnVisitVariableNode(VariableNode& node) override {
215  ReportAnyError(node);
216  parentVariable = nullptr;
217  variableChildDepth = 0;
218 
219  if (parentType == Type::Variable ||
220  parentType == Type::VariableOrProperty ||
221  parentType == Type::VariableOrPropertyOrParameter) {
222  childType = parentType;
223 
224  bool isRootVariableDeclared = CheckVariableExistence(
225  node.location, node.name, node.child != nullptr);
226  if (node.child) {
227  if (isRootVariableDeclared) {
228  const auto &variable =
229  projectScopedContainers.GetVariablesContainersList().Get(
230  node.name);
231  parentVariable = &variable;
232  }
233  node.child->Visit(*this);
234  }
235  } else if (parentType == Type::ObjectVariable) {
236  childType = parentType;
237 
238  if (!rootObjectName.empty()) {
239  ValidateObjectVariableOrVariableOrProperty(
240  rootObjectName, node.nameLocation, node.name, node.nameLocation,
241  false, !!node.child);
242 
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)
252  ->Get(node.name);
253  if (node.child) {
254  parentVariable = &objectVariable;
255  } else {
256  ValidateLastChildVariable(objectVariable, node.nameLocation);
257  }
258  }
259  rootObjectName = "";
260  }
261  if (node.child) {
262  node.child->Visit(*this);
263  }
264  } else if (parentType == Type::LegacyVariable) {
265  childType = parentType;
266 
267  if (node.child) {
268  node.child->Visit(*this);
269  }
270  } else if (parentType == Type::String || parentType == Type::Number ||
271  parentType == Type::NumberOrString) {
272  // The node represents a variable or an object variable in an expression waiting for its *value* to be returned.
273  childType = parentType;
274 
275  const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
276  const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
277  const auto& propertiesContainerList = projectScopedContainers.GetPropertiesContainersList();
278 
279  forbidsUsageOfBracketsBecauseParentIsObject = false;
280  projectScopedContainers.MatchIdentifierWithName<void>(node.name,
281  [&]() {
282  // This represents an object.
283  variableObjectName = node.name;
284  variableObjectNameLocation = node.nameLocation;
285  // While understood by the parser, it's forbidden to use the bracket notation just after
286  // an object name (`MyObject["MyVariable"]`).
287  forbidsUsageOfBracketsBecauseParentIsObject = true;
288  }, [&]() {
289  // This is a variable.
290  const auto &variable =
291  projectScopedContainers.GetVariablesContainersList().Get(
292  node.name);
293  if (node.child) {
294  parentVariable = &variable;
295  } else {
296  ValidateLastChildVariable(variable, node.location);
297  }
298  }, [&]() {
299  // This is a property.
300  // Being in this node implies that there is at least a child - which is not supported for properties.
301  RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
302  node.location);
303  }, [&]() {
304  // This is a parameter.
305  // Being in this node implies that there is at least a child - which is not supported for parameters.
306  RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
307  node.location);
308  }, [&]() {
309  // This is something else.
310  RaiseTypeError(_("No object, variable or property with this name found."),
311  node.location);
312  });
313 
314  if (node.child) {
315  node.child->Visit(*this);
316  }
317 
318  forbidsUsageOfBracketsBecauseParentIsObject = false;
319  } else {
320  RaiseTypeError(_("You entered a variable, but this type was expected:") +
321  " " + TypeToString(parentType),
322  node.location);
323 
324  if (node.child) {
325  node.child->Visit(*this);
326  }
327  }
328  }
329  void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
330  ReportAnyError(node);
331  // TODO Also check child-variables existence on a path with only VariableAccessor to raise non-fatal errors.
332  if (!variableObjectName.empty()) {
333  ValidateObjectVariableOrVariableOrProperty(
334  variableObjectName, variableObjectNameLocation, node.name,
335  node.nameLocation, true, !!node.child);
336 
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)
346  ->Get(node.name);
347  if (node.child) {
348  parentVariable = &objectVariable;
349  } else {
350  parentVariable = nullptr;
351  }
352  } else {
353  parentVariable = nullptr;
354  }
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);
362  if (node.child) {
363  parentVariable = &childVariable;
364  variableChildDepth++;
365  } else {
366  ValidateLastChildVariable(childVariable, node.nameLocation);
367  parentVariable = nullptr;
368  variableChildDepth = 0;
369  }
370  }
371  else {
372  parentVariable = nullptr;
373  variableChildDepth = 0;
374  }
375  }
376  // In the case we accessed an object variable (`MyObject.MyVariable`),
377  // brackets can now be used (`MyObject.MyVariable["MyChildVariable"]` is now valid).
378  forbidsUsageOfBracketsBecauseParentIsObject = false;
379 
380  if (node.child) {
381  node.child->Visit(*this);
382  }
383  }
384  void OnVisitVariableBracketAccessorNode(
385  VariableBracketAccessorNode& node) override {
386  ReportAnyError(node);
387 
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`."),
396  node.location);
397  }
398  forbidsUsageOfBracketsBecauseParentIsObject = false;
399 
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;
409 
410  if (node.child) {
411  node.child->Visit(*this);
412  }
413  }
414  void OnVisitIdentifierNode(IdentifierNode& node) override {
415  ReportAnyError(node);
416  if (parentType == Type::String) {
417  if (!ValidateObjectVariableOrVariableOrProperty(node)) {
418  // The identifier is not a variable, so either the variable is not properly declared
419  // or it's a text without quotes.
420  RaiseUnknownIdentifierError(_("You must wrap your text inside double quotes "
421  "(example: \"Hello world\")."),
422  node.location);
423  }
424  }
425  else if (parentType == Type::Number) {
426  if (!ValidateObjectVariableOrVariableOrProperty(node)) {
427  // The identifier is not a variable, so the variable is not properly declared.
428  RaiseUnknownIdentifierError(_("You must enter a number."), node.location);
429  }
430  }
431  else if (parentType == Type::NumberOrString) {
432  if (!ValidateObjectVariableOrVariableOrProperty(node)) {
433  // The identifier is not a variable, so either the variable is not properly declared
434  // or it's a text without quotes.
435  RaiseUnknownIdentifierError(
436  _("You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
437  node.location);
438  }
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);
449  }
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());
457 
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);
477  }
478  }
479  }
480  rootObjectName = "";
481  }
482  } else if (parentType != Type::Object &&
483  parentType != Type::LegacyVariable) {
484  // It can't happen.
485  RaiseTypeError(
486  _("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
487  node.location);
488  childType = parentType;
489  } else {
490  childType = parentType;
491  }
492  }
493  void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
494  ReportAnyError(node);
495  }
496  void OnVisitFunctionCallNode(FunctionCallNode& node) override {
497  childType = ValidateFunction(node);
498  }
499  void OnVisitEmptyNode(EmptyNode& node) override {
500  ReportAnyError(node);
501  gd::String message;
502  if (parentType == Type::Number) {
503  message = _("You must enter a number or a valid expression call.");
504  } else if (parentType == Type::String) {
505  message = _(
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.");
513  } else {
514  // It can't happen.
515  message = _("You must enter a valid expression.");
516  }
517  RaiseTypeError(message, node.location);
518  childType = Type::Empty;
519  }
520 
521 private:
522  enum Type {
523  Unknown = 0,
524  Number,
525  String,
526  NumberOrString,
527  Variable,
528  ObjectVariable,
529  LegacyVariable,
530  Object,
531  Empty,
532  VariableOrProperty,
533  VariableOrPropertyOrParameter
534  };
535  Type ValidateFunction(const gd::FunctionCallNode& function);
536  bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier);
537  bool ValidateObjectVariableOrVariableOrProperty(
538  const gd::String &identifierName,
539  const gd::ExpressionParserLocation identifierNameLocation,
540  const gd::String &childIdentifierName,
541  const gd::ExpressionParserLocation childIdentifierNameLocation,
542  const bool isUndeclaredVariableFatal,
543  const bool hasMoreChildren = false);
544  bool ValidateChildVariable(
545  const gd::Variable &parentVariable, const gd::String &childVariableName,
546  const gd::ExpressionParserLocation childNameLocation,
547  const bool isUndeclaredVariableFatal);
548  void ValidateLastChildVariable(
549  const gd::Variable &lastChildVariable,
550  const gd::ExpressionParserLocation childNameLocation);
551 
552  bool CheckVariableExistence(const ExpressionParserLocation &location,
553  const gd::String &name, bool hasChild) {
554  if (!currentParameterExtraInfo ||
555  *currentParameterExtraInfo != "AllowUndeclaredVariable") {
556  bool isRootVariableDeclared = false;
557  projectScopedContainers.MatchIdentifierWithName<void>(
558  name,
559  [&]() {
560  // This represents an object.
561  RaiseVariableNameCollisionError(
562  _("This variable has the same name as an object. Consider "
563  "renaming one or the other."),
564  location, name);
565  },
566  [&]() {
567  // This is a variable.
568  isRootVariableDeclared = true;
569  },
570  [&]() {
571  // This is a property.
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."),
577  location, name);
578  } else if (hasChild) {
579  RaiseMalformedVariableParameter(
580  _("Properties can't have children."), location, name);
581  }
582  },
583  [&]() {
584  // This is a parameter.
585  if (parentType != Type::VariableOrPropertyOrParameter) {
586  RaiseVariableNameCollisionError(
587  _("This variable has the same name as a parameter. Consider "
588  "renaming one or the other."),
589  location, name);
590  } else if (hasChild) {
591  RaiseMalformedVariableParameter(
592  _("Properties can't have children."), location, name);
593  }
594  },
595  [&]() {
596  // This is something else.
597  RaiseUndeclaredVariableError(
598  _("No variable with this name found."), location,
599  name);
600  });
601  return isRootVariableDeclared;
602  }
603  return false;
604  }
605 
606  void ReportAnyError(const ExpressionNode& node, bool isFatal = true) {
607  if (node.diagnostic) {
608  // Syntax errors are holden by the AST nodes.
609  // It's fine to give pointers on them as the AST live longer than errors
610  // handling.
611  allErrors.push_back(node.diagnostic.get());
612  if (isFatal) {
613  fatalErrors.push_back(node.diagnostic.get());
614  }
615  }
616  }
617 
618  void RaiseError(gd::ExpressionParserError::ErrorType type,
619  const gd::String &message,
620  const ExpressionParserLocation &location, bool isFatal = true,
621  const gd::String &actualValue = "",
622  const gd::String &objectName = "") {
623  auto diagnostic = gd::make_unique<ExpressionParserError>(
624  type, message, location, actualValue, objectName);
625  allErrors.push_back(diagnostic.get());
626  if (isFatal) {
627  fatalErrors.push_back(diagnostic.get());
628  }
629  // Errors found by the validator are not holden by the AST nodes.
630  // They must be owned by the validator to keep living while errors are
631  // handled by the caller.
632  supplementalErrors.push_back(std::move(diagnostic));
633  }
634 
635  void RaiseUnknownIdentifierError(const gd::String &message,
636  const ExpressionParserLocation &location) {
637  RaiseError(gd::ExpressionParserError::ErrorType::UnknownIdentifier, message,
638  location);
639  }
640 
641  void RaiseUndeclaredVariableError(const gd::String &message,
642  const ExpressionParserLocation &location,
643  const gd::String &variableName,
644  const gd::String &objectName = "",
645  const bool isUndeclaredVariableFatal = true) {
646  RaiseError(gd::ExpressionParserError::ErrorType::UndeclaredVariable,
647  message, location, isUndeclaredVariableFatal, variableName, objectName);
648  }
649 
650  void RaiseVariableNameCollisionError(const gd::String &message,
651  const ExpressionParserLocation &location,
652  const gd::String &variableName,
653  const gd::String &objectName = "") {
654  RaiseError(gd::ExpressionParserError::ErrorType::VariableNameCollision,
655  message, location, false, variableName, objectName);
656  }
657 
658  void RaiseMalformedVariableParameter(const gd::String &message,
659  const ExpressionParserLocation &location,
660  const gd::String &variableName) {
661  RaiseError(gd::ExpressionParserError::ErrorType::MalformedVariableParameter,
662  message, location, true, variableName, "");
663  }
664 
665  void RaiseTypeError(const gd::String &message,
666  const ExpressionParserLocation &location,
667  bool isFatal = true) {
668  RaiseError(gd::ExpressionParserError::ErrorType::MismatchedType, message,
669  location, isFatal);
670  }
671 
672  void RaiseOperatorError(const gd::String &message,
673  const ExpressionParserLocation &location) {
674  RaiseError(gd::ExpressionParserError::ErrorType::InvalidOperator, message,
675  location);
676  }
677 
678  void ReadChildTypeFromVariable(gd::Variable::Type variableType) {
679  if (variableType == gd::Variable::Number) {
680  childType = Type::Number;
681  } else if (variableType == gd::Variable::String) {
682  childType = Type::String;
683  } else {
684  // Nothing - we don't know the precise type (this could be used as a string or as a number).
685  }
686  }
687 
688  static bool ShouldTypeBeRefined(Type type) {
689  return (type == Type::Unknown || type == Type::NumberOrString);
690  }
691 
692  static Type StringToType(const gd::String &type);
693  static const gd::String &TypeToString(Type type);
694  static const gd::String unknownTypeString;
695  static const gd::String numberTypeString;
696  static const gd::String stringTypeString;
697  static const gd::String numberOrStringTypeString;
698  static const gd::String variableTypeString;
699  static const gd::String legacyVariableTypeString;
700  static const gd::String objectTypeString;
701  static const gd::String identifierTypeString;
702  static const gd::String emptyTypeString;
703  // Used as the default for the `extraInfo_` constructor argument: a long-lived
704  // empty string, so that storing &extraInfo_ in currentParameterExtraInfo
705  // never dangles when no explicit extraInfo is provided.
706  static const gd::String emptyParameterExtraInfo;
707 
708  std::vector<ExpressionParserError*> fatalErrors;
709  std::vector<ExpressionParserError*> allErrors;
710  std::vector<ExpressionParserError*> deprecationWarnings;
711  std::vector<std::unique_ptr<ExpressionParserError>> supplementalErrors;
712  Type childType;
713  Type parentType;
715  gd::String rootObjectName;
716  bool forbidsUsageOfBracketsBecauseParentIsObject;
717  gd::String variableObjectName;
718  gd::ExpressionParserLocation variableObjectNameLocation;
719  const gd::Variable *parentVariable = nullptr;
720  size_t variableChildDepth = 0;
721  const gd::String *currentParameterExtraInfo;
722  const gd::Platform &platform;
723  const gd::ProjectScopedContainers &projectScopedContainers;
724 };
725 
726 } // namespace gd
727 
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
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