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  // Ignore properties here.
227  // There is no support for "children" of properties.
228  },
229  [&]() {
230  // Ignore parameters here.
231  // There is no support for "children" of parameters.
232  },
233  [&]() {
234  // Ignore unrecognised identifiers here.
235  });
236  }
237  }
238 
239  private:
240  const gd::Variable* WalkUntilLastChild(
241  const gd::Variable& variable,
242  const std::vector<gd::String>& childVariableNames,
243  size_t startIndex = 0) {
244  if (bailOutBecauseEmptyVariableName)
245  return nullptr; // Do not even attempt to find the parent if we had an issue
246  // when visiting nodes.
247 
248  const gd::Variable* currentVariable = &variable;
249 
250  for (size_t index = startIndex; index < childVariableNames.size();
251  ++index) {
252  const gd::String& childName = childVariableNames[index];
253 
254  if (childName.empty()) {
255  if (currentVariable->GetChildrenCount() == 0) {
256  // The array or structure is empty, we can't walk through it - there
257  // is no "parent".
258  return nullptr;
259  }
260 
261  if (currentVariable->GetType() == gd::Variable::Array) {
262  currentVariable = &currentVariable->GetAtIndex(0);
263  } else {
264  currentVariable =
265  currentVariable->GetAllChildren().begin()->second.get();
266  }
267  } else {
268  if (!currentVariable->HasChild(childName)) {
269  // Non existing child - there is no "parent".
270  return nullptr;
271  }
272 
273  currentVariable = &currentVariable->GetChild(childName);
274  }
275  }
276 
277  // Return the last parent of the chain of variables (so not the last
278  // variable but the one before it).
279  return currentVariable;
280  }
281 
282  VariableAndItsParent WalkUntilLastParent(
283  const gd::Variable& variable,
284  const std::vector<gd::String>& childVariableNames,
285  size_t startIndex = 0) {
286  if (bailOutBecauseEmptyVariableName)
287  return {}; // Do not even attempt to find the parent if we had an issue
288  // when visiting nodes.
289 
290  const gd::Variable* currentVariable = &variable;
291 
292  // Walk until size - 1 as we want the last parent.
293  for (size_t index = startIndex; index + 1 < childVariableNames.size();
294  ++index) {
295  const gd::String& childName = childVariableNames[index];
296 
297  if (childName.empty()) {
298  if (currentVariable->GetChildrenCount() == 0) {
299  // The array or structure is empty, we can't walk through it - there
300  // is no "parent".
301  return {};
302  }
303 
304  if (currentVariable->GetType() == gd::Variable::Array) {
305  currentVariable = &currentVariable->GetAtIndex(0);
306  } else {
307  currentVariable =
308  currentVariable->GetAllChildren().begin()->second.get();
309  }
310  } else {
311  if (!currentVariable->HasChild(childName)) {
312  // Non existing child - there is no "parent".
313  return {};
314  }
315 
316  currentVariable = &currentVariable->GetChild(childName);
317  }
318  }
319 
320  // Return the last parent of the chain of variables (so not the last
321  // variable but the one before it).
322  return {.parentVariable = currentVariable};
323  }
324 
325  VariableAndItsParent WalkUntilLastParent(
326  const gd::VariablesContainer& variablesContainer,
327  const std::vector<gd::String>& childVariableNames) {
328  if (bailOutBecauseEmptyVariableName)
329  return {}; // Do not even attempt to find the parent if we had an issue
330  // when visiting nodes.
331 
332  if (variableName.empty())
333  return {}; // There is no "parent" to the variables container itself.
334 
335  const gd::Variable* variable = variablesContainer.Has(variableName)
336  ? &variablesContainer.Get(variableName)
337  : nullptr;
338  if (childVariableNames.empty() || !variable)
339  return {// No child: the parent is the variables container itself.
340  .parentVariablesContainer = &variablesContainer};
341 
342  return WalkUntilLastParent(*variable, childVariableNames, 0);
343  }
344 
345  const gd::Platform& platform;
346  const gd::ProjectScopedContainers& projectScopedContainers;
347  const gd::String& parameterType;
348  gd::String& objectName;
349  const gd::ExpressionNode* lastNodeToCheck;
350 
351  const gd::VariablesContainer* variablesContainer;
352  gd::String variableName;
353  std::vector<gd::String> childVariableNames;
354  bool bailOutBecauseEmptyVariableName;
355 };
356 
357 } // 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 & GetVariablesContainerFromVariableName(const gd::String &variableName) const
Definition: VariablesContainersList.cpp:77
const VariablesContainer * GetTopMostVariablesContainer() const
Avoid using apart when a scope must be forced.
Definition: VariablesContainersList.h:73
const VariablesContainer * GetBottomMostVariablesContainer() const
Avoid using apart when a scope must be forced.
Definition: VariablesContainersList.h:83
bool Has(const gd::String &name) const
Return true if the specified variable is in one of the containers.
Definition: VariablesContainersList.cpp:58
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