GDevelop Core
Core library for developing platforms and tools compatible with GDevelop.
ExpressionVariablePathFinder.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 
11 #include "GDCore/Events/Parsers/ExpressionParser2Node.h"
12 #include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
13 #include "GDCore/Project/ProjectScopedContainers.h"
14 #include "GDCore/Project/Variable.h"
15 #include "GDCore/Project/VariablesContainer.h"
16 
17 namespace gd {
18 class Platform;
19 } // namespace gd
20 
21 namespace gd {
22 
29  const gd::VariablesContainer* parentVariablesContainer;
30  const gd::Variable* parentVariable;
31 };
32 
40  public:
41 
42  static VariableAndItsParent GetLastParentOfNode(
43  const gd::Platform& platform,
44  const gd::ProjectScopedContainers& projectScopedContainers,
45  gd::ExpressionNode& node);
46 
47  static const gd::Variable::Type GetVariableType(
48  const gd::Platform& platform,
49  const gd::ProjectScopedContainers& projectScopedContainers,
50  gd::ExpressionNode& node, const gd::String& objectName) {
51  // The context is not checked because this is called on variable parameters.
52  gd::String parameterType = objectName.empty() ? "variable" : "objectvar";
53  gd::String objName = objectName;
55  platform, projectScopedContainers, parameterType, objName);
56  node.Visit(typeFinder);
57 
58  if (typeFinder.variableName.empty() || !typeFinder.variablesContainer) {
59  return gd::Variable::Unknown;
60  }
61  auto *variable = typeFinder.WalkUntilLastChild(
62  typeFinder.variablesContainer->Get(typeFinder.variableName),
63  typeFinder.childVariableNames);
64  return variable ? variable->GetType() : gd::Variable::Unknown;
65  }
66 
67  static const gd::Variable::Type GetArrayVariableType(
68  const gd::Platform& platform,
69  const gd::ProjectScopedContainers& projectScopedContainers,
70  gd::ExpressionNode& node, const gd::String& objectName) {
71  // The context is not checked because this is called on variable parameters.
72  gd::String parameterType = objectName.empty() ? "variable" : "objectvar";
73  gd::String objName = objectName;
75  platform, projectScopedContainers, parameterType, objName);
76  node.Visit(typeFinder);
77 
78  if (typeFinder.variableName.empty() || !typeFinder.variablesContainer) {
79  return gd::Variable::Unknown;
80  }
81  auto *variable = typeFinder.WalkUntilLastChild(
82  typeFinder.variablesContainer->Get(typeFinder.variableName),
83  typeFinder.childVariableNames);
84  if (variable && variable->GetType() != gd::Variable::Array) {
85  return gd::Variable::Unknown;
86  }
87  return variable && variable->GetChildrenCount() > 0
88  ? variable->GetAtIndex(0).GetType()
89  : gd::Variable::Unknown;
90  }
91 
92  virtual ~ExpressionVariablePathFinder(){};
93 
94  protected:
96  const gd::Platform& platform_,
97  const gd::ProjectScopedContainers& projectScopedContainers_,
98  const gd::String& parameterType_,
99  gd::String& objectName_,
100  const gd::ExpressionNode* lastNodeToCheck_ = nullptr)
101  : platform(platform_),
102  projectScopedContainers(projectScopedContainers_),
103  parameterType(parameterType_),
104  objectName(objectName_),
105  lastNodeToCheck(lastNodeToCheck_),
106  variablesContainer(nullptr),
107  variableName(""),
108  bailOutBecauseEmptyVariableName(false) {};
109 
110  void OnVisitVariableBracketAccessorNode(
111  VariableBracketAccessorNode& node) override;
112 
113  void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
114  void OnVisitOperatorNode(OperatorNode& node) override {}
115  void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
116  void OnVisitNumberNode(NumberNode& node) override {}
117  void OnVisitTextNode(TextNode& node) override {}
118  void OnVisitVariableNode(VariableNode& node) override {
119  FindVariableFor(node.name);
120  if (node.child && &node != lastNodeToCheck) node.child->Visit(*this);
121  }
122  void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
123  if (node.name.empty() && node.child) {
124  // A variable accessor should always have a name if it has a child (i.e:
125  // another accessor). While the parser may have generated an empty name,
126  // flag this so we avoid finding a wrong parent (and so, run the risk of
127  // giving wrong autocompletions).
128  bailOutBecauseEmptyVariableName = true;
129  }
130  if (variableName.empty()) {
131  const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
132  if (objectsContainersList.HasObjectOrGroupWithVariableNamed(objectName,
133  node.name) !=
134  gd::ObjectsContainersList::VariableExistence::DoesNotExist) {
135  variableName = node.name;
136  variablesContainer =
137  projectScopedContainers.GetObjectsContainersList()
139  }
140  } else {
141  childVariableNames.push_back(node.name);
142  }
143  if (node.child && &node != lastNodeToCheck) node.child->Visit(*this);
144  }
145  void OnVisitIdentifierNode(IdentifierNode& node) override {
146  FindVariableFor(node.identifierName, node.identifierNameDotLocation.IsValid() ? &node.childIdentifierName : nullptr);
147  }
148  void OnVisitEmptyNode(EmptyNode& node) override {}
149  void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
150  void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {}
151 
152  void FindVariableFor(const gd::String& identifier, gd::String* childIdentifier = nullptr) {
153  if (!objectName.empty()) {
154  const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
155  if (objectsContainersList.HasObjectOrGroupWithVariableNamed(objectName,
156  identifier) !=
157  gd::ObjectsContainersList::VariableExistence::DoesNotExist) {
158  variableName = identifier;
159  variablesContainer =
160  projectScopedContainers.GetObjectsContainersList()
162  if (childIdentifier) {
163  childVariableNames.push_back(*childIdentifier);
164  }
165  }
166  }
167  else if (parameterType == "scenevar") {
168  // The node represents a variable name, and the variables container
169  // containing it was identified in the FunctionCallNode.
170  variablesContainer = projectScopedContainers.GetVariablesContainersList()
172  variableName = identifier;
173  if (childIdentifier) {
174  childVariableNames.push_back(*childIdentifier);
175  }
176  }
177  else if (parameterType == "globalvar") {
178  // The node represents a variable name, and the variables container
179  // containing it was identified in the FunctionCallNode.
180  variablesContainer = projectScopedContainers.GetVariablesContainersList()
182  variableName = identifier;
183  if (childIdentifier) {
184  childVariableNames.push_back(*childIdentifier);
185  }
186  } else {
187  // Otherwise, the identifier is to be interpreted as usual:
188  // it can be an object (on which a variable is accessed),
189  // or a variable.
190  projectScopedContainers.MatchIdentifierWithName<void>(
191  identifier,
192  [&]() {
193  objectName = identifier;
194  if (childIdentifier) {
195  if (parameterType == "variable") {
196  // An object is overlapping the variable.
197  // Even in "variable" parameters, this is not allowed to be
198  // consistent with expressions.
199  } else {
200  // It's an object variable expression.
201  const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
202  if (objectsContainersList.HasObjectOrGroupWithVariableNamed(objectName,
203  *childIdentifier) !=
204  gd::ObjectsContainersList::VariableExistence::DoesNotExist) {
205  variableName = *childIdentifier;
206  variablesContainer =
207  projectScopedContainers.GetObjectsContainersList()
209  }
210  }
211  }
212  },
213  [&]() {
214  // This is a variable.
215  if (projectScopedContainers.GetVariablesContainersList().Has(identifier)) {
216  variablesContainer =
217  &(projectScopedContainers.GetVariablesContainersList()
219  variableName = identifier;
220  if (childIdentifier) {
221  childVariableNames.push_back(*childIdentifier);
222  }
223  }
224  },
225  [&]() {
226  // This is a property.
227  if (parameterType != "objectvar" &&
228  projectScopedContainers.GetVariablesContainersList().Has(
229  identifier)) {
230  variablesContainer =
231  &(projectScopedContainers.GetVariablesContainersList()
233  variableName = identifier;
234  // There is no support for "children" of properties.
235  }
236  },
237  [&]() {
238  // This is a parameter.
239  if (parameterType != "objectvar" &&
240  projectScopedContainers.GetVariablesContainersList().Has(
241  identifier)) {
242  variablesContainer =
243  &(projectScopedContainers.GetVariablesContainersList()
245  variableName = identifier;
246  // There is no support for "children" of parameters.
247  }
248  },
249  [&]() {
250  // Ignore unrecognised identifiers here.
251  });
252  }
253  }
254 
255  private:
256  const gd::Variable* WalkUntilLastChild(
257  const gd::Variable& variable,
258  const std::vector<gd::String>& childVariableNames,
259  size_t startIndex = 0) {
260  if (bailOutBecauseEmptyVariableName)
261  return nullptr; // Do not even attempt to find the parent if we had an issue
262  // when visiting nodes.
263 
264  const gd::Variable* currentVariable = &variable;
265 
266  for (size_t index = startIndex; index < childVariableNames.size();
267  ++index) {
268  const gd::String& childName = childVariableNames[index];
269 
270  if (childName.empty()) {
271  if (currentVariable->GetChildrenCount() == 0) {
272  // The array or structure is empty, we can't walk through it - there
273  // is no "parent".
274  return nullptr;
275  }
276 
277  if (currentVariable->GetType() == gd::Variable::Array) {
278  currentVariable = &currentVariable->GetAtIndex(0);
279  } else {
280  currentVariable =
281  currentVariable->GetAllChildren().begin()->second.get();
282  }
283  } else {
284  if (!currentVariable->HasChild(childName)) {
285  // Non existing child - there is no "parent".
286  return nullptr;
287  }
288 
289  currentVariable = &currentVariable->GetChild(childName);
290  }
291  }
292 
293  // Return the last parent of the chain of variables (so not the last
294  // variable but the one before it).
295  return currentVariable;
296  }
297 
298  VariableAndItsParent WalkUntilLastParent(
299  const gd::Variable& variable,
300  const std::vector<gd::String>& childVariableNames,
301  size_t startIndex = 0) {
302  if (bailOutBecauseEmptyVariableName)
303  return {}; // Do not even attempt to find the parent if we had an issue
304  // when visiting nodes.
305 
306  const gd::Variable* currentVariable = &variable;
307 
308  // Walk until size - 1 as we want the last parent.
309  for (size_t index = startIndex; index + 1 < childVariableNames.size();
310  ++index) {
311  const gd::String& childName = childVariableNames[index];
312 
313  if (childName.empty()) {
314  if (currentVariable->GetChildrenCount() == 0) {
315  // The array or structure is empty, we can't walk through it - there
316  // is no "parent".
317  return {};
318  }
319 
320  if (currentVariable->GetType() == gd::Variable::Array) {
321  currentVariable = &currentVariable->GetAtIndex(0);
322  } else {
323  currentVariable =
324  currentVariable->GetAllChildren().begin()->second.get();
325  }
326  } else {
327  if (!currentVariable->HasChild(childName)) {
328  // Non existing child - there is no "parent".
329  return {};
330  }
331 
332  currentVariable = &currentVariable->GetChild(childName);
333  }
334  }
335 
336  // Return the last parent of the chain of variables (so not the last
337  // variable but the one before it).
338  return {.parentVariable = currentVariable};
339  }
340 
341  VariableAndItsParent WalkUntilLastParent(
342  const gd::VariablesContainer& variablesContainer,
343  const std::vector<gd::String>& childVariableNames) {
344  if (bailOutBecauseEmptyVariableName)
345  return {}; // Do not even attempt to find the parent if we had an issue
346  // when visiting nodes.
347 
348  if (variableName.empty())
349  return {}; // There is no "parent" to the variables container itself.
350 
351  const gd::Variable* variable = variablesContainer.Has(variableName)
352  ? &variablesContainer.Get(variableName)
353  : nullptr;
354  if (childVariableNames.empty() || !variable)
355  return {// No child: the parent is the variables container itself.
356  .parentVariablesContainer = &variablesContainer};
357 
358  return WalkUntilLastParent(*variable, childVariableNames, 0);
359  }
360 
361  const gd::Platform& platform;
362  const gd::ProjectScopedContainers& projectScopedContainers;
363  const gd::String& parameterType;
364  gd::String& objectName;
365  const gd::ExpressionNode* lastNodeToCheck;
366 
367  const gd::VariablesContainer* variablesContainer;
368  gd::String variableName;
369  std::vector<gd::String> childVariableNames;
370  bool bailOutBecauseEmptyVariableName;
371 };
372 
373 } // namespace gd
The interface for any worker class ("visitor" pattern) that want to interact with the nodes of a pars...
Definition: ExpressionParser2NodeWorker.h:36
Find a variable path from an expression node.
Definition: ExpressionVariablePathFinder.h:39
const gd::VariablesContainer * GetObjectOrGroupVariablesContainer(const gd::String &objectOrGroupName) const
Return the container of the variables for the specified object or group of objects.
Definition: ObjectsContainersList.cpp:189
Base class for implementing a platform.
Definition: Platform.h:42
Holds references to variables, objects, properties and other containers.
Definition: ProjectScopedContainers.h:34
String represents an UTF8 encoded string.
Definition: String.h:33
void push_back(value_type character)
Add a character (from its codepoint) at the end of the String.
Definition: String.cpp:223
bool empty() const
Returns true if the string is empty.
Definition: String.h:157
Defines a variable which can be used by an object, a layout or a project.
Definition: Variable.h:29
Type
Definition: Variable.h:32
size_t GetChildrenCount() const
Get the count of children that the variable has.
Definition: Variable.h:211
const std::map< gd::String, std::shared_ptr< Variable > > & GetAllChildren() const
Get the map containing all the children.
Definition: Variable.h:268
bool HasChild(const gd::String &name) const
Return true if the variable is a structure and has the specified child.
Definition: Variable.cpp:138
Variable & GetChild(const gd::String &name)
Return the child with the specified name.
Definition: Variable.cpp:148
Type GetType() const
Get the type of the variable.
Definition: Variable.h:64
Variable & GetAtIndex(const size_t index)
Return the element with the specified index.
Definition: Variable.cpp:189
Class defining a container for gd::Variable.
Definition: VariablesContainer.h:28
bool Has(const gd::String &name) const
Return true if the specified variable is in the container.
Definition: VariablesContainer.cpp:45
Variable & Get(const gd::String &name)
Return a reference to the variable called name.
Definition: VariablesContainer.cpp:51
const VariablesContainer * GetTopMostVariablesContainer() const
Avoid using apart when a scope must be forced.
Definition: VariablesContainersList.h:100
const VariablesContainer * GetBottomMostVariablesContainer() const
Avoid using apart when a scope must be forced.
Definition: VariablesContainersList.h:111
const VariablesContainer & GetVariablesContainerFromVariableOrPropertyOrParameterName(const gd::String &variableName) const
Definition: VariablesContainersList.cpp:150
bool Has(const gd::String &name) const
Return true if the specified variable is in one of the containers.
Definition: VariablesContainersList.cpp:131
Definition: CommonTools.h:24
An empty node, used when parsing failed/a syntax error was encountered and any other node could not m...
Definition: ExpressionParser2Node.h:423
The base node, from which all nodes in the tree of an expression inherits from.
Definition: ExpressionParser2Node.h:100
A function call node (either free function, object function or object behavior function)....
Definition: ExpressionParser2Node.h:371
An identifier node, usually representing an object or a variable with an optional function name or ch...
Definition: ExpressionParser2Node.h:204
ExpressionParserLocation identifierNameDotLocation
Location of the "." after the object or variable name.
Definition: ExpressionParser2Node.h:228
gd::String childIdentifierName
The object function or variable child name.
Definition: ExpressionParser2Node.h:222
gd::String identifierName
The object or variable name.
Definition: ExpressionParser2Node.h:219
A number node. For example: "123". Its type is always "number".
Definition: ExpressionParser2Node.h:161
The name of a function to call on an object or the behavior For example: "MyObject....
Definition: ExpressionParser2Node.h:321
An operator node. For example: "lhs + rhs".
Definition: ExpressionParser2Node.h:129
Definition: ExpressionParser2Node.h:115
A text node. For example: "Hello World". Its type is always "string".
Definition: ExpressionParser2Node.h:177
A unary operator node. For example: "-2".
Definition: ExpressionParser2Node.h:145
A direct accessor to a child variable. Example: MyChild in MyVariable.MyChild.
Definition: ExpressionParser2Node.h:282
Contains a variables container or a variable. Useful to refer to the parent of a variable (which can ...
Definition: ExpressionVariablePathFinder.h:28
A bracket accessor to a child variable. Example: ["MyChild"] (in MyVariable["MyChild"]).
Definition: ExpressionParser2Node.h:300
A variable, or object variable, with bracket accessor or at least 2 "dot" accessors.
Definition: ExpressionParser2Node.h:261