mirror of
https://github.com/4ian/GDevelop.git
synced 2026-10-12 02:01:17 +00:00
Allow to properly use variables/properties/parameters in brackets to access a structure variable (#5738)
* This means expressions like `MyStructure[SomeIndexVariable]` will work properly for a structure if `SomeIndexVariable` is a string variable. Gdevelop now properly uses the type of the variable/property/parameter (to avoid considering what's inside the brackets as a number if it's a string). Remember to declare your variables in the variables editor so that you can use them in expressions.
This commit is contained in:
@@ -1212,13 +1212,13 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesConta
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getProperty" + property.GetName() + "()";
|
||||
return "getProperty" + property.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getParameter" + parameter.GetName() + "()";
|
||||
return "getParameter" + parameter.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class EventsList;
|
||||
class Expression;
|
||||
@@ -58,8 +58,9 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* \brief Construct a code generator for the specified
|
||||
* objects/groups and platform
|
||||
*/
|
||||
EventsCodeGenerator(const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
EventsCodeGenerator(
|
||||
const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
virtual ~EventsCodeGenerator(){};
|
||||
|
||||
/**
|
||||
@@ -546,7 +547,9 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
};
|
||||
|
||||
virtual gd::String GenerateVariableValueAs(const gd::String& type) {
|
||||
return type == "string" ? ".getAsString()" : ".getAsNumber()";
|
||||
return type == "number|string" ? ".getAsNumberOrString()"
|
||||
: type == "string" ? ".getAsString()"
|
||||
: ".getAsNumber()";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -577,14 +580,16 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
return "fakeObjectListOf_" + objectName;
|
||||
}
|
||||
|
||||
virtual gd::String GeneratePropertyGetter(const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
virtual gd::String GeneratePropertyGetter(
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
virtual gd::String GenerateParameterGetter(
|
||||
const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
/**
|
||||
* \brief Generate the code to reference an object which is
|
||||
@@ -665,7 +670,8 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* The default implementation generates C-style code : It wraps the predicate
|
||||
* inside parenthesis and add a !.
|
||||
*/
|
||||
virtual gd::String GenerateNegatedPredicate(const gd::String& predicate) const {
|
||||
virtual gd::String GenerateNegatedPredicate(
|
||||
const gd::String& predicate) const {
|
||||
return "!(" + predicate + ")";
|
||||
};
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
|
||||
// This "translation" from the type to an enum could be avoided
|
||||
// if all types were moved to an enum.
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -191,7 +191,7 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
return;
|
||||
}
|
||||
|
||||
ExpressionCodeGenerator generator("string", "", codeGenerator, context);
|
||||
ExpressionCodeGenerator generator("number|string", "", codeGenerator, context);
|
||||
node.expression->Visit(generator);
|
||||
output +=
|
||||
codeGenerator.GenerateVariableBracketAccessor(generator.GetOutput());
|
||||
@@ -200,7 +200,7 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -271,7 +271,7 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -502,7 +502,7 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
@@ -511,7 +511,7 @@ void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(
|
||||
ObjectFunctionNameNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
|
||||
@@ -58,7 +58,7 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -88,7 +88,7 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
context.AddObjectName(projectScopedContainers, node.identifierName);
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
|
||||
@@ -81,8 +81,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, objectsContainersList, rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -115,7 +114,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == objectName) {
|
||||
hasDoneRenaming = true;
|
||||
@@ -217,8 +216,7 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, objectsContainersList, rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -250,7 +248,7 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == searchedObjectName) {
|
||||
hasObject = true;
|
||||
|
||||
@@ -395,10 +395,8 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix("", type);
|
||||
completions.push_back(
|
||||
@@ -409,10 +407,8 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// No completions.
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix("", type);
|
||||
completions.push_back(
|
||||
@@ -488,7 +484,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
if (type == "globalvar") {
|
||||
@@ -535,7 +531,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
// Only show completions of objects if an object is required.
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
@@ -615,10 +611,8 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
if (!node.behaviorFunctionName.empty() ||
|
||||
node.behaviorNameNamespaceSeparatorLocation.IsValid()) {
|
||||
// Behavior function (or behavior function being written, with the
|
||||
@@ -669,10 +663,8 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
bool isCaretOnParenthesis = IsCaretOn(node.openingParenthesisLocation) ||
|
||||
IsCaretOn(node.closingParenthesisLocation);
|
||||
|
||||
@@ -741,10 +733,8 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix(node.text, type, node.location);
|
||||
completions.push_back(
|
||||
|
||||
@@ -14,13 +14,12 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
@@ -41,10 +40,10 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
* operations.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionLeftSideTypeFinder typeFinder(
|
||||
platform, objectsContainersList);
|
||||
platform, projectScopedContainers);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
@@ -53,9 +52,9 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
|
||||
protected:
|
||||
ExpressionLeftSideTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainersList &objectsContainersList_)
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_)
|
||||
: platform(platform_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
type("unknown") {};
|
||||
|
||||
const gd::String &GetType() {
|
||||
@@ -67,6 +66,14 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
node.leftHandSide->Visit(*this);
|
||||
|
||||
// The type is decided by the first operand, unless it can (`number|string`)
|
||||
// or should (`unknown`) be refined, in which case we go for the right
|
||||
// operand (which got visited knowing the type of the first operand, so it's
|
||||
// equal or strictly more precise than the left operand).
|
||||
if (type == "unknown" || type == "number|string") {
|
||||
node.rightHandSide->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
node.factor->Visit(*this);
|
||||
@@ -83,7 +90,7 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, objectsContainersList, node);
|
||||
platform, projectScopedContainers.GetObjectsContainersList(), node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
type = "unknown";
|
||||
}
|
||||
@@ -93,12 +100,99 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
type = "unknown";
|
||||
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.name,
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
// We could store it to explore the type of the variable, but in practice this
|
||||
// is only called for structures/arrays with 2 levels, and we don't support structure
|
||||
// type identification for now.
|
||||
},
|
||||
[&]() {
|
||||
// This is a variable.
|
||||
// We could store it to explore the type of the variable, but in practice this
|
||||
// is only called for structures/arrays with 2 levels, and we don't support structure
|
||||
// type identification for now.
|
||||
}, [&]() {
|
||||
// This is a property with more than one child - this is unsupported.
|
||||
}, [&]() {
|
||||
// This is a parameter with more than one child - this is unsupported.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
type = "unknown";
|
||||
});
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
type = "unknown";
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
type = "unknown";
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName,
|
||||
[&]() {
|
||||
// It's an object variable.
|
||||
if (projectScopedContainers.GetObjectsContainersList()
|
||||
.HasObjectOrGroupWithVariableNamed(
|
||||
node.identifierName, node.childIdentifierName)
|
||||
== ObjectsContainersList::VariableExistence::DoesNotExist) {
|
||||
type = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
auto variableType =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetTypeOfObjectOrGroupVariable(node.identifierName,
|
||||
node.childIdentifierName);
|
||||
ReadTypeFromVariable(variableType);
|
||||
},
|
||||
[&]() {
|
||||
// It's a variable.
|
||||
const auto& variable =
|
||||
projectScopedContainers.GetVariablesContainersList().Get(
|
||||
node.identifierName);
|
||||
|
||||
if (node.childIdentifierName.empty()) {
|
||||
ReadTypeFromVariable(variable.GetType());
|
||||
} else {
|
||||
if (!variable.HasChild(node.childIdentifierName)) {
|
||||
type = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
ReadTypeFromVariable(
|
||||
variable.GetChild(node.childIdentifierName).GetType());
|
||||
}
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
const gd::NamedPropertyDescriptor& property = projectScopedContainers
|
||||
.GetPropertiesContainersList().Get(node.identifierName).second;
|
||||
|
||||
if (property.GetType() == "Number") {
|
||||
type = "number";
|
||||
} else if (property.GetType() == "Boolean") {
|
||||
// Nothing - we don't know the precise type (this could be used a string or as a number)
|
||||
} else {
|
||||
// Assume type is String or equivalent.
|
||||
type = "string";
|
||||
}
|
||||
}, [&]() {
|
||||
// It's a parameter.
|
||||
|
||||
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
|
||||
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, node.identifierName);
|
||||
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
|
||||
if (valueTypeMetadata.IsNumber()) {
|
||||
type = "number";
|
||||
} else if (valueTypeMetadata.IsString()) {
|
||||
type = "string";
|
||||
} else if (valueTypeMetadata.IsBoolean()) {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
} else {
|
||||
type = "unknown";
|
||||
}
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
type = "unknown";
|
||||
});
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
type = "unknown";
|
||||
@@ -108,10 +202,18 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
|
||||
private:
|
||||
void ReadTypeFromVariable(gd::Variable::Type variableType) {
|
||||
if (variableType == gd::Variable::Number) {
|
||||
type = "number";
|
||||
} else if (variableType == gd::Variable::String) {
|
||||
type = "string";
|
||||
}
|
||||
}
|
||||
|
||||
gd::String type;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
@@ -51,11 +50,11 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
* sub-expression that a given node represents.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionTypeFinder typeFinder(
|
||||
platform, objectsContainersList, rootType);
|
||||
platform, projectScopedContainers, rootType);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
@@ -64,10 +63,10 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
|
||||
protected:
|
||||
ExpressionTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainersList &objectsContainersList_,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_,
|
||||
const gd::String &rootType_)
|
||||
: platform(platform_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
rootType(rootType_),
|
||||
type(ExpressionTypeFinder::unknownType),
|
||||
child(nullptr) {};
|
||||
@@ -113,8 +112,12 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
}
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers,
|
||||
node);
|
||||
|
||||
// If we can infer a definitive number or string type, use it.
|
||||
// Otherwise, we only know that it's number or string, and this can even
|
||||
// be used as is at runtime.
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
type = leftSideType;
|
||||
@@ -126,7 +129,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (child == nullptr) {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, objectsContainersList, node);
|
||||
platform, projectScopedContainers.GetObjectsContainersList(), node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
VisitParent(node);
|
||||
}
|
||||
@@ -138,7 +141,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
gd::MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers.GetObjectsContainersList(),
|
||||
node,
|
||||
*child);
|
||||
if (parameterMetadata == nullptr || parameterMetadata->GetType().empty()) {
|
||||
@@ -159,7 +162,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
else if (rootType == ExpressionTypeFinder::numberOrStringType) {
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers,
|
||||
node);
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
@@ -183,7 +186,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
ExpressionNode *child;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
|
||||
@@ -89,12 +89,17 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
const auto& propertiesContainersList = projectScopedContainers.GetPropertiesContainersList();
|
||||
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
|
||||
|
||||
// Unless we find something precise (like a variable or property or parameter with a known type),
|
||||
// we consider this node will be of the type required by the parent.
|
||||
childType = parentType;
|
||||
|
||||
return projectScopedContainers.MatchIdentifierWithName<bool>(identifier.identifierName,
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
if (identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("An object variable or expression should be entered."),
|
||||
identifier.identifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
|
||||
@@ -103,26 +108,31 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
if (variableExistence == gd::ObjectsContainersList::DoesNotExist) {
|
||||
RaiseTypeError(_("This variable does not exist on this object or group."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
|
||||
RaiseTypeError(_("This variable only exists on some objects of the group. It must be declared for all objects."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
|
||||
RaiseTypeError(_("This group is empty. Add an object to this group first."),
|
||||
identifier.identifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
|
||||
auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(identifier.identifierName, identifier.childIdentifierName);
|
||||
ReadChildTypeFromVariable(variableType);
|
||||
|
||||
return true; // We found a variable.
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
|
||||
// Try to identify a declared variable with the name (and maybe the child
|
||||
// variable).
|
||||
|
||||
const gd::Variable& variable =
|
||||
variablesContainersList.Get(identifier.identifierName);
|
||||
|
||||
@@ -130,6 +140,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
// Just the root variable is accessed, check it can be used in an
|
||||
// expression.
|
||||
validateVariableTypeForExpression(variable.GetType());
|
||||
ReadChildTypeFromVariable(variable.GetType());
|
||||
|
||||
return true; // We found a variable.
|
||||
} else {
|
||||
@@ -143,6 +154,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
|
||||
const gd::Variable& childVariable =
|
||||
variable.GetChild(identifier.childIdentifierName);
|
||||
ReadChildTypeFromVariable(childVariable.GetType());
|
||||
return true; // We found a variable.
|
||||
}
|
||||
}, [&]() {
|
||||
@@ -150,23 +162,44 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
if (!identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We found a property, even if the child is not allowed.
|
||||
}
|
||||
|
||||
const gd::NamedPropertyDescriptor& property = projectScopedContainers
|
||||
.GetPropertiesContainersList().Get(identifier.identifierName).second;
|
||||
|
||||
if (property.GetType() == "Number") {
|
||||
childType = Type::Number;
|
||||
} else if (property.GetType() == "Boolean") {
|
||||
// Nothing - we don't know the precise type (this could be used a string or as a number)
|
||||
} else {
|
||||
// Assume type is String or equivalent.
|
||||
childType = Type::String;
|
||||
}
|
||||
|
||||
return true; // We found a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
if (!identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We found a parameter, even if the child is not allowed.
|
||||
}
|
||||
|
||||
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, identifier.identifierName);
|
||||
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
|
||||
if (!valueTypeMetadata.IsNumber() && !valueTypeMetadata.IsString() && !valueTypeMetadata.IsBoolean()) {
|
||||
if (valueTypeMetadata.IsNumber()) {
|
||||
childType = Type::Number;
|
||||
} else if (valueTypeMetadata.IsString()) {
|
||||
childType = Type::String;
|
||||
} else if (valueTypeMetadata.IsBoolean()) {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
} else {
|
||||
RaiseTypeError(_("This parameter is not a string, number or boolean - it can't be used in an expression."),
|
||||
identifier.identifierNameLocation);
|
||||
|
||||
return true; // We found a parameter, even though the type is incompatible.
|
||||
}
|
||||
|
||||
|
||||
@@ -103,9 +103,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
"example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
|
||||
}
|
||||
else if (node.op != '+') {
|
||||
RaiseOperatorError(
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts."),
|
||||
RaiseOperatorError(
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts."),
|
||||
ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
|
||||
}
|
||||
} else if (leftType == Type::Object) {
|
||||
@@ -124,7 +124,11 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
node.rightHandSide->Visit(*this);
|
||||
const Type rightType = childType;
|
||||
|
||||
childType = leftType;
|
||||
// The type is decided by the first operand, unless it can (`number|string`)
|
||||
// or should (`unknown`) be refined, in which case we go for the right
|
||||
// operand (which got visited knowing the type of the first operand, so it's
|
||||
// equal or strictly more precise than the left operand).
|
||||
childType = (leftType == Type::Unknown || leftType == Type::NumberOrString) ? leftType : rightType;
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
@@ -309,8 +313,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
RaiseTypeError(
|
||||
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
|
||||
node.location);
|
||||
childType = parentType;
|
||||
} else {
|
||||
childType = parentType;
|
||||
}
|
||||
childType = parentType;
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
ReportAnyError(node);
|
||||
@@ -379,6 +385,16 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
RaiseError("invalid_operator", message, location);
|
||||
}
|
||||
|
||||
void ReadChildTypeFromVariable(gd::Variable::Type variableType) {
|
||||
if (variableType == gd::Variable::Number) {
|
||||
childType = Type::Number;
|
||||
} else if (variableType == gd::Variable::String) {
|
||||
childType = Type::String;
|
||||
} else {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
}
|
||||
}
|
||||
|
||||
static Type StringToType(const gd::String &type);
|
||||
static const gd::String &TypeToString(Type type);
|
||||
static const gd::String unknownTypeString;
|
||||
|
||||
@@ -110,7 +110,7 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
|
||||
result.GetUsedExtensions().insert("BuiltinVariables");
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
project.GetCurrentPlatform(), GetObjectsContainersList(), rootType, node);
|
||||
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -154,7 +154,7 @@ void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
|
||||
// Add extensions bound to Objects/Behaviors/Functions
|
||||
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
project.GetCurrentPlatform(), GetObjectsContainersList(), rootType, node);
|
||||
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) ||
|
||||
GetObjectsContainersList().HasObjectOrGroupNamed(node.identifierName)) {
|
||||
// An object or object variable is used.
|
||||
|
||||
@@ -148,6 +148,55 @@ ObjectsContainersList::GetObjectOrGroupVariablesContainer(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gd::Variable::Type ObjectsContainersList::GetTypeOfObjectOrGroupVariable(
|
||||
const gd::String& objectOrGroupName, const gd::String& variableName) const {
|
||||
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
|
||||
return variables.Get(variableName).GetType();
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// This could be adapted if objects groups have variables in the future.
|
||||
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase. Consider that the first object having the variable will
|
||||
// define its type.
|
||||
const auto& objectGroup = (*it)->GetObjectGroups().Get(objectOrGroupName);
|
||||
const auto& objectNames = objectGroup.GetAllObjectsNames();
|
||||
|
||||
for (const auto& objectName : objectNames) {
|
||||
if (HasObjectWithVariableNamed(objectName, variableName)) {
|
||||
return GetTypeOfObjectVariable(objectName, variableName);
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
|
||||
gd::Variable::Type ObjectsContainersList::GetTypeOfObjectVariable(const gd::String& objectName, const gd::String& variableName) const {
|
||||
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectName).GetVariables();
|
||||
|
||||
return variables.Get(variableName).GetType();
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachObjectOrGroupVariableWithPrefix(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& prefix,
|
||||
|
||||
@@ -72,6 +72,11 @@ class GD_CORE_API ObjectsContainersList {
|
||||
const gd::VariablesContainer* GetObjectOrGroupVariablesContainer(
|
||||
const gd::String& objectOrGroupName) const;
|
||||
|
||||
/**
|
||||
* \brief Get a type from an object/group variable.
|
||||
*/
|
||||
gd::Variable::Type GetTypeOfObjectOrGroupVariable(const gd::String& objectOrGroupName, const gd::String& variableName) const;
|
||||
|
||||
/**
|
||||
* \brief Get a type from an object/group name.
|
||||
* \note If a group contains only objects of a same type, then the group has
|
||||
@@ -160,6 +165,8 @@ class GD_CORE_API ObjectsContainersList {
|
||||
bool HasObjectWithVariableNamed(const gd::String& objectName,
|
||||
const gd::String& variableName) const;
|
||||
|
||||
gd::Variable::Type GetTypeOfObjectVariable(const gd::String& objectName, const gd::String& variableName) const;
|
||||
|
||||
void ForEachObjectVariableWithPrefix(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& prefix,
|
||||
|
||||
@@ -22,12 +22,19 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
|
||||
// Add some variables and objects:
|
||||
layout1.GetVariables().InsertNew("MySceneVariable", 0);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2", 1);
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable", 2).GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2", 2).GetChild("MyChild");
|
||||
project.GetVariables().InsertNew("MyGlobalNumberVariable").SetValue(1234);
|
||||
project.GetVariables().InsertNew("MyGlobalStringVariable").SetString("TestGlobal");
|
||||
layout1.GetVariables().InsertNew("MySceneVariable").SetValue(123);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2").SetValue(123);
|
||||
layout1.GetVariables().InsertNew("MySceneStringVariable").SetString("MyString");
|
||||
layout1.GetVariables().InsertNew("MySceneBooleanVariable").SetBool(true);
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable").GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2").GetChild("MyChild");
|
||||
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
auto &mySpriteObject = layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
mySpriteObject.GetVariables().InsertNew("MyNumberVariable").SetValue(123);
|
||||
mySpriteObject.GetVariables().InsertNew("MyStringVariable").SetString("Test");
|
||||
mySpriteObject.GetVariables().InsertNew("MyStructureVariable").GetChild("MyStringChild").SetString("Test");
|
||||
layout1.InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyOtherSpriteObject", 1);
|
||||
layout1.InsertNewObject(project,
|
||||
@@ -432,7 +439,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyProperty() + 1");
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyPropertyAsnumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
@@ -444,7 +451,56 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyProperty() + getPropertyMyProperty2()");
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyPropertyAsnumber() + getPropertyMyProperty2Asnumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Properties (1 level, number|string)") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyNumberProperty").SetType("Number");
|
||||
propertiesContainer.InsertNew("MyStringProperty").SetType("String");
|
||||
propertiesContainer.InsertNew("MyBooleanProperty").SetType("Boolean");
|
||||
|
||||
gd::EventsCodeGenerator codeGeneratorWithProperties(platform, projectScopedContainersWithProperties);
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberProperty");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyNumberPropertyAsnumber()");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringProperty");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyStringPropertyAsstring()");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyBooleanProperty");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyBooleanPropertyAsnumber|string()");
|
||||
}
|
||||
}
|
||||
SECTION("Parameters (1 level)") {
|
||||
@@ -473,7 +529,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1() + 1");
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1Asnumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
@@ -485,7 +541,64 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1() + getParameterMyParameter2()");
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1Asnumber() + getParameterMyParameter2Asnumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Parameters (1 level, number|string)") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyNumberParameter");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyStringParameter");
|
||||
param2.SetType("string");
|
||||
gd::ParameterMetadata param3;
|
||||
param3.SetName("MyBooleanParameter");
|
||||
param3.SetType("yesorno");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
parameters.push_back(param3);
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
|
||||
gd::EventsCodeGenerator codeGeneratorWithProperties(platform, projectScopedContainersWithParameters);
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberParameter");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyNumberParameterAsnumber()");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringParameter");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyStringParameterAsstring()");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyBooleanParameter");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyBooleanParameterAsnumber|string()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (1 level)") {
|
||||
@@ -554,7 +667,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor)") {
|
||||
SECTION("Scene variables (2 levels with bracket accessor, string)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[\"MyChild\"] + 1");
|
||||
@@ -580,6 +693,144 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, number)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[3] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(3).getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[3] + MySceneStructureVariable2[3]");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(3).getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(3).getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a number variable as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneVariable] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneVariable).getAsNumber()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a string variable as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStringVariable] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a non string/number variable as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneBooleanVariable] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneBooleanVariable).getAsNumberOrString()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumberOrString()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type and an operator with a number as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + 2] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumber() + 2).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type and an operator with a string as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + \"Test\"] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsString() + \"Test\").getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type as index) (expression type: number|string)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric]");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumberOrString()).getAsNumberOrString()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a number variable casted to string as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[\"\" + MySceneVariable] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"\" + getLayoutVariable(MySceneVariable).getAsString()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Object variable with non existing object (invalid)") {
|
||||
auto node =
|
||||
parser.ParseExpression("MyNonExistingSpriteObject.MyVariable");
|
||||
@@ -757,7 +1008,20 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"fakeBadVariable");
|
||||
}
|
||||
}
|
||||
SECTION("Valid variables") {
|
||||
SECTION("Valid variables (upcoming, new 'variable' type working for any variable)") {
|
||||
// When implemented, copy the test cases from the next section, like this:
|
||||
// SECTION("simple variable") {
|
||||
// REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
// codeGenerator, context, "variable", "MySceneVariable", "")
|
||||
// == "getLayoutVariable(MySceneVariable)");
|
||||
// }
|
||||
// SECTION("simple (global) variable") {
|
||||
// REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
// codeGenerator, context, "variable", "MyGlobalNumberVariable", "")
|
||||
// == "getProjectVariable(MyGlobalNumberVariable)");
|
||||
// }
|
||||
}
|
||||
SECTION("Valid variables (legacy, pre-scoped variables)") {
|
||||
SECTION("simple variable") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable", "")
|
||||
@@ -779,13 +1043,63 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"\"world\" ]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(\"hello\" + \"world\")");
|
||||
}
|
||||
SECTION("object variable (legacy)") {
|
||||
SECTION("bracket access (using a string object variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySpriteObject.MyStringVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getVariableForObject(MySpriteObject, MyStringVariable).getAsString())");
|
||||
}
|
||||
SECTION("bracket access (using a number object variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySpriteObject.MyNumberVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getVariableForObject(MySpriteObject, MyNumberVariable).getAsNumber())");
|
||||
}
|
||||
SECTION("bracket access (using a string variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySceneStringVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString())");
|
||||
}
|
||||
SECTION("bracket access (using a number variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySceneVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneVariable).getAsNumber())");
|
||||
}
|
||||
SECTION("bracket access (using a string global variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MyGlobalStringVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getProjectVariable(MyGlobalStringVariable).getAsString())");
|
||||
}
|
||||
SECTION("bracket access (using a number global variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MyGlobalNumberVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getProjectVariable(MyGlobalNumberVariable).getAsNumber())");
|
||||
}
|
||||
SECTION("bracket access (using a boolean variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySceneBooleanVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneBooleanVariable).getAsNumberOrString())");
|
||||
}
|
||||
SECTION("bracket access (using a structure variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySceneStructureVariable.MyChild.SubChild]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"SubChild\").getAsNumberOrString())");
|
||||
}
|
||||
SECTION("object variable") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "myVariable", "MySpriteObject")
|
||||
== "getVariableForObject(MySpriteObject, myVariable)");
|
||||
}
|
||||
SECTION("object variable with bracket access (using a structure variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "myVariable[MySceneStringVariable]", "MySpriteObject")
|
||||
== "getVariableForObject(MySpriteObject, myVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString())");
|
||||
}
|
||||
SECTION("object variable with bracket access (using an object variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "myVariable[MySpriteObject.MyStructureVariable.MyChild]", "MySpriteObject")
|
||||
== "getVariableForObject(MySpriteObject, myVariable).getChild(getVariableForObject(MySpriteObject, MyStructureVariable).getChild(\"MyChild\").getAsNumberOrString())");
|
||||
}
|
||||
}
|
||||
SECTION("Valid function calls with variables") {
|
||||
SECTION("Valid function calls with variables (legacy, pre-scoped)") {
|
||||
SECTION("Simple access") {
|
||||
SECTION("Scene variable") {
|
||||
auto node = parser.ParseExpression(
|
||||
|
||||
@@ -22,10 +22,13 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable", 0);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2", 1);
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable", 2).GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2", 2).GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneVariable");
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable").GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2").GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneNumberVariable").SetValue(123);
|
||||
layout1.GetVariables().InsertNew("MySceneStringVariable").SetString("Test");
|
||||
layout1.GetVariables().InsertNew("MySceneBooleanVariable").SetBool(true);
|
||||
|
||||
// Create an instance of BuiltinObject.
|
||||
// This is not possible in practice.
|
||||
@@ -38,9 +41,11 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
layout1.GetObjectGroups().InsertNew("EmptyGroup");
|
||||
|
||||
auto &mySpriteObject = layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 1);
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable", 0);
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable2", 1);
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable3", 2);
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable");
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable2");
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable3");
|
||||
mySpriteObject.GetVariables().InsertNew("MyNumberVariable").SetValue(123);
|
||||
mySpriteObject.GetVariables().InsertNew("MyStringVariable").SetString("Test");
|
||||
auto &mySpriteObject2 = layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject2", 1);
|
||||
mySpriteObject2.GetVariables().InsertNew("MyVariable", 0);
|
||||
mySpriteObject2.GetVariables().InsertNew("MyVariable2", 1);
|
||||
@@ -64,7 +69,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", emptyNode);
|
||||
platform, projectScopedContainers, "string", emptyNode);
|
||||
REQUIRE(type == "string");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
|
||||
@@ -79,7 +84,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", emptyNode);
|
||||
platform, projectScopedContainers, "number", emptyNode);
|
||||
REQUIRE(type == "number");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
|
||||
@@ -94,7 +99,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "object", emptyNode);
|
||||
platform, projectScopedContainers, "object", emptyNode);
|
||||
REQUIRE(type == "object");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
|
||||
@@ -111,7 +116,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", emptyNode);
|
||||
platform, projectScopedContainers, "string", emptyNode);
|
||||
REQUIRE(type == "string");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
@@ -120,7 +125,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", emptyNode);
|
||||
platform, projectScopedContainers, "number", emptyNode);
|
||||
REQUIRE(type == "number");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
@@ -129,7 +134,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "object", emptyNode);
|
||||
platform, projectScopedContainers, "object", emptyNode);
|
||||
REQUIRE(type == "object");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
@@ -485,7 +490,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", operatorNode);
|
||||
platform, projectScopedContainers, "number", operatorNode);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(type == "number");
|
||||
auto &leftNumberNode =
|
||||
@@ -504,7 +509,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", operatorNode);
|
||||
platform, projectScopedContainers, "string", operatorNode);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(type == "string");
|
||||
auto &leftTextNode =
|
||||
@@ -526,7 +531,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", operatorNode);
|
||||
platform, projectScopedContainers, "number|string", operatorNode);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(type == "number");
|
||||
auto &leftNumberNode =
|
||||
@@ -545,7 +550,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", operatorNode);
|
||||
platform, projectScopedContainers, "number|string", operatorNode);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(type == "string");
|
||||
auto &leftTextNode =
|
||||
@@ -567,7 +572,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -583,7 +588,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '+');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -599,7 +604,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -617,7 +622,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number|string", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -633,7 +638,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number|string", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '+');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -649,7 +654,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number|string", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -914,6 +919,242 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetEndPosition() == 19);
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with a known variable type first)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneNumberVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 24);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStringVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 24);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneNumberVariable + MySceneBooleanVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 49);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStringVariable + MySceneBooleanVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 49);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyNumberVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyStringVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with a parameter first)") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyNumberParameter");
|
||||
param.SetType("number");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyStringParameter");
|
||||
param.SetType("string");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyBooleanParameter");
|
||||
param.SetType("yesorno");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberParameter + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringParameter + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberParameter + MyBooleanParameter + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringParameter + MyBooleanParameter + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with a property first)") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyNumberProperty").SetType("Number");
|
||||
propertiesContainer.InsertNew("MyStringProperty").SetType("String");
|
||||
propertiesContainer.InsertNew("MyBooleanProperty").SetType("Boolean");
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberProperty + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringProperty + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberProperty + MyBooleanProperty + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringProperty + MyBooleanProperty + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with an unknown variable type first)") {
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneBooleanVariable + 123 + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneBooleanVariable + \"hello world\" + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStructureVariable.MyChild.UnknownSubChild + 123 + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStructureVariable.MyChild.UnknownSubChild + \"hello world\" + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches with parenthesis") {
|
||||
{
|
||||
auto node =
|
||||
@@ -1451,21 +1692,78 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
|
||||
SECTION("Valid property") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyProperty").SetType("Number");
|
||||
propertiesContainer.InsertNew("MyProperty2").SetType("String");
|
||||
propertiesContainer.InsertNew("MyProperty3").SetType("Boolean");
|
||||
|
||||
{
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty");
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
propertiesContainer.InsertNew("MyProperty");
|
||||
propertiesContainer.InsertNew("MyProperty2");
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "number|string");
|
||||
}
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty + MyProperty2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty + MyProperty2 + MyProperty3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1485,7 +1783,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name.");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You must wrap your text inside double quotes (example: \"Hello world\").");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1529,7 +1827,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
|
||||
SECTION("Valid parameter") {
|
||||
{
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
@@ -1537,18 +1834,75 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("string");
|
||||
gd::ParameterMetadata param3;
|
||||
param3.SetName("MyParameter3");
|
||||
param3.SetType("yesorno");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
parameters.push_back(param3);
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter1");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number|string");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter1 + MyParameter2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter1 + MyParameter2 + MyParameter3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1598,7 +1952,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name.");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You must enter a number.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1658,7 +2012,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", functionNode);
|
||||
platform, projectScopedContainers, "number|string", functionNode);
|
||||
REQUIRE(functionNode.functionName == "MyExtension::GetNumber");
|
||||
REQUIRE(type == "number");
|
||||
REQUIRE(functionNode.objectName == "");
|
||||
@@ -1674,7 +2028,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", functionNode);
|
||||
platform, projectScopedContainers, "number|string", functionNode);
|
||||
REQUIRE(functionNode.functionName == "MyExtension::ToString");
|
||||
REQUIRE(type == "string");
|
||||
REQUIRE(functionNode.objectName == "");
|
||||
@@ -1809,7 +2163,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", objectFunctionCall);
|
||||
platform, projectScopedContainers, "string", objectFunctionCall);
|
||||
REQUIRE(objectFunctionCall.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionCall.functionName == "");
|
||||
REQUIRE(type == "string");
|
||||
@@ -1820,7 +2174,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", objectFunctionCall);
|
||||
platform, projectScopedContainers, "number", objectFunctionCall);
|
||||
REQUIRE(objectFunctionCall.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionCall.functionName == "");
|
||||
REQUIRE(type == "number");
|
||||
@@ -1833,7 +2187,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", objectFunctionCall);
|
||||
platform, projectScopedContainers, "number|string", objectFunctionCall);
|
||||
REQUIRE(objectFunctionCall.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionCall.functionName == "");
|
||||
REQUIRE(type == "number|string");
|
||||
@@ -1854,7 +2208,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", objectFunctionName);
|
||||
platform, projectScopedContainers, "string", objectFunctionName);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.functionName == "");
|
||||
@@ -1866,7 +2220,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", objectFunctionName);
|
||||
platform, projectScopedContainers, "number", objectFunctionName);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.functionName == "");
|
||||
@@ -1881,7 +2235,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", objectFunctionName);
|
||||
platform, projectScopedContainers, "number|string", objectFunctionName);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.functionName == "");
|
||||
@@ -1893,7 +2247,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", freeFunctionCall);
|
||||
platform, projectScopedContainers, "string", freeFunctionCall);
|
||||
REQUIRE(freeFunctionCall.objectName == "");
|
||||
REQUIRE(freeFunctionCall.functionName == "fun");
|
||||
REQUIRE(type == "string");
|
||||
@@ -1904,7 +2258,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", freeFunctionCall);
|
||||
platform, projectScopedContainers, "number", freeFunctionCall);
|
||||
REQUIRE(freeFunctionCall.objectName == "");
|
||||
REQUIRE(freeFunctionCall.functionName == "fun");
|
||||
REQUIRE(type == "number");
|
||||
@@ -1916,7 +2270,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", freeFunctionCall);
|
||||
platform, projectScopedContainers, "number|string", freeFunctionCall);
|
||||
REQUIRE(freeFunctionCall.objectName == "");
|
||||
REQUIRE(freeFunctionCall.functionName == "fun");
|
||||
REQUIRE(type == "number|string");
|
||||
@@ -2272,21 +2626,21 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
auto node = parser.ParseExpression("123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", *node.get());
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("123 + MyExtension::GetNumber()");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", *node.get());
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("\"Hello\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", *node.get());
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
@@ -2294,10 +2648,160 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
"\"Hello\" + MyExtension::ToString(3)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", *node.get());
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with an known variable first") {
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneNumberVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStringVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneNumberVariable + MySceneBooleanVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStringVariable + MySceneBooleanVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with an unknown variable first") {
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneBooleanVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneBooleanVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStructureVariable.MyChild.UnknownSubChild + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStructureVariable.MyChild.UnknownSubChild + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with a property first") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyNumberProperty").SetType("Number");
|
||||
propertiesContainer.InsertNew("MyStringProperty").SetType("String");
|
||||
propertiesContainer.InsertNew("MyBooleanProperty").SetType("Boolean");
|
||||
{
|
||||
auto node = parser.ParseExpression("MyNumberProperty + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyStringProperty + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyBooleanProperty + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyBooleanProperty + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with a parameter first") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyNumberParameter");
|
||||
param.SetType("number");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyStringParameter");
|
||||
param.SetType("string");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyBooleanParameter");
|
||||
param.SetType("yesorno");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
{
|
||||
auto node = parser.ParseExpression("MyNumberParameter + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyStringParameter + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyBooleanParameter + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyBooleanParameter + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid function call with object variable") {
|
||||
{
|
||||
|
||||
@@ -1381,7 +1381,16 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(
|
||||
property.GetName())) +
|
||||
"()";
|
||||
|
||||
if (type == "string") {
|
||||
if (type == "number|string") {
|
||||
if (property.GetType() == "Number") {
|
||||
return propertyGetterCode;
|
||||
} else if (property.GetType() == "Boolean") {
|
||||
return "(" + propertyGetterCode + " ? \"true\" : \"false\")";
|
||||
} else {
|
||||
// Assume type is String or equivalent.
|
||||
return propertyGetterCode;
|
||||
}
|
||||
} else if (type == "string") {
|
||||
if (property.GetType() == "Number") {
|
||||
return "(\"\" + " + propertyGetterCode + ")";
|
||||
} else if (property.GetType() == "Boolean") {
|
||||
@@ -1413,7 +1422,16 @@ gd::String EventsCodeGenerator::GenerateParameterGetter(
|
||||
gd::String parameterGetterCode =
|
||||
"eventsFunctionContext.getArgument(" + ConvertToStringExplicit(parameter.GetName()) + ")";
|
||||
|
||||
if (type == "string") {
|
||||
if (type == "number|string") {
|
||||
if (parameter.GetValueTypeMetadata().IsNumber()) {
|
||||
return parameterGetterCode;
|
||||
} else if (parameter.GetValueTypeMetadata().IsBoolean()) {
|
||||
return "(" + parameterGetterCode + " ? \"true\" : \"false\")";
|
||||
} else {
|
||||
// Assume type is String or equivalent.
|
||||
return parameterGetterCode;
|
||||
}
|
||||
} else if (type == "string") {
|
||||
if (parameter.GetValueTypeMetadata().IsNumber()) {
|
||||
return "(\"\" + " + parameterGetterCode + ")";
|
||||
} else if (parameter.GetValueTypeMetadata().IsBoolean()) {
|
||||
|
||||
@@ -302,11 +302,17 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
const gd::String& objectName);
|
||||
|
||||
virtual gd::String GenerateVariableAccessor(gd::String childName) {
|
||||
// This could be probably optimised by using `getChildNamed`.
|
||||
return ".getChild(" + ConvertToStringExplicit(childName) + ")";
|
||||
};
|
||||
|
||||
virtual gd::String GenerateVariableBracketAccessor(
|
||||
gd::String expressionCode) {
|
||||
// This uses `getChild` which allows to access a child
|
||||
// with a number (an index, for an array) or a string (for a structure).
|
||||
// This could be optimised, if the type of the accessed variable AND the type of the index is known,
|
||||
// so that `getChildAt` (for an array, with an index) or `getChildNamed` (for a structure, with a name)
|
||||
// is used instead.
|
||||
return ".getChild(" + expressionCode + ")";
|
||||
};
|
||||
|
||||
|
||||
+52
-17
@@ -255,26 +255,43 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the child with the specified name or at the specified index.
|
||||
*
|
||||
* If the variable is an array, prefer `getChildAt`.
|
||||
* If the variable is a structure, prefer `getChildNamed`.
|
||||
*
|
||||
* If the variable has not the specified child, an empty variable with the specified name
|
||||
* (or index) is added as child.
|
||||
*
|
||||
* @returns The child variable
|
||||
*/
|
||||
getChild(childName: string | number): gdjs.Variable {
|
||||
if (this._type === 'array')
|
||||
return this.getChildAt(
|
||||
// @ts-ignore
|
||||
Number.isInteger(childName) ? childName : parseInt(childName, 10) || 0
|
||||
);
|
||||
else {
|
||||
if (this._type !== 'structure') this.castTo('structure');
|
||||
return this.getChildNamed('' + childName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the child with the specified name.
|
||||
*
|
||||
* If the variable has not the specified child, an empty variable with the specified name
|
||||
* is added as child.
|
||||
*
|
||||
* @returns The child variable
|
||||
*/
|
||||
getChild(childName: string): gdjs.Variable {
|
||||
// Make sure the variable is a collection
|
||||
if (this.isPrimitive()) this.castTo('structure');
|
||||
getChildNamed(childName: string): gdjs.Variable {
|
||||
const child = this._children[childName];
|
||||
if (child === undefined || child === null)
|
||||
return (this._children[childName] = new gdjs.Variable());
|
||||
|
||||
if (this._type === 'array')
|
||||
return this.getChildAt(parseInt(childName, 10) || 0);
|
||||
|
||||
if (
|
||||
this._children[childName] === undefined ||
|
||||
this._children[childName] === null
|
||||
)
|
||||
this._children[childName] = new gdjs.Variable();
|
||||
return this._children[childName];
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,8 +303,8 @@ namespace gdjs {
|
||||
* @returns The variable (for chaining calls)
|
||||
*/
|
||||
addChild(childName: string, childVariable: gdjs.Variable): this {
|
||||
// Make sure this is a structure
|
||||
this.castTo('structure');
|
||||
if (this._type !== 'structure') this.castTo('structure');
|
||||
|
||||
this._children[childName] = childVariable;
|
||||
return this;
|
||||
}
|
||||
@@ -393,6 +410,21 @@ namespace gdjs {
|
||||
this._str = '' + newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the variable, as a number if it's one,
|
||||
* or as a string (if it's a string or something else)
|
||||
*
|
||||
* In most cases, prefer calling `getAsNumber` or `getAsString` directly.
|
||||
* This is a fallback in case a variable type can't be known statically for sure,
|
||||
* like `getValue`.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
getAsNumberOrString(): number | string {
|
||||
if (this._type === 'number') return this._value;
|
||||
return this.getAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the variable, considered as a boolean
|
||||
* @return The boolean value of the variable.
|
||||
@@ -550,7 +582,8 @@ namespace gdjs {
|
||||
* Get a variable at a given index of the array.
|
||||
*/
|
||||
getChildAt(index: integer) {
|
||||
this.castTo('array');
|
||||
if (this._type !== 'array') this.castTo('array');
|
||||
|
||||
if (
|
||||
this._childrenArray[index] === undefined ||
|
||||
this._childrenArray[index] === null
|
||||
@@ -570,7 +603,8 @@ namespace gdjs {
|
||||
* Pushes a copy of a variable into the array.
|
||||
*/
|
||||
pushVariableCopy(variable: gdjs.Variable) {
|
||||
this.castTo('array');
|
||||
if (this._type !== 'array') this.castTo('array');
|
||||
|
||||
this._childrenArray.push(variable.clone());
|
||||
}
|
||||
|
||||
@@ -578,7 +612,8 @@ namespace gdjs {
|
||||
* Pushes a value into the array.
|
||||
*/
|
||||
pushValue(value: string | float | boolean) {
|
||||
this.castTo('array');
|
||||
if (this._type !== 'array') this.castTo('array');
|
||||
|
||||
this._childrenArray.push(
|
||||
new gdjs.Variable({
|
||||
type: typeof value as 'string' | 'number' | 'boolean',
|
||||
|
||||
@@ -244,6 +244,7 @@ namespace gdjs {
|
||||
getValue: () => 0,
|
||||
getChild: () => gdjs.VariablesContainer.badVariable,
|
||||
getChildAt: () => gdjs.VariablesContainer.badVariable,
|
||||
getChildNamed: () => gdjs.VariablesContainer.badVariable,
|
||||
hasChild: function () {
|
||||
return false;
|
||||
},
|
||||
@@ -271,6 +272,9 @@ namespace gdjs {
|
||||
getAsNumber: function () {
|
||||
return 0;
|
||||
},
|
||||
getAsNumberOrString: function () {
|
||||
return 0;
|
||||
},
|
||||
getAsBoolean: function () {
|
||||
return false;
|
||||
},
|
||||
|
||||
Binary file not shown.
BIN
Binary file not shown.
|
After Width: | Height: | Size: 897 B |
Binary file not shown.
|
After Width: | Height: | Size: 877 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 878 B |
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@ describe('gdjs.Variable', function () {
|
||||
|
||||
expect(intVar.getAsNumber()).to.be(526);
|
||||
expect(intVar.getAsString()).to.be('526');
|
||||
expect(intVar.getAsNumberOrString()).to.be(526);
|
||||
expect(intVar.getAsBoolean()).to.be(true);
|
||||
expect(intVar.getType()).to.be('number');
|
||||
|
||||
@@ -25,11 +26,13 @@ describe('gdjs.Variable', function () {
|
||||
|
||||
expect(floatVar.getAsNumber()).to.be(10.568);
|
||||
expect(floatVar.getAsString()).to.be('10.568');
|
||||
expect(floatVar.getAsNumberOrString()).to.be(10.568);
|
||||
expect(floatVar.getAsBoolean()).to.be(true);
|
||||
expect(floatVar.getType()).to.be('number');
|
||||
|
||||
expect(strVar.getAsNumber()).to.be(0);
|
||||
expect(strVar.getAsString()).to.be('test variable');
|
||||
expect(strVar.getAsNumberOrString()).to.be('test variable');
|
||||
expect(strVar.getAsBoolean()).to.be(true);
|
||||
expect(strVar.getType()).to.be('string');
|
||||
|
||||
@@ -45,12 +48,14 @@ describe('gdjs.Variable', function () {
|
||||
expect(numStrVar.getAsNumber()).to.be(5);
|
||||
expect(numStrVar.getAsString()).to.be('5Apples');
|
||||
expect(numStrVar.getAsBoolean()).to.be(true);
|
||||
expect(numStrVar.getAsNumberOrString()).to.be('5Apples');
|
||||
expect(numStrVar.getType()).to.be('string');
|
||||
|
||||
expect(boolVar.getType()).to.be('boolean');
|
||||
expect(boolVar.getAsString()).to.be('true');
|
||||
expect(boolVar.getAsNumber()).to.be(1);
|
||||
expect(boolVar.getAsBoolean()).to.be(true);
|
||||
expect(boolVar.getAsNumberOrString()).to.be('true');
|
||||
});
|
||||
|
||||
it('should do some variable arithmetics', function () {
|
||||
@@ -66,6 +71,7 @@ describe('gdjs.Variable', function () {
|
||||
expect(a.getAsNumber()).to.be(3);
|
||||
a.concatenateString('Apples');
|
||||
expect(a.getAsString()).to.be('3Apples');
|
||||
expect(a.getAsNumberOrString()).to.be('3Apples');
|
||||
});
|
||||
|
||||
it('should clear a collection', function () {
|
||||
|
||||
@@ -2600,7 +2600,7 @@ interface ExpressionNodeLocationFinder {
|
||||
};
|
||||
|
||||
interface ExpressionTypeFinder {
|
||||
[Const, Ref] DOMString STATIC_GetType([Const, Ref] Platform platform, [Const, Ref] ObjectsContainersList objectsContainersList, [Const] DOMString rootType, [Ref] ExpressionNode node);
|
||||
[Const, Ref] DOMString STATIC_GetType([Const, Ref] Platform platform, [Const, Ref] ProjectScopedContainers projectScopedContainers, [Const] DOMString rootType, [Ref] ExpressionNode node);
|
||||
};
|
||||
|
||||
interface ExpressionNode {
|
||||
|
||||
@@ -191,6 +191,10 @@ class Variable {
|
||||
this.setString(this.getAsString() + str);
|
||||
}
|
||||
|
||||
getAsNumberOrString() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
addChild(childName, childVariable) {
|
||||
// Make sure this is a structure
|
||||
this.castTo('structure');
|
||||
|
||||
@@ -171,109 +171,198 @@ describe('libGD.js - GDJS Scene Code Generation integration tests', function ()
|
||||
project.delete();
|
||||
});
|
||||
|
||||
// TODO: this does not pass because of string variables not understood as strings inside brackets.
|
||||
// it('generates code for scene variables, including structures being accessed by other variables', function () {
|
||||
// const project = new gd.ProjectHelper.createNewGDJSProject();
|
||||
// const layout = project.insertNewLayout('Scene', 0);
|
||||
// layout.insertNewObject(project, '', 'MyObject', 0);
|
||||
// layout.getVariables().insertNew('MyNumberVariable', 0).setValue(123);
|
||||
// layout.getVariables().insertNew('MyStringVariable', 1).setString('Test');
|
||||
// layout
|
||||
// .getVariables()
|
||||
// .insertNew('MyOtherStringVariable', 1)
|
||||
// .setString('SomeChild');
|
||||
// layout
|
||||
// .getVariables()
|
||||
// .insertNew('MyOtherStringVariable2', 1)
|
||||
// .setString('SomeOtherChild');
|
||||
// const structureVariable = layout
|
||||
// .getVariables()
|
||||
// .insertNew('MyStructureVariable', 2);
|
||||
// structureVariable.getChild('Test').setValue(1);
|
||||
// structureVariable.getChild('123').setValue(2);
|
||||
// structureVariable.getChild('42').setValue(4);
|
||||
// structureVariable.getChild('MyObject').setValue(8);
|
||||
// structureVariable.getChild('SomeChild').getChild('Test').setValue(16);
|
||||
// structureVariable.getChild('SomeChild').getChild('123').setValue(32);
|
||||
// structureVariable.getChild('SomeChild').getChild('42').setValue(64);
|
||||
// structureVariable.getChild('SomeChild').getChild('MyObject').setValue(128);
|
||||
// structureVariable
|
||||
// .getChild('SomeChild')
|
||||
// .getChild('SomeOtherChild')
|
||||
// .getChild('Test')
|
||||
// .setValue(256);
|
||||
// structureVariable
|
||||
// .getChild('SomeChild')
|
||||
// .getChild('SomeOtherChild')
|
||||
// .getChild('123')
|
||||
// .setValue(512);
|
||||
// structureVariable
|
||||
// .getChild('SomeChild')
|
||||
// .getChild('SomeOtherChild')
|
||||
// .getChild('42')
|
||||
// .setValue(1024);
|
||||
// structureVariable
|
||||
// .getChild('SomeChild')
|
||||
// .getChild('SomeOtherChild')
|
||||
// .getChild('MyObject')
|
||||
// .setValue(2048);
|
||||
// const serializedLayoutEvents = gd.Serializer.fromJSObject([
|
||||
// {
|
||||
// type: 'BuiltinCommonInstructions::Standard',
|
||||
// conditions: [],
|
||||
// actions: [
|
||||
// {
|
||||
// type: { value: 'ModVarScene' },
|
||||
// parameters: [
|
||||
// 'Counter',
|
||||
// '+',
|
||||
// 'MyStructureVariable[MyStringVariable] + MyStructureVariable[MyNumberVariable] + MyStructureVariable[MyObject.X()] + MyStructureVariable[MyObject.ObjectName()]',
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// type: { value: 'ModVarScene' },
|
||||
// parameters: [
|
||||
// 'Counter',
|
||||
// '+',
|
||||
// 'MyStructureVariable.SomeChild[MyStringVariable] + MyStructureVariable.SomeChild[MyNumberVariable] + MyStructureVariable.SomeChild[MyObject.X()] + MyStructureVariable.SomeChild[MyObject.ObjectName()]',
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// type: { value: 'ModVarScene' },
|
||||
// parameters: [
|
||||
// 'Counter',
|
||||
// '+',
|
||||
// 'MyStructureVariable[MyOtherStringVariable][MyOtherStringVariable2][MyStringVariable] +MyStructureVariable[MyOtherStringVariable][MyOtherStringVariable2][MyNumberVariable] + MyStructureVariable[MyOtherStringVariable][MyOtherStringVariable2][MyObject.X()] + MyStructureVariable[MyOtherStringVariable][MyOtherStringVariable2][MyObject.ObjectName()]',
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// events: [],
|
||||
// },
|
||||
// ]);
|
||||
// layout.getEvents().unserializeFrom(project, serializedLayoutEvents);
|
||||
describe('(Scene) variable code generation', () => {
|
||||
let project = null;
|
||||
let layout = null;
|
||||
beforeEach(() => {
|
||||
project = new gd.ProjectHelper.createNewGDJSProject();
|
||||
layout = project.insertNewLayout('Scene', 0);
|
||||
layout.insertNewObject(project, '', 'MyObject', 0);
|
||||
|
||||
// const runCompiledEvents = generateCompiledEventsForLayout(
|
||||
// gd,
|
||||
// project,
|
||||
// layout
|
||||
// );
|
||||
// These variables are "simple" and their type will be known at code generation.
|
||||
layout.getVariables().insertNew('MyNumberVariable', 0).setValue(123);
|
||||
layout.getVariables().insertNew('MyStringVariable', 1).setString('Test');
|
||||
layout
|
||||
.getVariables()
|
||||
.insertNew('MyOtherStringVariable', 1)
|
||||
.setString('SomeChild');
|
||||
|
||||
// const serializedSceneElement = new gd.SerializerElement();
|
||||
// layout.serializeTo(serializedSceneElement);
|
||||
// Use a variable that has a value deep inside a structure - so code generation
|
||||
// does not know its type and will issue a `getAsNumberOrString`.
|
||||
layout
|
||||
.getVariables()
|
||||
.insertNew('MyOtherStructureVariable', 1)
|
||||
.getChild('Child')
|
||||
.getChild('SubChild')
|
||||
.setString('SomeOtherChild');
|
||||
const structureVariable = layout
|
||||
.getVariables()
|
||||
.insertNew('MyStructureVariable', 2);
|
||||
|
||||
// const { gdjs, runtimeScene } = makeMinimalGDJSMock({
|
||||
// sceneData: JSON.parse(gd.Serializer.toJSON(serializedSceneElement)),
|
||||
// });
|
||||
// serializedSceneElement.delete();
|
||||
// const myObjectInstance = runtimeScene.createObject('MyObject');
|
||||
// myObjectInstance.setX(42);
|
||||
// runCompiledEvents(gdjs, runtimeScene);
|
||||
// Make a structure that we will address using other variables.
|
||||
structureVariable.getChild('Test').setValue(1);
|
||||
structureVariable.getChild('123').setValue(2);
|
||||
structureVariable.getChild('42').setValue(4);
|
||||
structureVariable.getChild('MyObject').setValue(8);
|
||||
structureVariable.getChild('SomeChild').getChild('Test').setValue(16);
|
||||
structureVariable.getChild('SomeChild').getChild('123').setValue(32);
|
||||
structureVariable.getChild('SomeChild').getChild('42').setValue(64);
|
||||
structureVariable
|
||||
.getChild('SomeChild')
|
||||
.getChild('MyObject')
|
||||
.setValue(128);
|
||||
structureVariable
|
||||
.getChild('SomeChild')
|
||||
.getChild('SomeOtherChild')
|
||||
.getChild('Test')
|
||||
.setValue(256);
|
||||
structureVariable
|
||||
.getChild('SomeChild')
|
||||
.getChild('SomeOtherChild')
|
||||
.getChild('123')
|
||||
.setValue(512);
|
||||
structureVariable
|
||||
.getChild('SomeChild')
|
||||
.getChild('SomeOtherChild')
|
||||
.getChild('42')
|
||||
.setValue(1024);
|
||||
structureVariable
|
||||
.getChild('SomeChild')
|
||||
.getChild('SomeOtherChild')
|
||||
.getChild('MyObject')
|
||||
.setValue(2048);
|
||||
});
|
||||
afterEach(() => {
|
||||
project.delete();
|
||||
});
|
||||
|
||||
// expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
// expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(
|
||||
// 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512 + 1024 + 2048
|
||||
// );
|
||||
it('generates code for structure variables accessed by another variable (1 level)', function () {
|
||||
const serializedLayoutEvents = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: [
|
||||
'Counter',
|
||||
'+',
|
||||
'MyStructureVariable[MyStringVariable] + MyStructureVariable[MyNumberVariable] + MyStructureVariable[MyObject.X()] + MyStructureVariable[MyObject.ObjectName()]',
|
||||
],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
]);
|
||||
layout.getEvents().unserializeFrom(project, serializedLayoutEvents);
|
||||
|
||||
// project.delete();
|
||||
// });
|
||||
const runCompiledEvents = generateCompiledEventsForLayout(
|
||||
gd,
|
||||
project,
|
||||
layout
|
||||
);
|
||||
|
||||
const serializedSceneElement = new gd.SerializerElement();
|
||||
layout.serializeTo(serializedSceneElement);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock({
|
||||
sceneData: JSON.parse(gd.Serializer.toJSON(serializedSceneElement)),
|
||||
});
|
||||
serializedSceneElement.delete();
|
||||
const myObjectInstance = runtimeScene.createObject('MyObject');
|
||||
myObjectInstance.setX(42);
|
||||
runCompiledEvents(gdjs, runtimeScene);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(
|
||||
1 + 2 + 4 + 8
|
||||
);
|
||||
});
|
||||
|
||||
it('generates code for structure variables accessed by another variable (2 levels)', function () {
|
||||
const serializedLayoutEvents = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: [
|
||||
'Counter',
|
||||
'+',
|
||||
'MyStructureVariable.SomeChild[MyStringVariable] + MyStructureVariable.SomeChild[MyNumberVariable] + MyStructureVariable.SomeChild[MyObject.X()] + MyStructureVariable.SomeChild[MyObject.ObjectName()]',
|
||||
],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
]);
|
||||
layout.getEvents().unserializeFrom(project, serializedLayoutEvents);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsForLayout(
|
||||
gd,
|
||||
project,
|
||||
layout
|
||||
);
|
||||
|
||||
const serializedSceneElement = new gd.SerializerElement();
|
||||
layout.serializeTo(serializedSceneElement);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock({
|
||||
sceneData: JSON.parse(gd.Serializer.toJSON(serializedSceneElement)),
|
||||
});
|
||||
serializedSceneElement.delete();
|
||||
const myObjectInstance = runtimeScene.createObject('MyObject');
|
||||
myObjectInstance.setX(42);
|
||||
runCompiledEvents(gdjs, runtimeScene);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(
|
||||
16 + 32 + 64 + 128
|
||||
);
|
||||
});
|
||||
|
||||
it('generates code for structure variables accessed by another variable (3 levels)', function () {
|
||||
const serializedLayoutEvents = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: [
|
||||
'Counter',
|
||||
'+',
|
||||
'MyStructureVariable[MyOtherStringVariable][MyOtherStructureVariable.Child.SubChild][MyStringVariable] +MyStructureVariable[MyOtherStringVariable][MyOtherStructureVariable.Child.SubChild][MyNumberVariable] + MyStructureVariable[MyOtherStringVariable][MyOtherStructureVariable.Child.SubChild][MyObject.X()] + MyStructureVariable[MyOtherStringVariable][MyOtherStructureVariable.Child.SubChild][MyObject.ObjectName()]',
|
||||
],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
]);
|
||||
layout.getEvents().unserializeFrom(project, serializedLayoutEvents);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsForLayout(
|
||||
gd,
|
||||
project,
|
||||
layout
|
||||
);
|
||||
|
||||
const serializedSceneElement = new gd.SerializerElement();
|
||||
layout.serializeTo(serializedSceneElement);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock({
|
||||
sceneData: JSON.parse(gd.Serializer.toJSON(serializedSceneElement)),
|
||||
});
|
||||
serializedSceneElement.delete();
|
||||
const myObjectInstance = runtimeScene.createObject('MyObject');
|
||||
myObjectInstance.setX(42);
|
||||
runCompiledEvents(gdjs, runtimeScene);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(
|
||||
256 + 512 + 1024 + 2048
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdExpressionTypeFinder {
|
||||
static getType(platform: gdPlatform, objectsContainersList: gdObjectsContainersList, rootType: string, node: gdExpressionNode): string;
|
||||
static getType(platform: gdPlatform, projectScopedContainers: gdProjectScopedContainers, rootType: string, node: gdExpressionNode): string;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
||||
@@ -273,6 +273,7 @@ export default class ExpressionField extends React.Component<Props, State> {
|
||||
const {
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
scope,
|
||||
expressionType,
|
||||
value,
|
||||
} = this.props;
|
||||
@@ -296,12 +297,14 @@ export default class ExpressionField extends React.Component<Props, State> {
|
||||
expressionNode,
|
||||
cursorPosition + 'fakeIdentifier'.length - 1
|
||||
);
|
||||
const projectScopedContainers = getProjectScopedContainersFromScope(
|
||||
scope,
|
||||
globalObjectsContainer,
|
||||
objectsContainer
|
||||
);
|
||||
const type = gd.ExpressionTypeFinder.getType(
|
||||
gd.JsPlatform.get(),
|
||||
gd.ObjectsContainersList.makeNewObjectsContainersListForContainers(
|
||||
globalObjectsContainer,
|
||||
objectsContainer
|
||||
),
|
||||
projectScopedContainers,
|
||||
expressionType,
|
||||
currentNode
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user