GDevelop Core
Core library for developing platforms and tools compatible with GDevelop.
ExpressionVariableParentFinder.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/Extensions/Metadata/ExpressionMetadata.h"
14 #include "GDCore/Extensions/Metadata/MetadataProvider.h"
15 #include "GDCore/Extensions/Metadata/ObjectMetadata.h"
16 #include "GDCore/Extensions/Metadata/ParameterMetadata.h"
17 #include "GDCore/Project/ProjectScopedContainers.h"
18 #include "GDCore/Project/Variable.h"
20 
21 namespace gd {
22 class Expression;
23 class ObjectsContainer;
24 class Platform;
25 class ParameterMetadata;
26 class ExpressionMetadata;
27 } // namespace gd
28 
29 namespace gd {
30 
37  const gd::VariablesContainer* parentVariablesContainer;
38  const gd::Variable* parentVariable;
39 };
40 
51  public:
52  static VariableAndItsParent GetLastParentOfNode(
53  const gd::Platform& platform,
54  const gd::ProjectScopedContainers& projectScopedContainers,
55  gd::ExpressionNode& node) {
56  gd::ExpressionVariableParentFinder typeFinder(platform,
57  projectScopedContainers);
58  node.Visit(typeFinder);
59  return typeFinder.variableAndItsParent;
60  }
61 
62  virtual ~ExpressionVariableParentFinder(){};
63 
64  protected:
66  const gd::Platform& platform_,
67  const gd::ProjectScopedContainers& projectScopedContainers_)
68  : platform(platform_),
69  projectScopedContainers(projectScopedContainers_),
70  variableNode(nullptr),
71  thisIsALegacyPrescopedVariable(false),
72  bailOutBecauseEmptyVariableName(false),
73  legacyPrescopedVariablesContainer(nullptr),
74  variableAndItsParent{} {};
75 
76  void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
77  void OnVisitOperatorNode(OperatorNode& node) override {}
78  void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
79  void OnVisitNumberNode(NumberNode& node) override {}
80  void OnVisitTextNode(TextNode& node) override {}
81  void OnVisitVariableNode(VariableNode& node) override {
82  if (variableNode != nullptr) {
83  // This is not possible
84  return;
85  }
86  variableNode = &node;
87 
88  // Check if the parent is a function call, in which we might be dealing
89  // with a legacy pre-scoped variable parameter:
90  if (node.parent) node.parent->Visit(*this);
91 
92  if (thisIsALegacyPrescopedVariable) {
93  // The node represents a variable name, and the variables container
94  // containing it was identified in the FunctionCallNode.
95  childVariableNames.insert(childVariableNames.begin(), node.name);
96  if (legacyPrescopedVariablesContainer)
97  variableAndItsParent = WalkUntilLastParent(
98  *legacyPrescopedVariablesContainer, childVariableNames);
99  } else {
100  // Otherwise, the identifier is to be interpreted as usual:
101  // it can be an object (on which a variable is accessed),
102  // or a variable.
103  projectScopedContainers.MatchIdentifierWithName<void>(
104  node.name,
105  [&]() {
106  // This is an object.
107  const auto* variablesContainer =
108  projectScopedContainers.GetObjectsContainersList()
110  if (variablesContainer)
111  variableAndItsParent =
112  WalkUntilLastParent(*variablesContainer, childVariableNames);
113  },
114  [&]() {
115  // This is a variable.
116  if (projectScopedContainers.GetVariablesContainersList().Has(
117  node.name)) {
118  variableAndItsParent = WalkUntilLastParent(
119  projectScopedContainers.GetVariablesContainersList().Get(
120  node.name),
121  childVariableNames);
122  }
123  },
124  [&]() {
125  // Ignore properties here.
126  // There is no support for "children" of properties.
127  },
128  [&]() {
129  // Ignore parameters here.
130  // There is no support for "children" of parameters.
131  },
132  [&]() {
133  // Ignore unrecognised identifiers here.
134  });
135  }
136  }
137  void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
138  if (node.name.empty() && node.child) {
139  // A variable accessor should always have a name if it has a child (i.e:
140  // another accessor). While the parser may have generated an empty name,
141  // flag this so we avoid finding a wrong parent (and so, run the risk of
142  // giving wrong autocompletions).
143  bailOutBecauseEmptyVariableName = true;
144  }
145  childVariableNames.insert(childVariableNames.begin(), node.name);
146  if (node.parent) node.parent->Visit(*this);
147  }
148  void OnVisitIdentifierNode(IdentifierNode& node) override {
149  if (variableNode != nullptr) {
150  // This is not possible
151  return;
152  }
153  // This node is not necessarily a variable node.
154  // It will be checked when visiting the FunctionCallNode, just after.
155  variableNode = &node;
156 
157  // Check if the parent is a function call, in which we might be dealing
158  // with a legacy pre-scoped variable parameter:
159  if (node.parent) node.parent->Visit(*this);
160 
161  if (thisIsALegacyPrescopedVariable) {
162  // The identifier represents a variable name, and the variables container
163  // containing it was identified in the FunctionCallNode.
164  if (!node.childIdentifierName.empty())
165  childVariableNames.insert(childVariableNames.begin(),
166  node.childIdentifierName);
167  childVariableNames.insert(childVariableNames.begin(),
168  node.identifierName);
169 
170  if (legacyPrescopedVariablesContainer)
171  variableAndItsParent = WalkUntilLastParent(
172  *legacyPrescopedVariablesContainer, childVariableNames);
173 
174  } else {
175  // Otherwise, the identifier is to be interpreted as usual:
176  // it can be an object (on which a variable is accessed),
177  // or a variable.
178  projectScopedContainers.MatchIdentifierWithName<void>(
179  node.identifierName,
180  [&]() {
181  // This is an object.
182  if (!node.childIdentifierName.empty())
183  childVariableNames.insert(childVariableNames.begin(),
184  node.childIdentifierName);
185 
186  const auto* variablesContainer =
187  projectScopedContainers.GetObjectsContainersList()
189  if (variablesContainer)
190  variableAndItsParent =
191  WalkUntilLastParent(*variablesContainer, childVariableNames);
192  },
193  [&]() {
194  // This is a variable.
195  if (!node.childIdentifierName.empty())
196  childVariableNames.insert(childVariableNames.begin(),
197  node.childIdentifierName);
198 
199  if (projectScopedContainers.GetVariablesContainersList().Has(
200  node.identifierName)) {
201  variableAndItsParent = WalkUntilLastParent(
202  projectScopedContainers.GetVariablesContainersList().Get(
203  node.identifierName),
204  childVariableNames);
205  }
206  },
207  [&]() {
208  // Ignore properties here.
209  // There is no support for "children" of properties.
210  },
211  [&]() {
212  // Ignore parameters here.
213  // There is no support for "children" of properties.
214  },
215  [&]() {
216  // Ignore unrecognised identifiers here.
217  });
218  }
219  }
220  void OnVisitEmptyNode(EmptyNode& node) override {}
221  void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
222  void OnVisitVariableBracketAccessorNode(
223  VariableBracketAccessorNode& node) override {
224  // Add a child with an empty name, which will be interpreted as
225  // "take the first child/item of the structure/array".
226  childVariableNames.insert(childVariableNames.begin(), "");
227  if (node.parent) node.parent->Visit(*this);
228  }
229  void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {
230  if (variableNode == nullptr) {
231  return;
232  }
233  int parameterIndex = -1;
234  for (int i = 0; i < functionCall.parameters.size(); i++) {
235  if (functionCall.parameters.at(i).get() == variableNode) {
236  parameterIndex = i;
237  break;
238  }
239  }
240  if (parameterIndex < 0) {
241  return;
242  }
243 
244  const auto& objectsContainersList =
245  projectScopedContainers.GetObjectsContainersList();
246 
247  const gd::ParameterMetadata* parameterMetadata =
248  MetadataProvider::GetFunctionCallParameterMetadata(
249  platform, objectsContainersList, functionCall, parameterIndex);
250  if (parameterMetadata == nullptr) return; // Unexpected
251 
252  // Support for legacy pre-scoped variables:
253  if (parameterMetadata->GetValueTypeMetadata().IsLegacyPreScopedVariable()) {
254  if (parameterMetadata->GetType() == "objectvar") {
255  // Legacy convention where a "objectvar"
256  // parameter represents a variable of the object represented by the
257  // previous "object" parameter. The object on which the function is
258  // called is returned if no previous parameters are objects.
259  gd::String objectName = functionCall.objectName;
260  for (int previousIndex = parameterIndex - 1; previousIndex >= 0;
261  previousIndex--) {
262  const gd::ParameterMetadata* previousParameterMetadata =
263  MetadataProvider::GetFunctionCallParameterMetadata(
264  platform, objectsContainersList, functionCall, previousIndex);
265  if (previousParameterMetadata != nullptr &&
267  previousParameterMetadata->GetType())) {
268  auto previousParameterNode =
269  functionCall.parameters[previousIndex].get();
270  IdentifierNode* objectNode =
271  dynamic_cast<IdentifierNode*>(previousParameterNode);
272  objectName = objectNode->identifierName;
273  break;
274  }
275  }
276 
277  legacyPrescopedVariablesContainer =
278  projectScopedContainers.GetObjectsContainersList()
280  thisIsALegacyPrescopedVariable = true;
281  } else if (parameterMetadata->GetType() == "scenevar") {
282  legacyPrescopedVariablesContainer =
283  projectScopedContainers.GetVariablesContainersList()
285  thisIsALegacyPrescopedVariable = true;
286  } else if (parameterMetadata->GetType() == "globalvar") {
287  legacyPrescopedVariablesContainer =
288  projectScopedContainers.GetVariablesContainersList()
290  thisIsALegacyPrescopedVariable = true;
291  }
292  } else {
293  thisIsALegacyPrescopedVariable = false;
294  legacyPrescopedVariablesContainer = nullptr;
295  }
296  }
297 
298  private:
299  VariableAndItsParent WalkUntilLastParent(
300  const gd::Variable& variable,
301  const std::vector<gd::String>& childVariableNames,
302  size_t startIndex = 0) {
303  if (bailOutBecauseEmptyVariableName)
304  return {}; // Do not even attempt to find the parent if we had an issue
305  // when visiting nodes.
306 
307  const gd::Variable* currentVariable = &variable;
308 
309  // Walk until size - 1 as we want the last parent.
310  for (size_t index = startIndex; index + 1 < childVariableNames.size();
311  ++index) {
312  const gd::String& childName = childVariableNames[index];
313 
314  if (childName.empty()) {
315  if (currentVariable->GetChildrenCount() == 0) {
316  // The array or structure is empty, we can't walk through it - there
317  // is no "parent".
318  return {};
319  }
320 
321  if (currentVariable->GetType() == gd::Variable::Array) {
322  currentVariable = &currentVariable->GetAtIndex(0);
323  } else {
324  currentVariable =
325  currentVariable->GetAllChildren().begin()->second.get();
326  }
327  } else {
328  if (!currentVariable->HasChild(childName)) {
329  // Non existing child - there is no "parent".
330  return {};
331  }
332 
333  currentVariable = &currentVariable->GetChild(childName);
334  }
335  }
336 
337  // Return the last parent of the chain of variables (so not the last
338  // variable but the one before it).
339  return {.parentVariable = currentVariable};
340  }
341 
342  VariableAndItsParent WalkUntilLastParent(
343  const gd::VariablesContainer& variablesContainer,
344  const std::vector<gd::String>& childVariableNames) {
345  if (bailOutBecauseEmptyVariableName)
346  return {}; // Do not even attempt to find the parent if we had an issue
347  // when visiting nodes.
348  if (childVariableNames.empty())
349  return {}; // There is no "parent" to the variables container itself.
350 
351  const gd::String& firstChildName = *childVariableNames.begin();
352 
353  const gd::Variable* variable = variablesContainer.Has(firstChildName)
354  ? &variablesContainer.Get(firstChildName)
355  : nullptr;
356  if (childVariableNames.size() == 1 || !variable)
357  return {// Only one child: the parent is the variables container itself.
358  .parentVariablesContainer = &variablesContainer};
359 
360  return WalkUntilLastParent(*variable, childVariableNames, 1);
361  }
362 
363  gd::ExpressionNode* variableNode;
364  std::vector<gd::String> childVariableNames;
365  bool thisIsALegacyPrescopedVariable;
366  bool bailOutBecauseEmptyVariableName;
367  const gd::VariablesContainer* legacyPrescopedVariablesContainer;
368  VariableAndItsParent variableAndItsParent;
369 
370  const gd::Platform& platform;
371  const gd::ProjectScopedContainers& projectScopedContainers;
372 };
373 
374 } // 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 the last parent (i.e: the variables container) of a node representing a variable.
Definition: ExpressionVariableParentFinder.h:50
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:158
Describe a parameter of an instruction (action, condition) or of an expression: type,...
Definition: ParameterMetadata.h:27
gd::ValueTypeMetadata & GetValueTypeMetadata()
Return the metadata of the parameter type.
Definition: ParameterMetadata.h:35
static bool IsObject(const gd::String &parameterType)
Return true if the type of the parameter is representing one object (or more, i.e: an object group).
Definition: ParameterMetadata.h:186
const gd::String & GetType() const
Return the type of the parameter.
Definition: ParameterMetadata.h:55
Base class for implementing a platform.
Definition: Platform.h:42
Holds references to variables, objects, properties and other containers.
Definition: ProjectScopedContainers.h:30
String represents an UTF8 encoded string.
Definition: String.h:31
String::iterator begin()
Get a beginning iterator.
Definition: String.cpp:66
bool empty() const
Returns true if the string is empty.
Definition: String.h:155
String & insert(size_type pos, const String &str)
Inserts characters right before the character at pos.
Definition: String.cpp:231
bool IsLegacyPreScopedVariable() const
Return true if the type is a variable but from a specific scope (scene, project or object)....
Definition: ValueTypeMetadata.h:146
Defines a variable which can be used by an object, a layout or a project.
Definition: Variable.h:29
size_t GetChildrenCount() const
Get the count of children that the variable has.
Definition: Variable.h:187
const std::map< gd::String, std::shared_ptr< Variable > > & GetAllChildren() const
Get the map containing all the children.
Definition: Variable.h:244
bool HasChild(const gd::String &name) const
Return true if the variable is a structure and has the specified child.
Definition: Variable.cpp:130
Variable & GetChild(const gd::String &name)
Return the child with the specified name.
Definition: Variable.cpp:140
Type GetType() const
Get the type of the variable.
Definition: Variable.h:60
Variable & GetAtIndex(const size_t index)
Return the element with the specified index.
Definition: Variable.cpp:180
Class defining a container for gd::Variable.
Definition: VariablesContainer.h:30
bool Has(const gd::String &name) const
Return true if the specified variable is in the container.
Definition: VariablesContainer.cpp:40
Variable & Get(const gd::String &name)
Return a reference to the variable called name.
Definition: VariablesContainer.cpp:46
const VariablesContainer * GetTopMostVariablesContainer() const
Avoid using apart when a scope must be forced.
Definition: VariablesContainersList.h:54
const Variable & Get(const gd::String &name) const
Return a reference to the variable called name.
Definition: VariablesContainersList.cpp:37
const VariablesContainer * GetBottomMostVariablesContainer() const
Avoid using apart when a scope must be forced.
Definition: VariablesContainersList.h:63
bool Has(const gd::String &name) const
Return true if the specified variable is in one of the containers.
Definition: VariablesContainersList.cpp:28
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:416
The base node, from which all nodes in the tree of an expression inherits from.
Definition: ExpressionParser2Node.h:93
A function call node (either free function, object function or object behavior function)....
Definition: ExpressionParser2Node.h:364
An identifier node, usually representing an object or a variable with an optional function name or ch...
Definition: ExpressionParser2Node.h:197
gd::String childIdentifierName
The object function or variable child name.
Definition: ExpressionParser2Node.h:215
gd::String identifierName
The object or variable name.
Definition: ExpressionParser2Node.h:212
A number node. For example: "123". Its type is always "number".
Definition: ExpressionParser2Node.h:154
The name of a function to call on an object or the behavior For example: "MyObject....
Definition: ExpressionParser2Node.h:314
An operator node. For example: "lhs + rhs".
Definition: ExpressionParser2Node.h:122
Definition: ExpressionParser2Node.h:108
A text node. For example: "Hello World". Its type is always "string".
Definition: ExpressionParser2Node.h:170
A unary operator node. For example: "-2".
Definition: ExpressionParser2Node.h:138
A direct accessor to a child variable. Example: MyChild in MyVariable.MyChild.
Definition: ExpressionParser2Node.h:275
Contains a variables container or a variable. Useful to refer to the parent of a variable (which can ...
Definition: ExpressionVariableParentFinder.h:36
A bracket accessor to a child variable. Example: ["MyChild"] (in MyVariable["MyChild"]).
Definition: ExpressionParser2Node.h:293
A variable, or object variable, with bracket accessor or at least 2 "dot" accessors.
Definition: ExpressionParser2Node.h:254