mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
77 Commits
40c576bc2d
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
![]() |
696b9f66fd | ||
![]() |
170a3ca9c2 | ||
![]() |
f851dcff3a | ||
![]() |
b5a3160871 | ||
![]() |
2e659ae47b | ||
![]() |
2cd809dc4e | ||
![]() |
b13e759748 | ||
![]() |
7dfc57a7ba | ||
![]() |
cb16de09e8 | ||
![]() |
5771bea1a7 | ||
![]() |
a0ae2e5c73 | ||
![]() |
f232322fcd | ||
![]() |
59ac34da02 | ||
![]() |
f0bebcd5d6 | ||
![]() |
92cb7f631a | ||
![]() |
20e85222bb | ||
![]() |
cf586dd491 | ||
![]() |
53d9350071 | ||
![]() |
dce3205162 | ||
![]() |
895ee9ade8 | ||
![]() |
290459a294 | ||
![]() |
04db905385 | ||
![]() |
759bca8a80 | ||
![]() |
75ff641cf9 | ||
![]() |
ad43e4b1dc | ||
![]() |
319e8d47ba | ||
![]() |
774824dae9 | ||
![]() |
085d12f70c | ||
![]() |
dc98a7c143 | ||
![]() |
8002d318f4 | ||
![]() |
7631f3a841 | ||
![]() |
105678b85e | ||
![]() |
17d806a5f2 | ||
![]() |
f666142e31 | ||
![]() |
578793348b | ||
![]() |
2d032fd152 | ||
![]() |
3a49ccbad4 | ||
![]() |
a9837c20df | ||
![]() |
54b8a7e813 | ||
![]() |
043b980ee4 | ||
![]() |
4bc9a2af1e | ||
![]() |
662d4c744e | ||
![]() |
41a8e3194e | ||
![]() |
b6713bd070 | ||
![]() |
3f50cd7ebc | ||
![]() |
bca98bfeb7 | ||
![]() |
48a7a4da6a | ||
![]() |
f8790f7668 | ||
![]() |
9739e42622 | ||
![]() |
697a4fd842 | ||
![]() |
dfb55bd17a | ||
![]() |
2fb8978b22 | ||
![]() |
fae04b0ff3 | ||
![]() |
bf8b7cc939 | ||
![]() |
4c18a2fea3 | ||
![]() |
8bc4734637 | ||
![]() |
70058a09f6 | ||
![]() |
c13a160236 | ||
![]() |
6a0e9b965c | ||
![]() |
4c4a8a4e18 | ||
![]() |
a2a3c57859 | ||
![]() |
91e9d781d8 | ||
![]() |
27fd26e449 | ||
![]() |
671114ed72 | ||
![]() |
413eab7e35 | ||
![]() |
b92d587e05 | ||
![]() |
6559e0c3ad | ||
![]() |
4227775b94 | ||
![]() |
326c9b2e01 | ||
![]() |
571f504368 | ||
![]() |
3db47a5219 | ||
![]() |
67380df6c8 | ||
![]() |
b37609bd11 | ||
![]() |
d3857c8701 | ||
![]() |
5af6c71e74 | ||
![]() |
4cfc51d34f | ||
![]() |
fb2376f939 |
@@ -658,7 +658,7 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
const gd::String& parameter,
|
||||
const gd::Expression& parameter,
|
||||
const gd::ParameterMetadata& metadata,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& lastObjectName,
|
||||
@@ -668,19 +668,20 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
|
||||
if (ParameterMetadata::IsExpression("number", metadata.type)) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, "number", parameter);
|
||||
*this, context, "number", parameter, lastObjectName);
|
||||
} else if (ParameterMetadata::IsExpression("string", metadata.type)) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, "string", parameter);
|
||||
*this, context, "string", parameter, lastObjectName);
|
||||
} else if (ParameterMetadata::IsExpression("variable", metadata.type)) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, metadata.type, parameter, lastObjectName);
|
||||
} else if (ParameterMetadata::IsObject(metadata.type)) {
|
||||
// It would be possible to run a gd::ExpressionCodeGenerator if later
|
||||
// objects can have nested objects, or function returning objects.
|
||||
argOutput = GenerateObject(parameter, metadata.type, context);
|
||||
argOutput = GenerateObject(parameter.GetPlainString(), metadata.type, context);
|
||||
} else if (metadata.type == "relationalOperator") {
|
||||
argOutput += parameter == "=" ? "==" : parameter;
|
||||
auto parameterString = parameter.GetPlainString();
|
||||
argOutput += parameterString == "=" ? "==" : parameterString;
|
||||
if (argOutput != "==" && argOutput != "<" && argOutput != ">" &&
|
||||
argOutput != "<=" && argOutput != ">=" && argOutput != "!=") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
@@ -689,7 +690,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
|
||||
argOutput = "\"" + argOutput + "\"";
|
||||
} else if (metadata.type == "operator") {
|
||||
argOutput += parameter;
|
||||
argOutput += parameter.GetPlainString();
|
||||
if (argOutput != "=" && argOutput != "+" && argOutput != "-" &&
|
||||
argOutput != "/" && argOutput != "*") {
|
||||
cout << "Warning: Bad operator: Set to = by default." << endl;
|
||||
@@ -698,9 +699,9 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
|
||||
argOutput = "\"" + argOutput + "\"";
|
||||
} else if (ParameterMetadata::IsBehavior(metadata.type)) {
|
||||
argOutput = GenerateGetBehaviorNameCode(parameter);
|
||||
argOutput = GenerateGetBehaviorNameCode(parameter.GetPlainString());
|
||||
} else if (metadata.type == "key") {
|
||||
argOutput = "\"" + ConvertToString(parameter) + "\"";
|
||||
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
|
||||
} else if (metadata.type == "audioResource" ||
|
||||
metadata.type == "bitmapFontResource" ||
|
||||
metadata.type == "fontResource" ||
|
||||
@@ -710,15 +711,17 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
// Deprecated, old parameter names:
|
||||
metadata.type == "password" || metadata.type == "musicfile" ||
|
||||
metadata.type == "soundfile" || metadata.type == "police") {
|
||||
argOutput = "\"" + ConvertToString(parameter) + "\"";
|
||||
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
|
||||
} else if (metadata.type == "mouse") {
|
||||
argOutput = "\"" + ConvertToString(parameter) + "\"";
|
||||
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
|
||||
} else if (metadata.type == "yesorno") {
|
||||
argOutput += (parameter == "yes" || parameter == "oui") ? GenerateTrue()
|
||||
auto parameterString = parameter.GetPlainString();
|
||||
argOutput += (parameterString == "yes" || parameterString == "oui") ? GenerateTrue()
|
||||
: GenerateFalse();
|
||||
} else if (metadata.type == "trueorfalse") {
|
||||
auto parameterString = parameter.GetPlainString();
|
||||
// This is duplicated in AdvancedExtension.cpp for GDJS
|
||||
argOutput += (parameter == "True" || parameter == "Vrai") ? GenerateTrue()
|
||||
argOutput += (parameterString == "True" || parameterString == "Vrai") ? GenerateTrue()
|
||||
: GenerateFalse();
|
||||
}
|
||||
// Code only parameter type
|
||||
@@ -738,7 +741,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
if (!metadata.type.empty())
|
||||
cout << "Warning: Unknown type of parameter \"" << metadata.type
|
||||
<< "\"." << std::endl;
|
||||
argOutput += "\"" + ConvertToString(parameter) + "\"";
|
||||
argOutput += "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,7 +761,7 @@ vector<gd::String> EventsCodeGenerator::GenerateParametersCodes(
|
||||
parametersInfo,
|
||||
[this, &context, &supplementaryParametersTypes, &arguments](
|
||||
const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterValue,
|
||||
const gd::String& lastObjectName) {
|
||||
gd::String argOutput =
|
||||
GenerateParameterCodes(parameterValue,
|
||||
@@ -1243,7 +1246,7 @@ gd::String EventsCodeGenerator::GenerateArgumentsList(
|
||||
return argumentsStr;
|
||||
}
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(gd::Project& project_,
|
||||
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
const gd::Layout& layout,
|
||||
const gd::Platform& platform_)
|
||||
: platform(platform_),
|
||||
@@ -1260,7 +1263,7 @@ EventsCodeGenerator::EventsCodeGenerator(gd::Project& project_,
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(
|
||||
const gd::Platform& platform_,
|
||||
gd::ObjectsContainer& globalObjectsAndGroups_,
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups_,
|
||||
const gd::ObjectsContainer& objectsAndGroups_)
|
||||
: platform(platform_),
|
||||
globalObjectsAndGroups(globalObjectsAndGroups_),
|
||||
|
@@ -48,7 +48,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* \brief Construct a code generator for the specified
|
||||
* platform/project/layout.
|
||||
*/
|
||||
EventsCodeGenerator(gd::Project& project_,
|
||||
EventsCodeGenerator(const gd::Project& project_,
|
||||
const gd::Layout& layout,
|
||||
const gd::Platform& platform_);
|
||||
|
||||
@@ -57,7 +57,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* objects/groups and platform
|
||||
*/
|
||||
EventsCodeGenerator(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& globalObjectsAndGroups_,
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups_,
|
||||
const gd::ObjectsContainer& objectsAndGroups_);
|
||||
virtual ~EventsCodeGenerator(){};
|
||||
|
||||
@@ -327,7 +327,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
/**
|
||||
* \brief Get the global objects/groups used for code generation.
|
||||
*/
|
||||
gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
|
||||
const gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
|
||||
return globalObjectsAndGroups;
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* \brief Get the project the code is being generated for.
|
||||
* \warning This is only valid if HasProjectAndLayout() is true.
|
||||
*/
|
||||
gd::Project& GetProject() const { return *project; }
|
||||
const gd::Project& GetProject() const { return *project; }
|
||||
|
||||
/**
|
||||
* \brief Get the layout the code is being generated for.
|
||||
@@ -517,7 +517,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* \endcode
|
||||
*/
|
||||
virtual gd::String GenerateParameterCodes(
|
||||
const gd::String& parameter,
|
||||
const gd::Expression& parameter,
|
||||
const gd::ParameterMetadata& metadata,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& lastObjectName,
|
||||
@@ -770,12 +770,12 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
|
||||
const gd::Platform& platform; ///< The platform being used.
|
||||
|
||||
gd::ObjectsContainer& globalObjectsAndGroups;
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups;
|
||||
const gd::ObjectsContainer& objectsAndGroups;
|
||||
|
||||
bool hasProjectAndLayout; ///< true only if project and layout are valid
|
||||
///< references. If false, they should not be used.
|
||||
gd::Project* project; ///< The project being used.
|
||||
const gd::Project* project; ///< The project being used.
|
||||
const gd::Layout* scene; ///< The scene being generated.
|
||||
|
||||
bool errorOccurred; ///< Must be set to true if an error occured.
|
||||
|
@@ -25,36 +25,38 @@
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
EventsCodeGenerator& codeGenerator,
|
||||
EventsCodeGenerationContext& context,
|
||||
const gd::String& type,
|
||||
const gd::String& expression,
|
||||
const gd::String& objectName) {
|
||||
gd::ExpressionParser2 parser(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups());
|
||||
ExpressionCodeGenerator generator(codeGenerator, context);
|
||||
const gd::String& rootType,
|
||||
const gd::Expression& expression,
|
||||
const gd::String& rootObjectName) {
|
||||
ExpressionCodeGenerator generator(rootType, rootObjectName, codeGenerator, context);
|
||||
|
||||
auto node = parser.ParseExpression(type, expression, objectName);
|
||||
auto node = expression.GetRootNode();
|
||||
if (!node) {
|
||||
std::cout << "Error: error while parsing: \"" << expression << "\" ("
|
||||
<< type << ")" << std::endl;
|
||||
std::cout << "Error: error while parsing: \"" << expression.GetPlainString()
|
||||
<< "\" (" << rootType << ")" << std::endl;
|
||||
|
||||
return generator.GenerateDefaultValue(type);
|
||||
return generator.GenerateDefaultValue(rootType);
|
||||
}
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
gd::ExpressionValidator validator(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootType);
|
||||
node->Visit(validator);
|
||||
if (!validator.GetErrors().empty()) {
|
||||
std::cout << "Error: \"" << validator.GetErrors()[0]->GetMessage()
|
||||
<< "\" in: \"" << expression << "\" (" << type << ")"
|
||||
<< std::endl;
|
||||
<< "\" in: \"" << expression.GetPlainString() << "\" ("
|
||||
<< rootType << ")" << std::endl;
|
||||
|
||||
return generator.GenerateDefaultValue(type);
|
||||
return generator.GenerateDefaultValue(rootType);
|
||||
}
|
||||
|
||||
node->Visit(generator);
|
||||
@@ -97,15 +99,24 @@ void ExpressionCodeGenerator::OnVisitTextNode(TextNode& node) {
|
||||
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.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootType,
|
||||
node);
|
||||
EventsCodeGenerator::VariableScope scope =
|
||||
node.type == "globalvar"
|
||||
type == "globalvar"
|
||||
? gd::EventsCodeGenerator::PROJECT_VARIABLE
|
||||
: ((node.type == "scenevar")
|
||||
: ((type == "scenevar")
|
||||
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
|
||||
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
|
||||
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootObjectName,
|
||||
node);
|
||||
output += codeGenerator.GenerateGetVariable(
|
||||
node.name, scope, context, node.objectName);
|
||||
node.name, scope, context, objectName);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
|
||||
@@ -117,7 +128,7 @@ void ExpressionCodeGenerator::OnVisitVariableAccessorNode(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) {
|
||||
ExpressionCodeGenerator generator(codeGenerator, context);
|
||||
ExpressionCodeGenerator generator("string", "", codeGenerator, context);
|
||||
node.expression->Visit(generator);
|
||||
output +=
|
||||
codeGenerator.GenerateVariableBracketAccessor(generator.GetOutput());
|
||||
@@ -125,39 +136,79 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
if (gd::ParameterMetadata::IsObject(node.type)) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootType,
|
||||
node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
output +=
|
||||
codeGenerator.GenerateObject(node.identifierName, node.type, context);
|
||||
} else {
|
||||
codeGenerator.GenerateObject(node.identifierName, type, context);
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
EventsCodeGenerator::VariableScope scope =
|
||||
type == "globalvar"
|
||||
? gd::EventsCodeGenerator::PROJECT_VARIABLE
|
||||
: ((type == "scenevar")
|
||||
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
|
||||
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
|
||||
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootObjectName,
|
||||
node);
|
||||
output += codeGenerator.GenerateGetVariable(
|
||||
node.identifierName, scope, context, objectName);
|
||||
if (!node.childIdentifierName.empty()) {
|
||||
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
|
||||
}
|
||||
} else if (node.childIdentifierName.empty()) {
|
||||
output += "/* Error during generation, unrecognized identifier type: " +
|
||||
codeGenerator.ConvertToString(node.type) + " with value " +
|
||||
codeGenerator.ConvertToString(type) + " with value " +
|
||||
codeGenerator.ConvertToString(node.identifierName) + " */ " +
|
||||
codeGenerator.ConvertToStringExplicit(node.identifierName);
|
||||
}
|
||||
else {
|
||||
// This is for function names that are put in IdentifierNode
|
||||
// because the type is needed to tell them appart from variables.
|
||||
output += GenerateDefaultValue(type);
|
||||
}
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(node.expressionMetadata)) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
node);
|
||||
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
output += "/* Error during generation, function not found: " +
|
||||
codeGenerator.ConvertToString(node.functionName) + " */ " +
|
||||
GenerateDefaultValue(node.type);
|
||||
GenerateDefaultValue(type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.objectName.empty()) {
|
||||
if (!node.behaviorName.empty()) {
|
||||
output += GenerateBehaviorFunctionCode(node.type,
|
||||
output += GenerateBehaviorFunctionCode(type,
|
||||
node.objectName,
|
||||
node.behaviorName,
|
||||
node.parameters,
|
||||
node.expressionMetadata);
|
||||
metadata);
|
||||
} else {
|
||||
output += GenerateObjectFunctionCode(
|
||||
node.type, node.objectName, node.parameters, node.expressionMetadata);
|
||||
type, node.objectName, node.parameters, metadata);
|
||||
}
|
||||
} else {
|
||||
output +=
|
||||
GenerateFreeFunctionCode(node.parameters, node.expressionMetadata);
|
||||
GenerateFreeFunctionCode(node.parameters, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,18 +356,21 @@ gd::String ExpressionCodeGenerator::GenerateParametersCodes(
|
||||
|
||||
auto& parameterMetadata = expressionMetadata.parameters[i];
|
||||
if (!parameterMetadata.IsCodeOnly()) {
|
||||
ExpressionCodeGenerator generator(codeGenerator, context);
|
||||
if (nonCodeOnlyParameterIndex < parameters.size()) {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootObjectName,
|
||||
*parameters[nonCodeOnlyParameterIndex].get());
|
||||
ExpressionCodeGenerator generator(parameterMetadata.GetType(), objectName, codeGenerator, context);
|
||||
parameters[nonCodeOnlyParameterIndex]->Visit(generator);
|
||||
parametersCode += generator.GetOutput();
|
||||
} else if (parameterMetadata.IsOptional()) {
|
||||
ExpressionCodeGenerator generator(parameterMetadata.GetType(), "", codeGenerator, context);
|
||||
// Optional parameters default value were not parsed at the time of the
|
||||
// expression parsing. Parse them now.
|
||||
ExpressionParser2 parser(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups());
|
||||
auto node = parser.ParseExpression(parameterMetadata.GetType(),
|
||||
parameterMetadata.GetDefaultValue());
|
||||
ExpressionParser2 parser;
|
||||
auto node = parser.ParseExpression(parameterMetadata.GetDefaultValue());
|
||||
|
||||
node->Visit(generator);
|
||||
parametersCode += generator.GetOutput();
|
||||
@@ -374,12 +428,22 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
output += GenerateDefaultValue(node.type);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(
|
||||
ObjectFunctionNameNode& node) {
|
||||
output += GenerateDefaultValue(node.type);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -35,9 +34,11 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionCodeGenerator(EventsCodeGenerator& codeGenerator_,
|
||||
ExpressionCodeGenerator(const gd::String &rootType_,
|
||||
const gd::String &rootObjectName_,
|
||||
EventsCodeGenerator& codeGenerator_,
|
||||
EventsCodeGenerationContext& context_)
|
||||
: codeGenerator(codeGenerator_), context(context_){};
|
||||
: rootType(rootType_), rootObjectName(rootObjectName_), codeGenerator(codeGenerator_), context(context_){};
|
||||
virtual ~ExpressionCodeGenerator(){};
|
||||
|
||||
/**
|
||||
@@ -57,7 +58,7 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
|
||||
static gd::String GenerateExpressionCode(EventsCodeGenerator& codeGenerator,
|
||||
EventsCodeGenerationContext& context,
|
||||
const gd::String& type,
|
||||
const gd::String& expression,
|
||||
const gd::Expression& expression,
|
||||
const gd::String& objectName = "");
|
||||
|
||||
const gd::String& GetOutput() { return output; };
|
||||
@@ -103,6 +104,8 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
|
||||
gd::String output;
|
||||
EventsCodeGenerator& codeGenerator;
|
||||
EventsCodeGenerationContext& context;
|
||||
const gd::String rootType;
|
||||
const gd::String rootObjectName;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
41
Core/GDCore/Events/Expression.cpp
Normal file
41
Core/GDCore/Events/Expression.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "GDCore/Events/Expression.h"
|
||||
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
Expression::Expression() : node(nullptr) {};
|
||||
|
||||
Expression::Expression(gd::String plainString_)
|
||||
: node(nullptr), plainString(plainString_) {};
|
||||
|
||||
Expression::Expression(const char* plainString_)
|
||||
: node(nullptr), plainString(plainString_) {};
|
||||
|
||||
Expression::Expression(const Expression& copy)
|
||||
: node(nullptr), plainString{copy.plainString} {};
|
||||
|
||||
Expression& Expression::operator=(const Expression& expression) {
|
||||
plainString = expression.plainString;
|
||||
node = nullptr;
|
||||
return *this;
|
||||
};
|
||||
|
||||
Expression::~Expression(){};
|
||||
|
||||
ExpressionNode* Expression::GetRootNode() const {
|
||||
if (!node) {
|
||||
gd::ExpressionParser2 parser = ExpressionParser2();
|
||||
node = std::move(parser.ParseExpression(plainString));
|
||||
}
|
||||
return node.get();
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -6,7 +6,15 @@
|
||||
|
||||
#ifndef GDCORE_EXPRESSION_H
|
||||
#define GDCORE_EXPRESSION_H
|
||||
|
||||
#include "GDCore/String.h"
|
||||
#include <memory>
|
||||
|
||||
namespace gd {
|
||||
class ExpressionParser2;
|
||||
class ObjectsContainer;
|
||||
struct ExpressionNode;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -24,32 +32,49 @@ class GD_CORE_API Expression {
|
||||
/**
|
||||
* \brief Construct an empty expression
|
||||
*/
|
||||
Expression(){};
|
||||
Expression();
|
||||
|
||||
/**
|
||||
* \brief Construct an expression from a string
|
||||
*/
|
||||
Expression(gd::String plainString_) : plainString(plainString_){};
|
||||
Expression(gd::String plainString_);
|
||||
|
||||
/**
|
||||
* \brief Construct an expression from a const char *
|
||||
*/
|
||||
Expression(const char* plainString_) : plainString(plainString_){};
|
||||
Expression(const char* plainString_);
|
||||
|
||||
/**
|
||||
* \brief Copy construct an expression.
|
||||
*/
|
||||
Expression(const Expression& copy);
|
||||
|
||||
/**
|
||||
* \brief Expression affectation overriding.
|
||||
*/
|
||||
Expression& operator=(const Expression& expression);
|
||||
|
||||
/**
|
||||
* \brief Get the plain string representing the expression
|
||||
*/
|
||||
inline const gd::String& GetPlainString() const { return plainString; };
|
||||
|
||||
/**
|
||||
* @brief Get the expression node.
|
||||
* @return std::unique_ptr<gd::ExpressionNode>
|
||||
*/
|
||||
gd::ExpressionNode* GetRootNode() const;
|
||||
|
||||
/**
|
||||
* \brief Mimics std::string::c_str
|
||||
*/
|
||||
inline const char* c_str() const { return plainString.c_str(); };
|
||||
|
||||
virtual ~Expression(){};
|
||||
virtual ~Expression();
|
||||
|
||||
private:
|
||||
gd::String plainString; ///< The expression string
|
||||
mutable std::unique_ptr<gd::ExpressionNode> node;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -26,132 +26,16 @@ namespace gd {
|
||||
|
||||
gd::String ExpressionParser2::NAMESPACE_SEPARATOR = "::";
|
||||
|
||||
ExpressionParser2::ExpressionParser2(
|
||||
const gd::Platform& platform_,
|
||||
const gd::ObjectsContainer& globalObjectsContainer_,
|
||||
const gd::ObjectsContainer& objectsContainer_)
|
||||
ExpressionParser2::ExpressionParser2()
|
||||
: expression(""),
|
||||
currentPosition(0),
|
||||
platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_) {}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* Return the minimum number of parameters, starting from a given parameter
|
||||
* (by convention, 1 for object functions and 2 for behavior functions).
|
||||
*/
|
||||
size_t GetMinimumParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
size_t initialParameterIndex) {
|
||||
size_t nb = 0;
|
||||
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
|
||||
if (!parameters[i].optional && !parameters[i].codeOnly) nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of parameters, starting from a given parameter
|
||||
* (by convention, 1 for object functions and 2 for behavior functions).
|
||||
*/
|
||||
size_t GetMaximumParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
size_t initialParameterIndex) {
|
||||
size_t nb = 0;
|
||||
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
|
||||
if (!parameters[i].codeOnly) nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ExpressionParserDiagnostic> ExpressionParser2::ValidateFunction(
|
||||
const gd::String& type,
|
||||
const gd::FunctionCallNode& function,
|
||||
size_t functionStartPosition) {
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(
|
||||
function.expressionMetadata)) {
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_function_name",
|
||||
_("Cannot find an expression with this name: ") +
|
||||
function.functionName + "\n" +
|
||||
_("Double check that you've not made any typo in the name."),
|
||||
functionStartPosition,
|
||||
GetCurrentPosition());
|
||||
}
|
||||
|
||||
// Validate the type of the function
|
||||
const gd::String& returnType = function.expressionMetadata.GetReturnType();
|
||||
if (returnType == "number") {
|
||||
if (type == "string")
|
||||
return RaiseTypeError(
|
||||
_("You tried to use an expression that returns a number, but a "
|
||||
"string is expected. Use `ToString` if you need to convert a "
|
||||
"number to a string."),
|
||||
functionStartPosition);
|
||||
else if (type != "number" && type != "number|string")
|
||||
return RaiseTypeError(_("You tried to use an expression that returns a "
|
||||
"number, but another type is expected:") +
|
||||
" " + type,
|
||||
functionStartPosition);
|
||||
} else if (returnType == "string") {
|
||||
if (type == "number")
|
||||
return RaiseTypeError(
|
||||
_("You tried to use an expression that returns a string, but a "
|
||||
"number is expected. Use `ToNumber` if you need to convert a "
|
||||
"string to a number."),
|
||||
functionStartPosition);
|
||||
else if (type != "string" && type != "number|string")
|
||||
return RaiseTypeError(_("You tried to use an expression that returns a "
|
||||
"string, but another type is expected:") +
|
||||
" " + type,
|
||||
functionStartPosition);
|
||||
} else {
|
||||
if (type != returnType)
|
||||
return RaiseTypeError(
|
||||
_("You tried to use an expression with the wrong return type:") + " " +
|
||||
returnType,
|
||||
functionStartPosition);
|
||||
}
|
||||
|
||||
// Validate parameters count
|
||||
size_t minParametersCount = GetMinimumParametersNumber(
|
||||
function.expressionMetadata.parameters,
|
||||
WrittenParametersFirstIndex(function.objectName, function.behaviorName));
|
||||
size_t maxParametersCount = GetMaximumParametersNumber(
|
||||
function.expressionMetadata.parameters,
|
||||
WrittenParametersFirstIndex(function.objectName, function.behaviorName));
|
||||
if (function.parameters.size() < minParametersCount ||
|
||||
function.parameters.size() > maxParametersCount) {
|
||||
gd::String expectedCountMessage =
|
||||
minParametersCount == maxParametersCount
|
||||
? _("The number of parameters must be exactly ") +
|
||||
gd::String::From(minParametersCount)
|
||||
: _("The number of parameters must be: ") +
|
||||
gd::String::From(minParametersCount) + "-" +
|
||||
gd::String::From(maxParametersCount);
|
||||
|
||||
if (function.parameters.size() < minParametersCount) {
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"too_few_parameters",
|
||||
"You have not entered enough parameters for the expression. " +
|
||||
expectedCountMessage,
|
||||
functionStartPosition,
|
||||
GetCurrentPosition());
|
||||
}
|
||||
}
|
||||
|
||||
return gd::make_unique<ExpressionParserDiagnostic>();
|
||||
}
|
||||
currentPosition(0) {}
|
||||
|
||||
std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
|
||||
size_t textStartPosition = GetCurrentPosition();
|
||||
SkipAllWhitespaces();
|
||||
if (!CheckIfChar(IsQuote)) {
|
||||
auto text = gd::make_unique<TextNode>("");
|
||||
// It can't happen.
|
||||
text->diagnostic =
|
||||
RaiseSyntaxError(_("A text must start with a double quote (\")."));
|
||||
text->location =
|
||||
|
@@ -40,9 +40,7 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API ExpressionParser2 {
|
||||
public:
|
||||
ExpressionParser2(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_);
|
||||
ExpressionParser2();
|
||||
virtual ~ExpressionParser2(){};
|
||||
|
||||
/**
|
||||
@@ -58,13 +56,11 @@ class GD_CORE_API ExpressionParser2 {
|
||||
* \return The node representing the expression as a parsed tree.
|
||||
*/
|
||||
std::unique_ptr<ExpressionNode> ParseExpression(
|
||||
const gd::String &type,
|
||||
const gd::String &expression_,
|
||||
const gd::String &objectName = "") {
|
||||
const gd::String &expression_) {
|
||||
expression = expression_;
|
||||
|
||||
currentPosition = 0;
|
||||
return Start(type, objectName);
|
||||
return Start();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,18 +84,16 @@ class GD_CORE_API ExpressionParser2 {
|
||||
* Each method is a part of the grammar.
|
||||
*/
|
||||
///@{
|
||||
std::unique_ptr<ExpressionNode> Start(const gd::String &type,
|
||||
const gd::String &objectName = "") {
|
||||
std::unique_ptr<ExpressionNode> Start() {
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
auto expression = Expression(type, objectName);
|
||||
|
||||
const gd::String &inferredType = expression->type;
|
||||
auto expression = Expression();
|
||||
|
||||
// Check for extra characters at the end of the expression
|
||||
if (!IsEndReached()) {
|
||||
auto op = gd::make_unique<OperatorNode>(inferredType, ' ');
|
||||
auto op = gd::make_unique<OperatorNode>(' ');
|
||||
op->leftHandSide = std::move(expression);
|
||||
op->rightHandSide = ReadUntilEnd("unknown");
|
||||
op->rightHandSide = ReadUntilEnd();
|
||||
op->rightHandSide->parent = op.get();
|
||||
|
||||
op->rightHandSide->diagnostic = RaiseSyntaxError(
|
||||
_("The expression has extra character at the end that should be "
|
||||
@@ -113,61 +107,49 @@ class GD_CORE_API ExpressionParser2 {
|
||||
return expression;
|
||||
}
|
||||
|
||||
std::unique_ptr<ExpressionNode> Expression(
|
||||
const gd::String &type, const gd::String &objectName = "") {
|
||||
std::unique_ptr<ExpressionNode> Expression() {
|
||||
SkipAllWhitespaces();
|
||||
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
std::unique_ptr<ExpressionNode> leftHandSide = Term(type, objectName);
|
||||
|
||||
const gd::String &inferredType = leftHandSide->type;
|
||||
std::unique_ptr<ExpressionNode> leftHandSide = Term();
|
||||
|
||||
SkipAllWhitespaces();
|
||||
|
||||
if (IsEndReached()) return leftHandSide;
|
||||
if (CheckIfChar(IsExpressionEndingChar)) return leftHandSide;
|
||||
if (CheckIfChar(IsExpressionOperator)) {
|
||||
auto op = gd::make_unique<OperatorNode>(inferredType, GetCurrentChar());
|
||||
auto op = gd::make_unique<OperatorNode>(GetCurrentChar());
|
||||
op->leftHandSide = std::move(leftHandSide);
|
||||
op->diagnostic = ValidateOperator(inferredType, GetCurrentChar());
|
||||
op->leftHandSide->parent = op.get();
|
||||
op->diagnostic = ValidateOperator(GetCurrentChar());
|
||||
SkipChar();
|
||||
op->rightHandSide = Expression(inferredType, objectName);
|
||||
op->rightHandSide = Expression();
|
||||
op->rightHandSide->parent = op.get();
|
||||
|
||||
op->location = ExpressionParserLocation(expressionStartPosition,
|
||||
GetCurrentPosition());
|
||||
return std::move(op);
|
||||
}
|
||||
|
||||
if (inferredType == "string") {
|
||||
leftHandSide->diagnostic = RaiseSyntaxError(
|
||||
"You must add the operator + between texts or expressions. For "
|
||||
"example: \"Your name: \" + VariableString(PlayerName).");
|
||||
} else if (inferredType == "number") {
|
||||
leftHandSide->diagnostic = RaiseSyntaxError(
|
||||
"No operator found. Did you forget to enter an operator (like +, -, "
|
||||
"* or /) between numbers or expressions?");
|
||||
} else {
|
||||
leftHandSide->diagnostic = RaiseSyntaxError(
|
||||
"More than one term was found. Verify that your expression is "
|
||||
"properly written.");
|
||||
}
|
||||
leftHandSide->diagnostic = RaiseSyntaxError(
|
||||
"More than one term was found. Verify that your expression is "
|
||||
"properly written.");
|
||||
|
||||
auto op = gd::make_unique<OperatorNode>(inferredType, ' ');
|
||||
auto op = gd::make_unique<OperatorNode>(' ');
|
||||
op->leftHandSide = std::move(leftHandSide);
|
||||
op->rightHandSide = Expression(inferredType, objectName);
|
||||
op->leftHandSide->parent = op.get();
|
||||
op->rightHandSide = Expression();
|
||||
op->rightHandSide->parent = op.get();
|
||||
op->location =
|
||||
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
|
||||
return std::move(op);
|
||||
}
|
||||
|
||||
std::unique_ptr<ExpressionNode> Term(const gd::String &type,
|
||||
const gd::String &objectName) {
|
||||
std::unique_ptr<ExpressionNode> Term() {
|
||||
SkipAllWhitespaces();
|
||||
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
std::unique_ptr<ExpressionNode> factor = Factor(type, objectName);
|
||||
|
||||
const gd::String &inferredType = factor->type;
|
||||
std::unique_ptr<ExpressionNode> factor = Factor();
|
||||
|
||||
SkipAllWhitespaces();
|
||||
|
||||
@@ -175,11 +157,13 @@ class GD_CORE_API ExpressionParser2 {
|
||||
// to guarantee the proper operator precedence. (Expression could also
|
||||
// be reworked to use a while loop).
|
||||
while (CheckIfChar(IsTermOperator)) {
|
||||
auto op = gd::make_unique<OperatorNode>(inferredType, GetCurrentChar());
|
||||
auto op = gd::make_unique<OperatorNode>(GetCurrentChar());
|
||||
op->leftHandSide = std::move(factor);
|
||||
op->diagnostic = ValidateOperator(inferredType, GetCurrentChar());
|
||||
op->leftHandSide->parent = op.get();
|
||||
op->diagnostic = ValidateOperator(GetCurrentChar());
|
||||
SkipChar();
|
||||
op->rightHandSide = Factor(inferredType, objectName);
|
||||
op->rightHandSide = Factor();
|
||||
op->rightHandSide->parent = op.get();
|
||||
op->location = ExpressionParserLocation(expressionStartPosition,
|
||||
GetCurrentPosition());
|
||||
SkipAllWhitespaces();
|
||||
@@ -190,54 +174,35 @@ class GD_CORE_API ExpressionParser2 {
|
||||
return factor;
|
||||
};
|
||||
|
||||
std::unique_ptr<ExpressionNode> Factor(const gd::String &type,
|
||||
const gd::String &objectName) {
|
||||
std::unique_ptr<ExpressionNode> Factor() {
|
||||
SkipAllWhitespaces();
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
|
||||
if (CheckIfChar(IsQuote)) {
|
||||
std::unique_ptr<ExpressionNode> factor = ReadText();
|
||||
if (type == "number")
|
||||
factor->diagnostic =
|
||||
RaiseTypeError(_("You entered a text, but a number was expected."),
|
||||
expressionStartPosition);
|
||||
else if (type != "string" && type != "number|string")
|
||||
factor->diagnostic = RaiseTypeError(
|
||||
_("You entered a text, but this type was expected:") + type,
|
||||
expressionStartPosition);
|
||||
|
||||
return factor;
|
||||
} else if (CheckIfChar(IsUnaryOperator)) {
|
||||
auto unaryOperatorCharacter = GetCurrentChar();
|
||||
SkipChar();
|
||||
|
||||
auto operatorOperand = Factor(type, objectName);
|
||||
const gd::String &inferredType = operatorOperand->type;
|
||||
auto operatorOperand = Factor();
|
||||
|
||||
auto unaryOperator = gd::make_unique<UnaryOperatorNode>(
|
||||
inferredType, unaryOperatorCharacter);
|
||||
unaryOperatorCharacter);
|
||||
unaryOperator->diagnostic = ValidateUnaryOperator(
|
||||
inferredType, unaryOperatorCharacter, expressionStartPosition);
|
||||
unaryOperatorCharacter, expressionStartPosition);
|
||||
unaryOperator->factor = std::move(operatorOperand);
|
||||
unaryOperator->factor->parent = unaryOperator.get();
|
||||
unaryOperator->location = ExpressionParserLocation(
|
||||
expressionStartPosition, GetCurrentPosition());
|
||||
|
||||
return std::move(unaryOperator);
|
||||
} else if (CheckIfChar(IsNumberFirstChar)) {
|
||||
std::unique_ptr<ExpressionNode> factor = ReadNumber();
|
||||
if (type == "string")
|
||||
factor->diagnostic = RaiseTypeError(
|
||||
_("You entered a number, but a text was expected (in quotes)."),
|
||||
expressionStartPosition);
|
||||
else if (type != "number" && type != "number|string")
|
||||
factor->diagnostic = RaiseTypeError(
|
||||
_("You entered a number, but this type was expected:") + type,
|
||||
expressionStartPosition);
|
||||
|
||||
return factor;
|
||||
} else if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
SkipChar();
|
||||
std::unique_ptr<ExpressionNode> factor = SubExpression(type, objectName);
|
||||
std::unique_ptr<ExpressionNode> factor = SubExpression();
|
||||
|
||||
if (!CheckIfChar(IsClosingParenthesis)) {
|
||||
factor->diagnostic =
|
||||
@@ -247,29 +212,20 @@ class GD_CORE_API ExpressionParser2 {
|
||||
SkipIfChar(IsClosingParenthesis);
|
||||
return factor;
|
||||
} else if (IsIdentifierAllowedChar()) {
|
||||
// This is a place where the grammar differs according to the
|
||||
// type being expected.
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
return Variable(type, objectName);
|
||||
} else {
|
||||
return Identifier(type);
|
||||
}
|
||||
return Identifier();
|
||||
}
|
||||
|
||||
std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace(type);
|
||||
factor->diagnostic = RaiseEmptyError(type, expressionStartPosition);
|
||||
std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace();
|
||||
return factor;
|
||||
}
|
||||
|
||||
std::unique_ptr<SubExpressionNode> SubExpression(
|
||||
const gd::String &type, const gd::String &objectName) {
|
||||
std::unique_ptr<SubExpressionNode> SubExpression() {
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
|
||||
auto expression = Expression(type, objectName);
|
||||
const gd::String &inferredType = expression->type;
|
||||
auto expression = Expression();
|
||||
|
||||
auto subExpression =
|
||||
gd::make_unique<SubExpressionNode>(inferredType, std::move(expression));
|
||||
gd::make_unique<SubExpressionNode>(std::move(expression));
|
||||
subExpression->location =
|
||||
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
|
||||
|
||||
@@ -277,7 +233,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
};
|
||||
|
||||
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
|
||||
Identifier(const gd::String &type) {
|
||||
Identifier() {
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
gd::String name = identifierAndLocation.name;
|
||||
auto nameLocation = identifierAndLocation.location;
|
||||
@@ -304,47 +260,28 @@ class GD_CORE_API ExpressionParser2 {
|
||||
|
||||
if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
ExpressionParserLocation openingParenthesisLocation = SkipChar();
|
||||
return FreeFunction(type, name, nameLocation, openingParenthesisLocation);
|
||||
return FreeFunction(name, nameLocation, openingParenthesisLocation);
|
||||
} else if (CheckIfChar(IsDot)) {
|
||||
ExpressionParserLocation dotLocation = SkipChar();
|
||||
SkipAllWhitespaces();
|
||||
return ObjectFunctionOrBehaviorFunction(
|
||||
type, name, nameLocation, dotLocation);
|
||||
} else {
|
||||
auto identifier = gd::make_unique<IdentifierNode>(name, type);
|
||||
if (type == "string") {
|
||||
identifier->diagnostic =
|
||||
RaiseTypeError(_("You must wrap your text inside double quotes "
|
||||
"(example: \"Hello world\")."),
|
||||
nameLocation.GetStartPosition());
|
||||
} else if (type == "number") {
|
||||
identifier->diagnostic = RaiseTypeError(
|
||||
_("You must enter a number."), nameLocation.GetStartPosition());
|
||||
} else if (type == "number|string") {
|
||||
identifier->diagnostic = RaiseTypeError(
|
||||
_("You must enter a number or a text, wrapped inside double quotes "
|
||||
"(example: \"Hello world\")."),
|
||||
nameLocation.GetStartPosition());
|
||||
} else if (!gd::ParameterMetadata::IsObject(type)) {
|
||||
identifier->diagnostic = RaiseTypeError(
|
||||
_("You've entered a name, but this type was expected:") + type,
|
||||
nameLocation.GetStartPosition());
|
||||
}
|
||||
|
||||
name, nameLocation, dotLocation);
|
||||
} else if (CheckIfChar(IsOpeningSquareBracket)) {
|
||||
return Variable(name, nameLocation);
|
||||
}
|
||||
else {
|
||||
auto identifier = gd::make_unique<IdentifierNode>(name);
|
||||
identifier->location = ExpressionParserLocation(
|
||||
nameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
identifier->identifierNameLocation = identifier->location;
|
||||
return std::move(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<VariableNode> Variable(const gd::String &type,
|
||||
const gd::String &objectName) {
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
const gd::String &name = identifierAndLocation.name;
|
||||
const auto &nameLocation = identifierAndLocation.location;
|
||||
|
||||
auto variable = gd::make_unique<VariableNode>(type, name, objectName);
|
||||
std::unique_ptr<VariableNode> Variable(const gd::String &name, gd::ExpressionParserLocation nameLocation) {
|
||||
auto variable = gd::make_unique<VariableNode>(name);
|
||||
variable->child = VariableAccessorOrVariableBracketAccessor();
|
||||
variable->child->parent = variable.get();
|
||||
|
||||
variable->location = ExpressionParserLocation(
|
||||
nameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
@@ -359,8 +296,8 @@ class GD_CORE_API ExpressionParser2 {
|
||||
SkipAllWhitespaces();
|
||||
if (CheckIfChar(IsOpeningSquareBracket)) {
|
||||
SkipChar();
|
||||
auto child = gd::make_unique<VariableBracketAccessorNode>(
|
||||
Expression("number|string"));
|
||||
auto child = gd::make_unique<VariableBracketAccessorNode>(Expression());
|
||||
child->expression->parent = child.get();
|
||||
|
||||
if (!CheckIfChar(IsClosingSquareBracket)) {
|
||||
child->diagnostic =
|
||||
@@ -369,6 +306,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
}
|
||||
SkipIfChar(IsClosingSquareBracket);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
child->child->parent = child.get();
|
||||
child->location =
|
||||
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
|
||||
|
||||
@@ -381,6 +319,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
auto child =
|
||||
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
child->child->parent = child.get();
|
||||
child->nameLocation = identifierAndLocation.location;
|
||||
child->dotLocation = dotLocation;
|
||||
child->location =
|
||||
@@ -389,40 +328,21 @@ class GD_CORE_API ExpressionParser2 {
|
||||
return std::move(child);
|
||||
}
|
||||
|
||||
return std::move(
|
||||
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>());
|
||||
return std::move(gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>());
|
||||
}
|
||||
|
||||
std::unique_ptr<FunctionCallNode> FreeFunction(
|
||||
const gd::String &type,
|
||||
const gd::String &functionFullName,
|
||||
const ExpressionParserLocation &identifierLocation,
|
||||
const ExpressionParserLocation &openingParenthesisLocation) {
|
||||
// TODO: error if trying to use function for type != "number" && != "string"
|
||||
// + Test for it
|
||||
|
||||
const gd::ExpressionMetadata &metadata =
|
||||
MetadataProvider::GetAnyExpressionMetadata(platform, functionFullName);
|
||||
|
||||
// In case we can't find a valid expression, ensure the node has the type
|
||||
// that is requested by the parent, so we avoid putting "unknown" (which
|
||||
// would be also correct, but less precise and would prevent completions to
|
||||
// be shown to the user)
|
||||
const gd::String returnType =
|
||||
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
|
||||
? type
|
||||
: metadata.GetReturnType();
|
||||
|
||||
auto parametersNode = Parameters(metadata.parameters);
|
||||
auto function =
|
||||
gd::make_unique<FunctionCallNode>(returnType,
|
||||
std::move(parametersNode.parameters),
|
||||
metadata,
|
||||
functionFullName);
|
||||
gd::make_unique<FunctionCallNode>(functionFullName);
|
||||
auto parametersNode = Parameters(function.get());
|
||||
function->parameters = std::move(parametersNode.parameters);
|
||||
function->diagnostic = std::move(parametersNode.diagnostic);
|
||||
if (!function->diagnostic) // TODO: reverse the order of diagnostic?
|
||||
function->diagnostic = ValidateFunction(
|
||||
type, *function, identifierLocation.GetStartPosition());
|
||||
|
||||
function->location = ExpressionParserLocation(
|
||||
identifierLocation.GetStartPosition(), GetCurrentPosition());
|
||||
@@ -434,16 +354,15 @@ class GD_CORE_API ExpressionParser2 {
|
||||
return std::move(function);
|
||||
}
|
||||
|
||||
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode>
|
||||
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
|
||||
ObjectFunctionOrBehaviorFunction(
|
||||
const gd::String &type,
|
||||
const gd::String &objectName,
|
||||
const ExpressionParserLocation &objectNameLocation,
|
||||
const ExpressionParserLocation &objectNameDotLocation) {
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
const gd::String &objectFunctionOrBehaviorName = identifierAndLocation.name;
|
||||
const auto &objectFunctionOrBehaviorNameLocation =
|
||||
identifierAndLocation.location;
|
||||
const gd::String &parentIdentifier,
|
||||
const ExpressionParserLocation &parentIdentifierLocation,
|
||||
const ExpressionParserLocation &parentIdentifierDotLocation) {
|
||||
auto childIdentifierAndLocation = ReadIdentifierName();
|
||||
const gd::String &childIdentifierName = childIdentifierAndLocation.name;
|
||||
const auto &childIdentifierNameLocation =
|
||||
childIdentifierAndLocation.location;
|
||||
|
||||
SkipAllWhitespaces();
|
||||
|
||||
@@ -451,87 +370,68 @@ class GD_CORE_API ExpressionParser2 {
|
||||
ExpressionParserLocation namespaceSeparatorLocation =
|
||||
SkipNamespaceSeparator();
|
||||
SkipAllWhitespaces();
|
||||
return BehaviorFunction(type,
|
||||
objectName,
|
||||
objectFunctionOrBehaviorName,
|
||||
objectNameLocation,
|
||||
objectNameDotLocation,
|
||||
objectFunctionOrBehaviorNameLocation,
|
||||
return BehaviorFunction(parentIdentifier,
|
||||
childIdentifierName,
|
||||
parentIdentifierLocation,
|
||||
parentIdentifierDotLocation,
|
||||
childIdentifierNameLocation,
|
||||
namespaceSeparatorLocation);
|
||||
} else if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
ExpressionParserLocation openingParenthesisLocation = SkipChar();
|
||||
|
||||
gd::String objectType =
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName);
|
||||
|
||||
const gd::ExpressionMetadata &metadata =
|
||||
MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform, objectType, objectFunctionOrBehaviorName);
|
||||
|
||||
// In case we can't find a valid expression, ensure the node has the type
|
||||
// that is requested by the parent, so we avoid putting "unknown" (which
|
||||
// would be also correct, but less precise and would prevent completions
|
||||
// to be shown to the user)
|
||||
const gd::String returnType =
|
||||
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
|
||||
? type
|
||||
: metadata.GetReturnType();
|
||||
|
||||
auto parametersNode = Parameters(metadata.parameters, objectName);
|
||||
auto function = gd::make_unique<FunctionCallNode>(
|
||||
returnType,
|
||||
objectName,
|
||||
std::move(parametersNode.parameters),
|
||||
metadata,
|
||||
objectFunctionOrBehaviorName);
|
||||
parentIdentifier,
|
||||
childIdentifierName);
|
||||
auto parametersNode = Parameters(function.get(), parentIdentifier);
|
||||
function->parameters = std::move(parametersNode.parameters),
|
||||
function->diagnostic = std::move(parametersNode.diagnostic);
|
||||
|
||||
if (!function->diagnostic) // TODO: reverse the order of diagnostic?
|
||||
function->diagnostic = ValidateFunction(
|
||||
type, *function, objectNameLocation.GetStartPosition());
|
||||
|
||||
// If the function needs a capability on the object that may not be covered
|
||||
// by all objects, check it now.
|
||||
if (!metadata.GetRequiredBaseObjectCapability().empty()) {
|
||||
const gd::ObjectMetadata &objectMetadata =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
|
||||
if (objectMetadata.IsUnsupportedBaseObjectCapability(
|
||||
metadata.GetRequiredBaseObjectCapability())) {
|
||||
function->diagnostic = RaiseTypeError(
|
||||
_("This expression exists, but it can't be used on this object."),
|
||||
objectNameLocation.GetStartPosition());
|
||||
}
|
||||
}
|
||||
|
||||
function->location = ExpressionParserLocation(
|
||||
objectNameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
function->objectNameLocation = objectNameLocation;
|
||||
function->objectNameDotLocation = objectNameDotLocation;
|
||||
function->functionNameLocation = objectFunctionOrBehaviorNameLocation;
|
||||
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
|
||||
function->objectNameLocation = parentIdentifierLocation;
|
||||
function->objectNameDotLocation = parentIdentifierDotLocation;
|
||||
function->functionNameLocation = childIdentifierNameLocation;
|
||||
function->openingParenthesisLocation = openingParenthesisLocation;
|
||||
function->closingParenthesisLocation =
|
||||
parametersNode.closingParenthesisLocation;
|
||||
return std::move(function);
|
||||
} else if (CheckIfChar(IsDot) || CheckIfChar(IsOpeningSquareBracket)) {
|
||||
auto variable = gd::make_unique<VariableNode>(parentIdentifier);
|
||||
auto child =
|
||||
gd::make_unique<VariableAccessorNode>(childIdentifierName);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
child->child->parent = child.get();
|
||||
child->nameLocation = childIdentifierNameLocation;
|
||||
child->dotLocation = parentIdentifierDotLocation;
|
||||
child->location = ExpressionParserLocation(
|
||||
parentIdentifierDotLocation.GetStartPosition(), GetCurrentPosition());
|
||||
variable->child = std::move(child);
|
||||
variable->child->parent = variable.get();
|
||||
|
||||
variable->location = ExpressionParserLocation(
|
||||
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
|
||||
variable->nameLocation = parentIdentifierLocation;
|
||||
|
||||
return std::move(variable);
|
||||
}
|
||||
|
||||
auto node = gd::make_unique<ObjectFunctionNameNode>(
|
||||
type, objectName, objectFunctionOrBehaviorName);
|
||||
node->diagnostic = RaiseSyntaxError(
|
||||
_("An opening parenthesis (for an object expression), or double colon "
|
||||
"(::) was expected (for a behavior expression)."));
|
||||
|
||||
auto node = gd::make_unique<IdentifierNode>(
|
||||
parentIdentifier, childIdentifierName);
|
||||
if (!CheckIfChar(IsParameterSeparator) && !CheckIfChar(IsClosingParenthesis) && !IsEndReached()) {
|
||||
node->diagnostic = RaiseSyntaxError(
|
||||
_("An opening parenthesis (for an object expression), a double colon "
|
||||
"(:: for a behavior expression), a dot or an opening bracket (for "
|
||||
"a child variable) where expected."));
|
||||
}
|
||||
node->location = ExpressionParserLocation(
|
||||
objectNameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
node->objectNameLocation = objectNameLocation;
|
||||
node->objectNameDotLocation = objectNameDotLocation;
|
||||
node->objectFunctionOrBehaviorNameLocation =
|
||||
objectFunctionOrBehaviorNameLocation;
|
||||
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
|
||||
node->identifierNameLocation = parentIdentifierLocation;
|
||||
node->identifierNameDotLocation = parentIdentifierDotLocation;
|
||||
node->childIdentifierNameLocation = childIdentifierNameLocation;
|
||||
return std::move(node);
|
||||
}
|
||||
|
||||
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode> BehaviorFunction(
|
||||
const gd::String &type,
|
||||
const gd::String &objectName,
|
||||
const gd::String &behaviorName,
|
||||
const ExpressionParserLocation &objectNameLocation,
|
||||
@@ -547,35 +447,14 @@ class GD_CORE_API ExpressionParser2 {
|
||||
if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
ExpressionParserLocation openingParenthesisLocation = SkipChar();
|
||||
|
||||
gd::String behaviorType = GetTypeOfBehavior(
|
||||
globalObjectsContainer, objectsContainer, behaviorName);
|
||||
|
||||
const gd::ExpressionMetadata &metadata =
|
||||
MetadataProvider::GetBehaviorAnyExpressionMetadata(
|
||||
platform, behaviorType, functionName);
|
||||
|
||||
// In case we can't find a valid expression, ensure the node has the type
|
||||
// that is requested by the parent, so we avoid putting "unknown" (which
|
||||
// would be also correct, but less precise and would prevent completions
|
||||
// to be shown to the user)
|
||||
const gd::String returnType =
|
||||
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
|
||||
? type
|
||||
: metadata.GetReturnType();
|
||||
|
||||
auto parametersNode =
|
||||
Parameters(metadata.parameters, objectName, behaviorName);
|
||||
auto function = gd::make_unique<FunctionCallNode>(
|
||||
returnType,
|
||||
objectName,
|
||||
behaviorName,
|
||||
std::move(parametersNode.parameters),
|
||||
metadata,
|
||||
functionName);
|
||||
auto parametersNode =
|
||||
Parameters(function.get(), objectName, behaviorName);
|
||||
function->parameters = std::move(parametersNode.parameters);
|
||||
function->diagnostic = std::move(parametersNode.diagnostic);
|
||||
if (!function->diagnostic) // TODO: reverse the order of diagnostic?
|
||||
function->diagnostic = ValidateFunction(
|
||||
type, *function, objectNameLocation.GetStartPosition());
|
||||
|
||||
function->location = ExpressionParserLocation(
|
||||
objectNameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
@@ -591,7 +470,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
return std::move(function);
|
||||
} else {
|
||||
auto node = gd::make_unique<ObjectFunctionNameNode>(
|
||||
type, objectName, behaviorName, functionName);
|
||||
objectName, behaviorName, functionName);
|
||||
node->diagnostic = RaiseSyntaxError(
|
||||
_("An opening parenthesis was expected here to call a function."));
|
||||
|
||||
@@ -615,7 +494,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
};
|
||||
|
||||
ParametersNode Parameters(
|
||||
std::vector<gd::ParameterMetadata> parameterMetadata,
|
||||
FunctionCallNode *functionCallNode,
|
||||
const gd::String &objectName = "",
|
||||
const gd::String &behaviorName = "") {
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters;
|
||||
@@ -626,77 +505,25 @@ class GD_CORE_API ExpressionParser2 {
|
||||
size_t parameterIndex =
|
||||
WrittenParametersFirstIndex(objectName, behaviorName);
|
||||
|
||||
bool previousCharacterIsParameterSeparator = false;
|
||||
while (!IsEndReached()) {
|
||||
SkipAllWhitespaces();
|
||||
|
||||
if (CheckIfChar(IsClosingParenthesis)) {
|
||||
if (CheckIfChar(IsClosingParenthesis) && !previousCharacterIsParameterSeparator) {
|
||||
auto closingParenthesisLocation = SkipChar();
|
||||
return ParametersNode{
|
||||
std::move(parameters), nullptr, closingParenthesisLocation};
|
||||
} else {
|
||||
if (parameterIndex < parameterMetadata.size()) {
|
||||
const gd::String &type = parameterMetadata[parameterIndex].GetType();
|
||||
if (parameterMetadata[parameterIndex].IsCodeOnly()) {
|
||||
// Do nothing, code only parameters are not written in expressions.
|
||||
} else if (gd::ParameterMetadata::IsExpression("number", type)) {
|
||||
parameters.push_back(Expression("number"));
|
||||
} else if (gd::ParameterMetadata::IsExpression("string", type)) {
|
||||
parameters.push_back(Expression("string"));
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
parameters.push_back(Expression(
|
||||
type, lastObjectName.empty() ? objectName : lastObjectName));
|
||||
} else if (gd::ParameterMetadata::IsObject(type)) {
|
||||
size_t parameterStartPosition = GetCurrentPosition();
|
||||
std::unique_ptr<ExpressionNode> objectExpression = Expression(type);
|
||||
|
||||
// Memorize the last object name. By convention, parameters that
|
||||
// require an object (mainly, "objectvar" and "behavior") should be
|
||||
// placed after the object in the list of parameters (if possible,
|
||||
// just after). Search "lastObjectName" in the codebase for other
|
||||
// place where this convention is enforced.
|
||||
if (auto identifierNode =
|
||||
dynamic_cast<IdentifierNode *>(objectExpression.get())) {
|
||||
lastObjectName = identifierNode->identifierName;
|
||||
} else {
|
||||
objectExpression->diagnostic =
|
||||
gd::make_unique<ExpressionParserError>(
|
||||
"malformed_object_parameter",
|
||||
_("An object name was expected but something else was "
|
||||
"written. Enter just the name of the object for this "
|
||||
"parameter."),
|
||||
parameterStartPosition,
|
||||
GetCurrentPosition());
|
||||
}
|
||||
|
||||
parameters.push_back(std::move(objectExpression));
|
||||
} else {
|
||||
size_t parameterStartPosition = GetCurrentPosition();
|
||||
parameters.push_back(Expression("unknown"));
|
||||
parameters.back()->diagnostic =
|
||||
gd::make_unique<ExpressionParserError>(
|
||||
"unknown_parameter_type",
|
||||
_("This function is improperly set up. Reach out to the "
|
||||
"extension developer or a GDevelop maintainer to fix "
|
||||
"this issue"),
|
||||
parameterStartPosition,
|
||||
GetCurrentPosition());
|
||||
}
|
||||
} else {
|
||||
size_t parameterStartPosition = GetCurrentPosition();
|
||||
parameters.push_back(Expression("unknown"));
|
||||
parameters.back()
|
||||
->diagnostic = gd::make_unique<ExpressionParserError>(
|
||||
"extra_parameter",
|
||||
_("This parameter was not expected by this expression. Remove it "
|
||||
"or verify that you've entered the proper expression name."),
|
||||
parameterStartPosition,
|
||||
GetCurrentPosition());
|
||||
}
|
||||
|
||||
SkipAllWhitespaces();
|
||||
SkipIfChar(IsParameterSeparator);
|
||||
parameterIndex++;
|
||||
}
|
||||
bool isEmptyParameter = CheckIfChar(IsParameterSeparator)
|
||||
|| (CheckIfChar(IsClosingParenthesis) && previousCharacterIsParameterSeparator);
|
||||
auto parameter = isEmptyParameter ? gd::make_unique<EmptyNode>() : Expression();
|
||||
parameter->parent = functionCallNode;
|
||||
parameters.push_back(std::move(parameter));
|
||||
|
||||
SkipAllWhitespaces();
|
||||
previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
|
||||
SkipIfChar(IsParameterSeparator);
|
||||
parameterIndex++;
|
||||
}
|
||||
|
||||
ExpressionParserLocation invalidClosingParenthesisLocation;
|
||||
@@ -708,92 +535,32 @@ class GD_CORE_API ExpressionParser2 {
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Validators
|
||||
* Return a diagnostic if any error is found
|
||||
*/
|
||||
///@{
|
||||
std::unique_ptr<ExpressionParserDiagnostic> ValidateFunction(
|
||||
const gd::String &type,
|
||||
const gd::FunctionCallNode &function,
|
||||
size_t functionStartPosition);
|
||||
|
||||
std::unique_ptr<ExpressionParserDiagnostic> ValidateOperator(
|
||||
const gd::String &type, gd::String::value_type operatorChar) {
|
||||
if (type == "number") {
|
||||
if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
|
||||
operatorChar == '*') {
|
||||
return gd::make_unique<ExpressionParserDiagnostic>();
|
||||
}
|
||||
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("You've used an operator that is not supported. Operator should be "
|
||||
"either +, -, / or *."),
|
||||
GetCurrentPosition());
|
||||
} else if (type == "string") {
|
||||
if (operatorChar == '+') {
|
||||
return gd::make_unique<ExpressionParserDiagnostic>();
|
||||
}
|
||||
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts."),
|
||||
GetCurrentPosition());
|
||||
} else if (gd::ParameterMetadata::IsObject(type)) {
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("Operators (+, -, /, *) can't be used with an object name. Remove "
|
||||
"the operator."),
|
||||
GetCurrentPosition());
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("Operators (+, -, /, *) can't be used in variable names. Remove "
|
||||
"the operator from the variable name."),
|
||||
GetCurrentPosition());
|
||||
gd::String::value_type operatorChar) {
|
||||
if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
|
||||
operatorChar == '*') {
|
||||
return gd::make_unique<ExpressionParserDiagnostic>();
|
||||
}
|
||||
|
||||
return gd::make_unique<ExpressionParserDiagnostic>();
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("You've used an operator that is not supported. Operator should be "
|
||||
"either +, -, / or *."),
|
||||
GetCurrentPosition());
|
||||
}
|
||||
|
||||
std::unique_ptr<ExpressionParserDiagnostic> ValidateUnaryOperator(
|
||||
const gd::String &type,
|
||||
gd::String::value_type operatorChar,
|
||||
size_t position) {
|
||||
if (type == "number") {
|
||||
if (operatorChar == '+' || operatorChar == '-') {
|
||||
return gd::make_unique<ExpressionParserDiagnostic>();
|
||||
}
|
||||
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("You've used an \"unary\" operator that is not supported. Operator "
|
||||
"should be "
|
||||
"either + or -."),
|
||||
position);
|
||||
} else if (type == "string") {
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts, and must be placed between two texts (or "
|
||||
"expressions)."),
|
||||
position);
|
||||
} else if (gd::ParameterMetadata::IsObject(type)) {
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("Operators (+, -) can't be used with an object name. Remove the "
|
||||
"operator."),
|
||||
position);
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("Operators (+, -) can't be used in variable names. Remove "
|
||||
"the operator from the variable name."),
|
||||
position);
|
||||
if (operatorChar == '+' || operatorChar == '-') {
|
||||
return gd::make_unique<ExpressionParserDiagnostic>();
|
||||
}
|
||||
|
||||
return gd::make_unique<ExpressionParserDiagnostic>();
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
"invalid_operator",
|
||||
_("You've used an \"unary\" operator that is not supported. Operator "
|
||||
"should be "
|
||||
"either + or -."),
|
||||
position);
|
||||
}
|
||||
///@}
|
||||
|
||||
@@ -981,7 +748,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
|
||||
std::unique_ptr<NumberNode> ReadNumber();
|
||||
|
||||
std::unique_ptr<EmptyNode> ReadUntilWhitespace(gd::String type) {
|
||||
std::unique_ptr<EmptyNode> ReadUntilWhitespace() {
|
||||
size_t startPosition = GetCurrentPosition();
|
||||
gd::String text;
|
||||
while (currentPosition < expression.size() &&
|
||||
@@ -990,13 +757,13 @@ class GD_CORE_API ExpressionParser2 {
|
||||
currentPosition++;
|
||||
}
|
||||
|
||||
auto node = gd::make_unique<EmptyNode>(type, text);
|
||||
auto node = gd::make_unique<EmptyNode>(text);
|
||||
node->location =
|
||||
ExpressionParserLocation(startPosition, GetCurrentPosition());
|
||||
return node;
|
||||
}
|
||||
|
||||
std::unique_ptr<EmptyNode> ReadUntilEnd(gd::String type) {
|
||||
std::unique_ptr<EmptyNode> ReadUntilEnd() {
|
||||
size_t startPosition = GetCurrentPosition();
|
||||
gd::String text;
|
||||
while (currentPosition < expression.size()) {
|
||||
@@ -1004,7 +771,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
currentPosition++;
|
||||
}
|
||||
|
||||
auto node = gd::make_unique<EmptyNode>(type, text);
|
||||
auto node = gd::make_unique<EmptyNode>(text);
|
||||
node->location =
|
||||
ExpressionParserLocation(startPosition, GetCurrentPosition());
|
||||
return node;
|
||||
@@ -1037,34 +804,11 @@ class GD_CORE_API ExpressionParser2 {
|
||||
return std::move(gd::make_unique<ExpressionParserError>(
|
||||
"type_error", message, beginningPosition, GetCurrentPosition()));
|
||||
}
|
||||
|
||||
std::unique_ptr<ExpressionParserError> RaiseEmptyError(
|
||||
const gd::String &type, size_t beginningPosition) {
|
||||
gd::String message;
|
||||
if (type == "number") {
|
||||
message = _("You must enter a number or a valid expression call.");
|
||||
} else if (type == "string") {
|
||||
message = _(
|
||||
"You must enter a text (between quotes) or a valid expression call.");
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
message = _("You must enter a variable name.");
|
||||
} else if (gd::ParameterMetadata::IsObject(type)) {
|
||||
message = _("You must enter a valid object name.");
|
||||
} else {
|
||||
message = _("You must enter a valid expression.");
|
||||
}
|
||||
|
||||
return std::move(RaiseTypeError(message, beginningPosition));
|
||||
}
|
||||
///@}
|
||||
|
||||
gd::String expression;
|
||||
std::size_t currentPosition;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
|
||||
static gd::String NAMESPACE_SEPARATOR;
|
||||
};
|
||||
|
||||
|
@@ -17,6 +17,7 @@ class ObjectsContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
struct FunctionCallNode;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -57,6 +58,10 @@ struct GD_CORE_API ExpressionParserDiagnostic {
|
||||
* \brief An error that can be attached to a gd::ExpressionNode.
|
||||
*/
|
||||
struct GD_CORE_API ExpressionParserError : public ExpressionParserDiagnostic {
|
||||
ExpressionParserError(const gd::String &type_,
|
||||
const gd::String &message_,
|
||||
const ExpressionParserLocation &location_)
|
||||
: type(type_), message(message_), location(location_){};
|
||||
ExpressionParserError(const gd::String &type_,
|
||||
const gd::String &message_,
|
||||
size_t position_)
|
||||
@@ -86,7 +91,7 @@ struct GD_CORE_API ExpressionParserError : public ExpressionParserDiagnostic {
|
||||
* an expression inherits from.
|
||||
*/
|
||||
struct GD_CORE_API ExpressionNode {
|
||||
ExpressionNode(const gd::String &type_) : type(type_){};
|
||||
ExpressionNode() : parent(nullptr) {};
|
||||
virtual ~ExpressionNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker){};
|
||||
|
||||
@@ -97,17 +102,12 @@ struct GD_CORE_API ExpressionNode {
|
||||
/// function can store the position of the
|
||||
/// object name, the dot, the function
|
||||
/// name, etc...
|
||||
|
||||
gd::String type; // Actual type of the node.
|
||||
// "string", "number", type supported by
|
||||
// gd::ParameterMetadata::IsObject, types supported by
|
||||
// gd::ParameterMetadata::IsExpression or "unknown".
|
||||
ExpressionNode *parent;
|
||||
};
|
||||
|
||||
struct GD_CORE_API SubExpressionNode : public ExpressionNode {
|
||||
SubExpressionNode(const gd::String &type_,
|
||||
std::unique_ptr<ExpressionNode> expression_)
|
||||
: ExpressionNode(type_), expression(std::move(expression_)){};
|
||||
SubExpressionNode(std::unique_ptr<ExpressionNode> expression_)
|
||||
: ExpressionNode(), expression(std::move(expression_)){};
|
||||
virtual ~SubExpressionNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitSubExpressionNode(*this);
|
||||
@@ -120,8 +120,8 @@ struct GD_CORE_API SubExpressionNode : public ExpressionNode {
|
||||
* \brief An operator node. For example: "lhs + rhs".
|
||||
*/
|
||||
struct GD_CORE_API OperatorNode : public ExpressionNode {
|
||||
OperatorNode(const gd::String &type_, gd::String::value_type op_)
|
||||
: ExpressionNode(type_), op(op_){};
|
||||
OperatorNode(gd::String::value_type op_)
|
||||
: ExpressionNode(), op(op_){};
|
||||
virtual ~OperatorNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitOperatorNode(*this);
|
||||
@@ -136,8 +136,8 @@ struct GD_CORE_API OperatorNode : public ExpressionNode {
|
||||
* \brief A unary operator node. For example: "-2".
|
||||
*/
|
||||
struct GD_CORE_API UnaryOperatorNode : public ExpressionNode {
|
||||
UnaryOperatorNode(const gd::String &type_, gd::String::value_type op_)
|
||||
: ExpressionNode(type_), op(op_){};
|
||||
UnaryOperatorNode(gd::String::value_type op_)
|
||||
: ExpressionNode(), op(op_){};
|
||||
virtual ~UnaryOperatorNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitUnaryOperatorNode(*this);
|
||||
@@ -153,7 +153,7 @@ struct GD_CORE_API UnaryOperatorNode : public ExpressionNode {
|
||||
*/
|
||||
struct GD_CORE_API NumberNode : public ExpressionNode {
|
||||
NumberNode(const gd::String &number_)
|
||||
: ExpressionNode("number"), number(number_){};
|
||||
: ExpressionNode(), number(number_){};
|
||||
virtual ~NumberNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitNumberNode(*this);
|
||||
@@ -168,7 +168,7 @@ struct GD_CORE_API NumberNode : public ExpressionNode {
|
||||
* Its `type` is always "string".
|
||||
*/
|
||||
struct GD_CORE_API TextNode : public ExpressionNode {
|
||||
TextNode(const gd::String &text_) : ExpressionNode("string"), text(text_){};
|
||||
TextNode(const gd::String &text_) : ExpressionNode(), text(text_){};
|
||||
virtual ~TextNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitTextNode(*this);
|
||||
@@ -177,32 +177,88 @@ struct GD_CORE_API TextNode : public ExpressionNode {
|
||||
gd::String text;
|
||||
};
|
||||
|
||||
struct GD_CORE_API IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode
|
||||
: public ExpressionNode {
|
||||
IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode()
|
||||
: ExpressionNode(){};
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief An identifier node, usually representing an object or a variable
|
||||
* with an optional function name or child variable name respectively.
|
||||
*
|
||||
* The name of a function to call on an object or the behavior,
|
||||
* for example: "MyObject.Function" or "MyObject.Physics".
|
||||
*
|
||||
* A variable, potentially with accessor to its child,
|
||||
* for example: MyVariable or MyVariable.MyChild
|
||||
*/
|
||||
struct GD_CORE_API IdentifierNode
|
||||
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
IdentifierNode(
|
||||
const gd::String &identifierName_)
|
||||
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(),
|
||||
identifierName(identifierName_),
|
||||
childIdentifierName(""){};
|
||||
IdentifierNode(
|
||||
const gd::String &identifierName_,
|
||||
const gd::String &childIdentifierName_)
|
||||
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(),
|
||||
identifierName(identifierName_),
|
||||
childIdentifierName(childIdentifierName_){};
|
||||
virtual ~IdentifierNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitIdentifierNode(*this);
|
||||
};
|
||||
|
||||
gd::String identifierName; ///< The object or variable name.
|
||||
gd::String childIdentifierName; ///< The object function or variable child name.
|
||||
|
||||
|
||||
ExpressionParserLocation
|
||||
identifierNameLocation; ///< Location of the object or variable name.
|
||||
ExpressionParserLocation
|
||||
identifierNameDotLocation; ///< Location of the "." after the object or variable name.
|
||||
ExpressionParserLocation childIdentifierNameLocation; ///< Location of object
|
||||
/// function, behavior or
|
||||
/// child variable name.
|
||||
};
|
||||
|
||||
struct GD_CORE_API FunctionCallOrObjectFunctionNameOrEmptyNode
|
||||
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
FunctionCallOrObjectFunctionNameOrEmptyNode()
|
||||
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(){};
|
||||
virtual ~FunctionCallOrObjectFunctionNameOrEmptyNode(){};
|
||||
void Visit(ExpressionParser2NodeWorker &worker) override{};
|
||||
};
|
||||
|
||||
struct GD_CORE_API VariableAccessorOrVariableBracketAccessorNode : public ExpressionNode {
|
||||
VariableAccessorOrVariableBracketAccessorNode() : ExpressionNode(""){};
|
||||
VariableAccessorOrVariableBracketAccessorNode() : ExpressionNode(){};
|
||||
|
||||
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode> child;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A variable, potentially with accessor to its children.
|
||||
*
|
||||
* Example: MyVariable or MyVariable.MyChildren
|
||||
* \brief A variable with bracket accessor or at least 2 "dot" accessors.
|
||||
*
|
||||
* Example: MyVariable[MyChildren] or MyVariable.MyChildren.MyGranChildren.
|
||||
*
|
||||
* Other cases like "MyVariable" or "MyVariable.MyChildren" are IdentifierNode
|
||||
* to allow handling ambiguities.
|
||||
*
|
||||
* \see gd::IdentifierNode
|
||||
* \see gd::VariableAccessorNode
|
||||
* \see gd::VariableBracketAccessorNode
|
||||
*/
|
||||
struct GD_CORE_API VariableNode : public ExpressionNode {
|
||||
VariableNode(const gd::String &type_,
|
||||
const gd::String &name_,
|
||||
const gd::String &objectName_)
|
||||
: ExpressionNode(type_), name(name_), objectName(objectName_){};
|
||||
struct GD_CORE_API VariableNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
VariableNode(const gd::String &name_)
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(), name(name_){};
|
||||
virtual ~VariableNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitVariableNode(*this);
|
||||
};
|
||||
|
||||
gd::String name;
|
||||
gd::String objectName;
|
||||
|
||||
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
|
||||
child; // Can be nullptr if no accessor
|
||||
@@ -216,7 +272,8 @@ struct GD_CORE_API VariableNode : public ExpressionNode {
|
||||
*/
|
||||
struct GD_CORE_API VariableAccessorNode
|
||||
: public VariableAccessorOrVariableBracketAccessorNode {
|
||||
VariableAccessorNode(const gd::String &name_) : name(name_){};
|
||||
VariableAccessorNode(const gd::String &name_)
|
||||
: VariableAccessorOrVariableBracketAccessorNode(), name(name_){};
|
||||
virtual ~VariableAccessorNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitVariableAccessorNode(*this);
|
||||
@@ -234,7 +291,7 @@ struct GD_CORE_API VariableAccessorNode
|
||||
struct GD_CORE_API VariableBracketAccessorNode
|
||||
: public VariableAccessorOrVariableBracketAccessorNode {
|
||||
VariableBracketAccessorNode(std::unique_ptr<ExpressionNode> expression_)
|
||||
: expression(std::move(expression_)){};
|
||||
: VariableAccessorOrVariableBracketAccessorNode(), expression(std::move(expression_)){};
|
||||
virtual ~VariableBracketAccessorNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitVariableBracketAccessorNode(*this);
|
||||
@@ -243,55 +300,26 @@ struct GD_CORE_API VariableBracketAccessorNode
|
||||
std::unique_ptr<ExpressionNode> expression;
|
||||
};
|
||||
|
||||
struct GD_CORE_API IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode
|
||||
: public ExpressionNode {
|
||||
IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(
|
||||
const gd::String &type)
|
||||
: ExpressionNode(type){};
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief An identifier node, usually representing an object or a function name.
|
||||
*/
|
||||
struct GD_CORE_API IdentifierNode
|
||||
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
IdentifierNode(const gd::String &identifierName_, const gd::String &type_)
|
||||
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(type_),
|
||||
identifierName(identifierName_){};
|
||||
virtual ~IdentifierNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitIdentifierNode(*this);
|
||||
};
|
||||
|
||||
gd::String identifierName;
|
||||
};
|
||||
|
||||
struct GD_CORE_API FunctionCallOrObjectFunctionNameOrEmptyNode
|
||||
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
FunctionCallOrObjectFunctionNameOrEmptyNode(const gd::String &type)
|
||||
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(type){};
|
||||
virtual ~FunctionCallOrObjectFunctionNameOrEmptyNode(){};
|
||||
void Visit(ExpressionParser2NodeWorker &worker) override{};
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief The name of a function to call on an object or the behavior
|
||||
* For example: "MyObject.Function" or "MyObject.Physics" or
|
||||
* "MyObject.Physics::LinearVelocity".
|
||||
* For example: "MyObject.Physics::LinearVelocity".
|
||||
*
|
||||
* Other cases like "MyObject.Function" or "MyObject.Physics" are IdentifierNode
|
||||
* to allow handling ambiguities.
|
||||
*
|
||||
* \see gd::IdentifierNode
|
||||
*/
|
||||
struct GD_CORE_API ObjectFunctionNameNode
|
||||
: public FunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
ObjectFunctionNameNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
ObjectFunctionNameNode(const gd::String &objectName_,
|
||||
const gd::String &objectFunctionOrBehaviorName_)
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
|
||||
objectName(objectName_),
|
||||
objectFunctionOrBehaviorName(objectFunctionOrBehaviorName_) {}
|
||||
ObjectFunctionNameNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
ObjectFunctionNameNode(const gd::String &objectName_,
|
||||
const gd::String &behaviorName_,
|
||||
const gd::String &behaviorFunctionName_)
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
|
||||
objectName(objectName_),
|
||||
objectFunctionOrBehaviorName(behaviorName_),
|
||||
behaviorFunctionName(behaviorFunctionName_) {}
|
||||
@@ -334,39 +362,24 @@ struct GD_CORE_API ObjectFunctionNameNode
|
||||
*/
|
||||
struct GD_CORE_API FunctionCallNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
/** \brief Construct a free function call node. */
|
||||
FunctionCallNode(const gd::String &type_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
const gd::String &functionName_)
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
|
||||
parameters(std::move(parameters_)),
|
||||
expressionMetadata(expressionMetadata_),
|
||||
FunctionCallNode(const gd::String &functionName_)
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
|
||||
functionName(functionName_){};
|
||||
|
||||
/** \brief Construct an object function call node. */
|
||||
FunctionCallNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
FunctionCallNode(const gd::String &objectName_,
|
||||
const gd::String &functionName_)
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
|
||||
objectName(objectName_),
|
||||
parameters(std::move(parameters_)),
|
||||
expressionMetadata(expressionMetadata_),
|
||||
functionName(functionName_){};
|
||||
|
||||
/** \brief Construct a behavior function call node. */
|
||||
FunctionCallNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
FunctionCallNode(const gd::String &objectName_,
|
||||
const gd::String &behaviorName_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
const gd::String &functionName_)
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
|
||||
objectName(objectName_),
|
||||
behaviorName(behaviorName_),
|
||||
parameters(std::move(parameters_)),
|
||||
expressionMetadata(expressionMetadata_),
|
||||
functionName(functionName_){};
|
||||
virtual ~FunctionCallNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
@@ -376,7 +389,6 @@ struct GD_CORE_API FunctionCallNode : public FunctionCallOrObjectFunctionNameOrE
|
||||
gd::String objectName;
|
||||
gd::String behaviorName;
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters;
|
||||
const ExpressionMetadata &expressionMetadata;
|
||||
gd::String functionName;
|
||||
|
||||
ExpressionParserLocation
|
||||
@@ -401,8 +413,8 @@ struct GD_CORE_API FunctionCallNode : public FunctionCallOrObjectFunctionNameOrE
|
||||
* encountered and any other node could not make sense.
|
||||
*/
|
||||
struct GD_CORE_API EmptyNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
EmptyNode(const gd::String &type_, const gd::String &text_ = "")
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_), text(text_){};
|
||||
EmptyNode(const gd::String &text_ = "")
|
||||
: FunctionCallOrObjectFunctionNameOrEmptyNode(), text(text_){};
|
||||
virtual ~EmptyNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitEmptyNode(*this);
|
||||
|
@@ -91,6 +91,9 @@ class GD_CORE_API ExpressionParser2NodePrinter
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
output += node.identifierName;
|
||||
if (!node.childIdentifierName.empty()) {
|
||||
output += "." + node.childIdentifierName;
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (!node.behaviorFunctionName.empty()) {
|
||||
|
@@ -13,7 +13,9 @@
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -30,12 +32,9 @@ ExtensionAndMetadata<BehaviorMetadata>
|
||||
MetadataProvider::GetExtensionAndBehaviorMetadata(const gd::Platform& platform,
|
||||
gd::String behaviorType) {
|
||||
for (auto& extension : platform.GetAllPlatformExtensions()) {
|
||||
auto behaviorTypes = extension->GetBehaviorsTypes();
|
||||
for (std::size_t j = 0; j < behaviorTypes.size(); ++j) {
|
||||
if (behaviorTypes[j] == behaviorType)
|
||||
return ExtensionAndMetadata<BehaviorMetadata>(
|
||||
*extension, extension->GetBehaviorMetadata(behaviorType));
|
||||
}
|
||||
if (extension->HasBehavior(behaviorType))
|
||||
return ExtensionAndMetadata<BehaviorMetadata>(
|
||||
*extension, extension->GetBehaviorMetadata(behaviorType));
|
||||
}
|
||||
|
||||
return ExtensionAndMetadata<BehaviorMetadata>(badExtension, badBehaviorMetadata);
|
||||
@@ -202,8 +201,7 @@ MetadataProvider::GetExtensionAndBehaviorExpressionMetadata(
|
||||
const gd::Platform& platform, gd::String autoType, gd::String exprType) {
|
||||
auto& extensions = platform.GetAllPlatformExtensions();
|
||||
for (auto& extension : extensions) {
|
||||
const auto& autos = extension->GetBehaviorsTypes();
|
||||
if (find(autos.begin(), autos.end(), autoType) != autos.end()) {
|
||||
if (extension->HasBehavior(autoType)) {
|
||||
const auto& allAutoExpressions =
|
||||
extension->GetAllExpressionsForBehavior(autoType);
|
||||
if (allAutoExpressions.find(exprType) != allAutoExpressions.end())
|
||||
@@ -292,8 +290,7 @@ MetadataProvider::GetExtensionAndBehaviorStrExpressionMetadata(
|
||||
const gd::Platform& platform, gd::String autoType, gd::String exprType) {
|
||||
auto& extensions = platform.GetAllPlatformExtensions();
|
||||
for (auto& extension : extensions) {
|
||||
const auto& autos = extension->GetBehaviorsTypes();
|
||||
if (find(autos.begin(), autos.end(), autoType) != autos.end()) {
|
||||
if (extension->HasBehavior(autoType)) {
|
||||
const auto& allBehaviorStrExpressions =
|
||||
extension->GetAllStrExpressionsForBehavior(autoType);
|
||||
if (allBehaviorStrExpressions.find(exprType) !=
|
||||
@@ -350,28 +347,30 @@ const gd::ExpressionMetadata& MetadataProvider::GetAnyExpressionMetadata(
|
||||
const gd::Platform& platform, gd::String exprType) {
|
||||
const auto& numberExpressionMetadata =
|
||||
GetExpressionMetadata(platform, exprType);
|
||||
if (&numberExpressionMetadata != &badExpressionMetadata) {
|
||||
return numberExpressionMetadata;
|
||||
}
|
||||
const auto& stringExpressionMetadata =
|
||||
GetStrExpressionMetadata(platform, exprType);
|
||||
|
||||
return &numberExpressionMetadata != &badExpressionMetadata
|
||||
? numberExpressionMetadata
|
||||
: &stringExpressionMetadata != &badExpressionMetadata
|
||||
? stringExpressionMetadata
|
||||
: badExpressionMetadata;
|
||||
if (&stringExpressionMetadata != &badExpressionMetadata) {
|
||||
return stringExpressionMetadata;
|
||||
}
|
||||
return badExpressionMetadata;
|
||||
}
|
||||
|
||||
const gd::ExpressionMetadata& MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
const gd::Platform& platform, gd::String objectType, gd::String exprType) {
|
||||
const auto& numberExpressionMetadata =
|
||||
GetObjectExpressionMetadata(platform, objectType, exprType);
|
||||
if (&numberExpressionMetadata != &badExpressionMetadata) {
|
||||
return numberExpressionMetadata;
|
||||
}
|
||||
const auto& stringExpressionMetadata =
|
||||
GetObjectStrExpressionMetadata(platform, objectType, exprType);
|
||||
|
||||
return &numberExpressionMetadata != &badExpressionMetadata
|
||||
? numberExpressionMetadata
|
||||
: &stringExpressionMetadata != &badExpressionMetadata
|
||||
? stringExpressionMetadata
|
||||
: badExpressionMetadata;
|
||||
if (&stringExpressionMetadata != &badExpressionMetadata) {
|
||||
return stringExpressionMetadata;
|
||||
}
|
||||
return badExpressionMetadata;
|
||||
}
|
||||
|
||||
const gd::ExpressionMetadata&
|
||||
@@ -380,14 +379,98 @@ MetadataProvider::GetBehaviorAnyExpressionMetadata(const gd::Platform& platform,
|
||||
gd::String exprType) {
|
||||
const auto& numberExpressionMetadata =
|
||||
GetBehaviorExpressionMetadata(platform, autoType, exprType);
|
||||
if (&numberExpressionMetadata != &badExpressionMetadata) {
|
||||
return numberExpressionMetadata;
|
||||
}
|
||||
const auto& stringExpressionMetadata =
|
||||
GetBehaviorStrExpressionMetadata(platform, autoType, exprType);
|
||||
if (&stringExpressionMetadata != &badExpressionMetadata) {
|
||||
return stringExpressionMetadata;
|
||||
}
|
||||
return badExpressionMetadata;
|
||||
}
|
||||
|
||||
return &numberExpressionMetadata != &badExpressionMetadata
|
||||
? numberExpressionMetadata
|
||||
: &stringExpressionMetadata != &badExpressionMetadata
|
||||
? stringExpressionMetadata
|
||||
: badExpressionMetadata;
|
||||
const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
FunctionCallNode& node) {
|
||||
|
||||
if (!node.behaviorName.empty()) {
|
||||
gd::String behaviorType =
|
||||
GetTypeOfBehavior(globalObjectsContainer, objectsContainer, node.behaviorName);
|
||||
return MetadataProvider::GetBehaviorAnyExpressionMetadata(
|
||||
platform, behaviorType, node.functionName);
|
||||
}
|
||||
else if (!node.objectName.empty()) {
|
||||
gd::String objectType =
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, node.objectName);
|
||||
return MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform, objectType, node.functionName);
|
||||
}
|
||||
|
||||
return MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
|
||||
}
|
||||
|
||||
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
FunctionCallNode& functionCall,
|
||||
ExpressionNode& parameter) {
|
||||
int parameterIndex = -1;
|
||||
for (int i = 0; i < functionCall.parameters.size(); i++) {
|
||||
if (functionCall.parameters.at(i).get() == ¶meter) {
|
||||
parameterIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parameterIndex < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
functionCall,
|
||||
parameterIndex);
|
||||
}
|
||||
|
||||
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
FunctionCallNode& functionCall,
|
||||
int parameterIndex) {
|
||||
// Search the parameter metadata index skipping invisible ones.
|
||||
size_t visibleParameterIndex = 0;
|
||||
size_t metadataParameterIndex =
|
||||
ExpressionParser2::WrittenParametersFirstIndex(
|
||||
functionCall.objectName, functionCall.behaviorName);
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, globalObjectsContainer, objectsContainer, functionCall);
|
||||
|
||||
if (IsBadExpressionMetadata(metadata)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO use a badMetadata instead of a nullptr?
|
||||
const gd::ParameterMetadata* parameterMetadata = nullptr;
|
||||
while (metadataParameterIndex <
|
||||
metadata.parameters.size()) {
|
||||
if (!metadata.parameters[metadataParameterIndex]
|
||||
.IsCodeOnly()) {
|
||||
if (visibleParameterIndex == parameterIndex) {
|
||||
parameterMetadata = &metadata.parameters[metadataParameterIndex];
|
||||
}
|
||||
visibleParameterIndex++;
|
||||
}
|
||||
metadataParameterIndex++;
|
||||
}
|
||||
const int visibleParameterCount = visibleParameterIndex;
|
||||
// It can be null if there are too many parameters in the expression, this text node is
|
||||
// not actually linked to a parameter expected by the function call.
|
||||
return parameterMetadata;
|
||||
}
|
||||
|
||||
MetadataProvider::~MetadataProvider() {}
|
||||
|
@@ -15,6 +15,8 @@ class ExpressionMetadata;
|
||||
class ExpressionMetadata;
|
||||
class Platform;
|
||||
class PlatformExtension;
|
||||
struct FunctionCallNode;
|
||||
struct ExpressionNode;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -234,6 +236,26 @@ class GD_CORE_API MetadataProvider {
|
||||
static const gd::ExpressionMetadata& GetObjectAnyExpressionMetadata(
|
||||
const gd::Platform& platform, gd::String objectType, gd::String exprType);
|
||||
|
||||
static const gd::ExpressionMetadata& GetFunctionCallMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
FunctionCallNode& node);
|
||||
|
||||
static const gd::ParameterMetadata* GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
FunctionCallNode& functionCall,
|
||||
ExpressionNode& parameter);
|
||||
|
||||
static const gd::ParameterMetadata* GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
FunctionCallNode& functionCall,
|
||||
int parameterIndex);
|
||||
|
||||
/**
|
||||
* Get information about an expression from its type.
|
||||
* Works for behavior expressions.
|
||||
|
@@ -35,4 +35,18 @@ void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
|
||||
name = element.GetStringAttribute("name");
|
||||
}
|
||||
|
||||
// TODO factorize in a file with an enum and helpers?
|
||||
const gd::String ParameterMetadata::numberType = "number";
|
||||
const gd::String ParameterMetadata::stringType = "string";
|
||||
|
||||
const gd::String &ParameterMetadata::GetExpressionValueType(const gd::String ¶meterType) {
|
||||
if (parameterType == "number" || gd::ParameterMetadata::IsExpression("number", parameterType)) {
|
||||
return ParameterMetadata::numberType;
|
||||
}
|
||||
if (parameterType == "string" || gd::ParameterMetadata::IsExpression("string", parameterType)) {
|
||||
return ParameterMetadata::stringType;
|
||||
}
|
||||
return parameterType;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -207,6 +207,15 @@ class GD_CORE_API ParameterMetadata {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the expression type from the parameter type.
|
||||
* Declinations of "number" and "string" types (like "forceMultiplier" or
|
||||
* "sceneName") are replaced by "number" and "string".
|
||||
*/
|
||||
static const gd::String &GetExpressionValueType(const gd::String ¶meterType);
|
||||
static const gd::String numberType;
|
||||
static const gd::String stringType;
|
||||
|
||||
/** \name Serialization
|
||||
*/
|
||||
///@{
|
||||
|
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace gd {
|
||||
void ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
gd::Project& project,
|
||||
const gd::Project& project,
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
gd::ObjectsContainer& outputObjectsContainer) {
|
||||
outputObjectsContainer.GetObjects().clear();
|
||||
@@ -59,13 +59,13 @@ void ParameterMetadataTools::IterateOverParameters(
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterValue,
|
||||
const gd::String& lastObjectName)> fn) {
|
||||
IterateOverParametersWithIndex(
|
||||
parameters,
|
||||
parametersMetadata,
|
||||
[&fn](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
fn(parameterMetadata, parameterValue, lastObjectName);
|
||||
@@ -76,17 +76,17 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName)> fn) {
|
||||
gd::String lastObjectName = "";
|
||||
for (std::size_t pNb = 0; pNb < parametersMetadata.size(); ++pNb) {
|
||||
const gd::ParameterMetadata& parameterMetadata = parametersMetadata[pNb];
|
||||
const gd::String& parameterValue =
|
||||
const gd::Expression& parameterValue =
|
||||
pNb < parameters.size() ? parameters[pNb].GetPlainString() : "";
|
||||
const gd::String& parameterValueOrDefault =
|
||||
parameterValue.empty() && parameterMetadata.optional
|
||||
? parameterMetadata.GetDefaultValue()
|
||||
const gd::Expression& parameterValueOrDefault =
|
||||
parameterValue.GetPlainString().empty() && parameterMetadata.optional
|
||||
? Expression(parameterMetadata.GetDefaultValue())
|
||||
: parameterValue;
|
||||
|
||||
fn(parameterMetadata, parameterValueOrDefault, pNb, lastObjectName);
|
||||
@@ -97,7 +97,7 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
// Search "lastObjectName" in the codebase for other place where this
|
||||
// convention is enforced.
|
||||
if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType()))
|
||||
lastObjectName = parameterValueOrDefault;
|
||||
lastObjectName = parameterValueOrDefault.GetPlainString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,7 @@ namespace gd {
|
||||
class GD_CORE_API ParameterMetadataTools {
|
||||
public:
|
||||
static void ParametersToObjectsContainer(
|
||||
gd::Project& project,
|
||||
const gd::Project& project,
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
gd::ObjectsContainer& outputObjectsContainer);
|
||||
|
||||
@@ -32,7 +32,7 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterValue,
|
||||
const gd::String& lastObjectName)> fn);
|
||||
|
||||
/**
|
||||
@@ -44,7 +44,7 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName)> fn);
|
||||
|
||||
|
@@ -346,6 +346,11 @@ gd::BehaviorMetadata& PlatformExtension::GetBehaviorMetadata(
|
||||
return badBehaviorMetadata;
|
||||
}
|
||||
|
||||
bool PlatformExtension::HasBehavior(
|
||||
const gd::String& behaviorType) const {
|
||||
return behaviorsInfo.find(behaviorType) != behaviorsInfo.end();
|
||||
}
|
||||
|
||||
gd::EffectMetadata& PlatformExtension::GetEffectMetadata(
|
||||
const gd::String& effectName) {
|
||||
if (effectsMetadata.find(effectName) != effectsMetadata.end())
|
||||
|
@@ -467,6 +467,12 @@ class GD_CORE_API PlatformExtension {
|
||||
*/
|
||||
BehaviorMetadata& GetBehaviorMetadata(const gd::String& behaviorType);
|
||||
|
||||
/**
|
||||
* \brief Return true if the extension contains a behavior associated to \a
|
||||
* behaviorType
|
||||
*/
|
||||
bool HasBehavior(const gd::String& behaviorType) const;
|
||||
|
||||
/**
|
||||
* \brief Return the metadata for the effect with the given name.
|
||||
*/
|
||||
|
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
@@ -118,27 +117,20 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const gd::String& type = parameterMetadata.type;
|
||||
|
||||
if (gd::ParameterMetadata::IsBehavior(type)) {
|
||||
if (lastObjectName == objectName) {
|
||||
if (parameterValue == oldBehaviorName) {
|
||||
if (parameterValue.GetPlainString() == oldBehaviorName) {
|
||||
instruction.SetParameter(parameterIndex,
|
||||
gd::Expression(newBehaviorName));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gd::ExpressionParser2 parser(
|
||||
platform, GetGlobalObjectsContainer(), GetObjectsContainer());
|
||||
auto node =
|
||||
gd::ParameterMetadata::IsExpression("number", type)
|
||||
? parser.ParseExpression("number", parameterValue)
|
||||
: (gd::ParameterMetadata::IsExpression("string", type)
|
||||
? parser.ParseExpression("string", parameterValue)
|
||||
: std::unique_ptr<gd::ExpressionNode>());
|
||||
auto node = parameterValue.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionBehaviorRenamer renamer(GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(),
|
||||
|
@@ -9,7 +9,6 @@
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
@@ -17,6 +16,7 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -31,7 +31,17 @@ namespace gd {
|
||||
class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionObjectsAnalyzer(EventsContext& context_) : context(context_){};
|
||||
ExpressionObjectsAnalyzer(
|
||||
const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::String &rootType_,
|
||||
EventsContext& context_) :
|
||||
platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
rootType(rootType_),
|
||||
context(context_){};
|
||||
virtual ~ExpressionObjectsAnalyzer(){};
|
||||
|
||||
protected:
|
||||
@@ -59,7 +69,8 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
if (gd::ParameterMetadata::IsObject(node.type)) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
context.AddObjectName(node.identifierName);
|
||||
}
|
||||
}
|
||||
@@ -87,6 +98,11 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {}
|
||||
|
||||
private:
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::String rootType;
|
||||
|
||||
EventsContext& context;
|
||||
};
|
||||
|
||||
@@ -102,7 +118,7 @@ bool EventsContextAnalyzer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
instruction.GetParameters(),
|
||||
instrInfo.parameters,
|
||||
[this](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterValue,
|
||||
const gd::String& lastObjectName) {
|
||||
AnalyzeParameter(platform,
|
||||
project,
|
||||
@@ -129,16 +145,14 @@ void EventsContextAnalyzer::AnalyzeParameter(
|
||||
if (ParameterMetadata::IsObject(type)) {
|
||||
context.AddObjectName(value);
|
||||
} else if (ParameterMetadata::IsExpression("number", type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("number", value);
|
||||
auto node = parameter.GetRootNode();
|
||||
|
||||
ExpressionObjectsAnalyzer analyzer(context);
|
||||
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "number", context);
|
||||
node->Visit(analyzer);
|
||||
} else if (ParameterMetadata::IsExpression("string", type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("string", value);
|
||||
auto node = parameter.GetRootNode();
|
||||
|
||||
ExpressionObjectsAnalyzer analyzer(context);
|
||||
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "string", context);
|
||||
node->Visit(analyzer);
|
||||
} else if (ParameterMetadata::IsBehavior(type)) {
|
||||
context.AddBehaviorName(lastObjectName, value);
|
||||
|
@@ -11,7 +11,6 @@
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
@@ -20,6 +19,7 @@
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/IDE/Events/InstructionSentenceFormatter.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -34,18 +34,30 @@ const gd::String EventsRefactorer::searchIgnoredCharacters = ";:,#()";
|
||||
*/
|
||||
class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionObjectRenamer(const gd::String& objectName_,
|
||||
ExpressionObjectRenamer(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::String &rootType_,
|
||||
const gd::String& objectName_,
|
||||
const gd::String& objectNewName_)
|
||||
: hasDoneRenaming(false),
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
rootType(rootType_),
|
||||
hasDoneRenaming(false),
|
||||
objectName(objectName_),
|
||||
objectNewName(objectNewName_){};
|
||||
virtual ~ExpressionObjectRenamer(){};
|
||||
|
||||
static bool Rename(gd::ExpressionNode& node,
|
||||
static bool Rename(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node,
|
||||
const gd::String& objectName,
|
||||
const gd::String& objectNewName) {
|
||||
if (ExpressionValidator::HasNoErrors(node)) {
|
||||
ExpressionObjectRenamer renamer(objectName, objectNewName);
|
||||
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
|
||||
ExpressionObjectRenamer renamer(platform, globalObjectsContainer, objectsContainer, rootType, objectName, objectNewName);
|
||||
node.Visit(renamer);
|
||||
|
||||
return renamer.HasDoneRenaming();
|
||||
@@ -81,7 +93,8 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
if (gd::ParameterMetadata::IsObject(node.type) &&
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == objectName) {
|
||||
hasDoneRenaming = true;
|
||||
node.identifierName = objectNewName;
|
||||
@@ -108,6 +121,11 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
bool hasDoneRenaming;
|
||||
const gd::String& objectName;
|
||||
const gd::String& objectNewName;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -118,14 +136,27 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
*/
|
||||
class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionObjectFinder(const gd::String& objectName_)
|
||||
: hasObject(false), objectName(objectName_){};
|
||||
ExpressionObjectFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::String &rootType_,
|
||||
const gd::String& objectName_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
rootType(rootType_),
|
||||
hasObject(false),
|
||||
objectName(objectName_){};
|
||||
virtual ~ExpressionObjectFinder(){};
|
||||
|
||||
static bool CheckIfHasObject(gd::ExpressionNode& node,
|
||||
static bool CheckIfHasObject(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node,
|
||||
const gd::String& objectName) {
|
||||
if (ExpressionValidator::HasNoErrors(node)) {
|
||||
ExpressionObjectFinder finder(objectName);
|
||||
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
|
||||
ExpressionObjectFinder finder(platform, globalObjectsContainer, objectsContainer, rootType, objectName);
|
||||
node.Visit(finder);
|
||||
|
||||
return finder.HasFoundObject();
|
||||
@@ -161,7 +192,8 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
if (gd::ParameterMetadata::IsObject(node.type) &&
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == objectName) {
|
||||
hasObject = true;
|
||||
}
|
||||
@@ -184,6 +216,11 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
private:
|
||||
bool hasObject;
|
||||
const gd::String& objectName;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
@@ -205,11 +242,9 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
// Replace object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"number", actions[aId].GetParameter(pNb).GetPlainString());
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
|
||||
actions[aId].SetParameter(
|
||||
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -217,11 +252,9 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
// Replace object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"string", actions[aId].GetParameter(pNb).GetPlainString());
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
|
||||
actions[aId].SetParameter(
|
||||
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -263,11 +296,9 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
// Replace object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"number", conditions[cId].GetParameter(pNb).GetPlainString());
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
|
||||
conditions[cId].SetParameter(
|
||||
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -275,11 +306,9 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
// Replace object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"string", conditions[cId].GetParameter(pNb).GetPlainString());
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
|
||||
conditions[cId].SetParameter(
|
||||
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -316,20 +345,18 @@ bool EventsRefactorer::RenameObjectInEventParameters(
|
||||
// Replace object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression("number",
|
||||
parameterMetadata.GetType())) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("number", expression.GetPlainString());
|
||||
auto node = expression.GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
|
||||
expression = ExpressionParser2NodePrinter::PrintNode(*node);
|
||||
}
|
||||
}
|
||||
// Replace object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression("string",
|
||||
parameterMetadata.GetType())) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("string", expression.GetPlainString());
|
||||
auto node = expression.GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
|
||||
expression = ExpressionParser2NodePrinter::PrintNode(*node);
|
||||
}
|
||||
}
|
||||
@@ -405,11 +432,9 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
// Find object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"number", actions[aId].GetParameter(pNb).GetPlainString());
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "number", *node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
@@ -417,11 +442,9 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
// Find object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"string", actions[aId].GetParameter(pNb).GetPlainString());
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "string", *node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
@@ -469,11 +492,9 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
// Find object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"number", conditions[cId].GetParameter(pNb).GetPlainString());
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "number", *node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
@@ -481,11 +502,9 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
// Find object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"string", conditions[cId].GetParameter(pNb).GetPlainString());
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "string", *node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@
|
||||
#include "EventsVariablesFinder.h"
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
@@ -32,10 +31,16 @@ namespace gd {
|
||||
class GD_CORE_API ExpressionParameterSearcher
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionParameterSearcher(std::set<gd::String>& results_,
|
||||
ExpressionParameterSearcher(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
std::set<gd::String>& results_,
|
||||
const gd::String& parameterType_,
|
||||
const gd::String& objectName_ = "")
|
||||
: results(results_),
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
results(results_),
|
||||
parameterType(parameterType_),
|
||||
objectName(objectName_){};
|
||||
virtual ~ExpressionParameterSearcher(){};
|
||||
@@ -68,10 +73,22 @@ class GD_CORE_API ExpressionParameterSearcher
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
bool considerFunction = objectName.empty() || node.objectName == objectName;
|
||||
|
||||
const gd::ExpressionMetadata &metadata = node.objectName.empty() ?
|
||||
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName) :
|
||||
MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform,
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
|
||||
node.functionName);
|
||||
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node.parameters.size() &&
|
||||
i < node.expressionMetadata.parameters.size();
|
||||
i < metadata.parameters.size();
|
||||
++i) {
|
||||
auto& parameterMetadata = node.expressionMetadata.parameters[i];
|
||||
auto& parameterMetadata = metadata.parameters[i];
|
||||
if (considerFunction && parameterMetadata.GetType() == parameterType) {
|
||||
// Store the value of the parameter
|
||||
results.insert(
|
||||
@@ -84,6 +101,10 @@ class GD_CORE_API ExpressionParameterSearcher
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {}
|
||||
|
||||
private:
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
|
||||
std::set<gd::String>& results; ///< Reference to the std::set where argument
|
||||
///< values must be stored.
|
||||
gd::String parameterType; ///< The type of the parameters to be searched for.
|
||||
@@ -165,24 +186,18 @@ std::set<gd::String> EventsVariablesFinder::FindArgumentsInInstructions(
|
||||
}
|
||||
// Search in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"number", instructions[aId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
ExpressionParameterSearcher searcher(
|
||||
results, parameterType, objectName);
|
||||
node->Visit(searcher);
|
||||
}
|
||||
// Search in gd::String expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].type) ||
|
||||
ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression(
|
||||
"number", instructions[aId].GetParameter(pNb).GetPlainString());
|
||||
auto node = instructions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
ExpressionParameterSearcher searcher(
|
||||
results, parameterType, objectName);
|
||||
platform,
|
||||
project,
|
||||
layout,
|
||||
results,
|
||||
parameterType,
|
||||
objectName);
|
||||
node->Visit(searcher);
|
||||
}
|
||||
// Remember the value of the last "object" parameter.
|
||||
|
@@ -15,6 +15,8 @@
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
@@ -290,7 +292,11 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
* and returns completions for it.
|
||||
*/
|
||||
static std::vector<ExpressionCompletionDescription>
|
||||
GetCompletionDescriptionsFor(gd::ExpressionNode& node,
|
||||
GetCompletionDescriptionsFor(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node,
|
||||
size_t searchedPosition) {
|
||||
gd::ExpressionNodeLocationFinder finder(searchedPosition);
|
||||
node.Visit(finder);
|
||||
@@ -303,6 +309,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
|
||||
gd::ExpressionNode* maybeParentNodeAtLocation = finder.GetParentNode();
|
||||
gd::ExpressionCompletionFinder autocompletionProvider(
|
||||
platform, globalObjectsContainer, objectsContainer, rootType,
|
||||
searchedPosition, maybeParentNodeAtLocation);
|
||||
nodeAtLocation->Visit(autocompletionProvider);
|
||||
return autocompletionProvider.GetCompletionDescriptions();
|
||||
@@ -320,19 +327,21 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
// No completions.
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override {
|
||||
// No completions
|
||||
@@ -344,6 +353,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
FunctionCallNode* functionCall =
|
||||
dynamic_cast<FunctionCallNode*>(maybeParentNodeAtLocation);
|
||||
if (functionCall != nullptr) {
|
||||
|
||||
int parameterIndex = -1;
|
||||
for (int i = 0; i < functionCall->parameters.size(); i++) {
|
||||
if (functionCall->parameters.at(i).get() == &node) {
|
||||
@@ -359,15 +369,16 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
size_t metadataParameterIndex =
|
||||
ExpressionParser2::WrittenParametersFirstIndex(
|
||||
functionCall->objectName, functionCall->behaviorName);
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, globalObjectsContainer, objectsContainer, *functionCall);
|
||||
|
||||
const gd::ParameterMetadata* parameterMetadata = nullptr;
|
||||
while (metadataParameterIndex <
|
||||
functionCall->expressionMetadata.parameters.size()) {
|
||||
if (!functionCall->expressionMetadata.parameters[metadataParameterIndex]
|
||||
metadata.parameters.size()) {
|
||||
if (!metadata.parameters[metadataParameterIndex]
|
||||
.IsCodeOnly()) {
|
||||
if (visibleParameterIndex == parameterIndex) {
|
||||
parameterMetadata = &functionCall->expressionMetadata
|
||||
.parameters[metadataParameterIndex];
|
||||
parameterMetadata = &metadata.parameters[metadataParameterIndex];
|
||||
}
|
||||
visibleParameterIndex++;
|
||||
}
|
||||
@@ -398,12 +409,21 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
// Variable fields doesn't use expression completion,
|
||||
// so the object will be found inside the expression itself.
|
||||
"",
|
||||
node);
|
||||
completions.push_back(ExpressionCompletionDescription::ForVariable(
|
||||
node.type,
|
||||
type,
|
||||
node.name,
|
||||
node.location.GetStartPosition(),
|
||||
node.location.GetEndPosition(),
|
||||
node.objectName));
|
||||
objectName));
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
// No completions
|
||||
@@ -413,35 +433,69 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// No completions
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
if (gd::ParameterMetadata::IsObject(node.type)) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
// Only show completions of objects if an object is required
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type,
|
||||
type,
|
||||
node.identifierName,
|
||||
node.location.GetStartPosition(),
|
||||
node.location.GetEndPosition()));
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
// Variable fields doesn't use expression completion,
|
||||
// so the object will be found inside the expression itself.
|
||||
"",
|
||||
node);
|
||||
completions.push_back(ExpressionCompletionDescription::ForVariable(
|
||||
type,
|
||||
node.identifierName,
|
||||
node.location.GetStartPosition(),
|
||||
node.location.GetEndPosition(),
|
||||
objectName));
|
||||
} else {
|
||||
// Show completions for expressions and objects otherwise.
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type,
|
||||
node.identifierName,
|
||||
node.location.GetStartPosition(),
|
||||
node.location.GetEndPosition()));
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type,
|
||||
node.identifierName,
|
||||
node.location.GetStartPosition(),
|
||||
node.location.GetEndPosition()));
|
||||
// Object function or behavior name
|
||||
if (IsCaretOn(node.identifierNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
type,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation.GetStartPosition(),
|
||||
node.identifierNameLocation.GetEndPosition()));
|
||||
if (!node.identifierNameDotLocation.IsValid()) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
type,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation.GetStartPosition(),
|
||||
node.identifierNameLocation.GetEndPosition()));
|
||||
}
|
||||
} else if (IsCaretOn(node.identifierNameDotLocation) ||
|
||||
IsCaretOn(node.childIdentifierNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForBehavior(
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation.GetStartPosition(),
|
||||
node.childIdentifierNameLocation.GetEndPosition(),
|
||||
node.identifierName));
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
type,
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation.GetStartPosition(),
|
||||
node.childIdentifierNameLocation.GetEndPosition(),
|
||||
node.identifierName));
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
if (!node.behaviorFunctionName.empty() ||
|
||||
node.behaviorNameNamespaceSeparatorLocation.IsValid()) {
|
||||
// Behavior function (or behavior function being written, with the
|
||||
// function name missing)
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type,
|
||||
type,
|
||||
node.objectName,
|
||||
node.objectNameLocation.GetStartPosition(),
|
||||
node.objectNameLocation.GetEndPosition()));
|
||||
@@ -455,7 +509,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
} else if (IsCaretOn(node.behaviorNameNamespaceSeparatorLocation) ||
|
||||
IsCaretOn(node.behaviorFunctionNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type,
|
||||
type,
|
||||
node.behaviorFunctionName,
|
||||
node.behaviorFunctionNameLocation.GetStartPosition(),
|
||||
node.behaviorFunctionNameLocation.GetEndPosition(),
|
||||
@@ -466,7 +520,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// Object function or behavior name
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type,
|
||||
type,
|
||||
node.objectName,
|
||||
node.objectNameLocation.GetStartPosition(),
|
||||
node.objectNameLocation.GetEndPosition()));
|
||||
@@ -478,7 +532,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
|
||||
node.objectName));
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type,
|
||||
type,
|
||||
node.objectFunctionOrBehaviorName,
|
||||
node.objectFunctionOrBehaviorNameLocation.GetStartPosition(),
|
||||
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
|
||||
@@ -487,6 +541,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
bool isCaretOnParenthesis = IsCaretOn(node.openingParenthesisLocation) ||
|
||||
IsCaretOn(node.closingParenthesisLocation);
|
||||
|
||||
@@ -494,7 +549,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// Behavior function
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type,
|
||||
type,
|
||||
node.objectName,
|
||||
node.objectNameLocation.GetStartPosition(),
|
||||
node.objectNameLocation.GetEndPosition()));
|
||||
@@ -507,7 +562,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
node.objectName));
|
||||
} else {
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type,
|
||||
type,
|
||||
node.functionName,
|
||||
node.functionNameLocation.GetStartPosition(),
|
||||
node.functionNameLocation.GetEndPosition(),
|
||||
@@ -519,7 +574,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// Object function
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type,
|
||||
type,
|
||||
node.objectName,
|
||||
node.objectNameLocation.GetStartPosition(),
|
||||
node.objectNameLocation.GetEndPosition()));
|
||||
@@ -538,7 +593,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type,
|
||||
type,
|
||||
node.functionName,
|
||||
node.functionNameLocation.GetStartPosition(),
|
||||
node.functionNameLocation.GetEndPosition(),
|
||||
@@ -548,7 +603,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
} else {
|
||||
// Free function
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type,
|
||||
type,
|
||||
node.functionName,
|
||||
node.functionNameLocation.GetStartPosition(),
|
||||
node.functionNameLocation.GetEndPosition())
|
||||
@@ -556,13 +611,14 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type,
|
||||
type,
|
||||
node.text,
|
||||
node.location.GetStartPosition(),
|
||||
node.location.GetEndPosition()));
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type,
|
||||
type,
|
||||
node.text,
|
||||
node.location.GetStartPosition(),
|
||||
node.location.GetEndPosition()));
|
||||
@@ -578,14 +634,27 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
(inclusive && searchedPosition <= location.GetEndPosition())));
|
||||
}
|
||||
|
||||
ExpressionCompletionFinder(size_t searchedPosition_,
|
||||
ExpressionCompletionFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::String &rootType_,
|
||||
size_t searchedPosition_,
|
||||
gd::ExpressionNode* maybeParentNodeAtLocation_)
|
||||
: searchedPosition(searchedPosition_),
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
rootType(rootType_),
|
||||
searchedPosition(searchedPosition_),
|
||||
maybeParentNodeAtLocation(maybeParentNodeAtLocation_){};
|
||||
|
||||
std::vector<ExpressionCompletionDescription> completions;
|
||||
size_t searchedPosition;
|
||||
gd::ExpressionNode* maybeParentNodeAtLocation;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
123
Core/GDCore/IDE/Events/ExpressionLeftSideTypeFinder.h
Normal file
123
Core/GDCore/IDE/Events/ExpressionLeftSideTypeFinder.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EXPRESSIONLEFTSIDETYPEFINDER_H
|
||||
#define GDCORE_EXPRESSIONLEFTSIDETYPEFINDER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#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/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Find the type of the node at the left side of operations.
|
||||
*
|
||||
* \see gd::ExpressionTypeFinder
|
||||
*/
|
||||
class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Helper function to find the type of the node at the left side of
|
||||
* operations.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionLeftSideTypeFinder typeFinder(
|
||||
platform, globalObjectsContainer, objectsContainer);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
|
||||
virtual ~ExpressionLeftSideTypeFinder(){};
|
||||
|
||||
protected:
|
||||
ExpressionLeftSideTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
type("unknown") {};
|
||||
|
||||
const gd::String &GetType() {
|
||||
return type;
|
||||
};
|
||||
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
node.expression->Visit(*this);
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
node.leftHandSide->Visit(*this);
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
node.factor->Visit(*this);
|
||||
}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
node.expression->Visit(*this);
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override {
|
||||
type = "number";
|
||||
}
|
||||
void OnVisitTextNode(TextNode& node) override {
|
||||
type = "string";
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, globalObjectsContainer, objectsContainer, node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
type = "unknown";
|
||||
}
|
||||
else {
|
||||
type = metadata.GetReturnType();
|
||||
}
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
type = "unknown";
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
type = "unknown";
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
type = "unknown";
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
type = "unknown";
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
type = "unknown";
|
||||
}
|
||||
|
||||
private:
|
||||
gd::String type;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EXPRESSIONLEFTSIDETYPEFINDER_H
|
35
Core/GDCore/IDE/Events/ExpressionTypeFinder.cpp
Normal file
35
Core/GDCore/IDE/Events/ExpressionTypeFinder.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
// TODO factorize in a file with an enum and helpers?
|
||||
const gd::String ExpressionTypeFinder::unknownType = "unknown";
|
||||
const gd::String ExpressionTypeFinder::numberType = "number";
|
||||
const gd::String ExpressionTypeFinder::stringType = "string";
|
||||
const gd::String ExpressionTypeFinder::numberOrStringType = "number|string";
|
||||
|
||||
} // namespace gd
|
198
Core/GDCore/IDE/Events/ExpressionTypeFinder.h
Normal file
198
Core/GDCore/IDE/Events/ExpressionTypeFinder.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EXPRESSIONTYPEFINDER_H
|
||||
#define GDCORE_EXPRESSIONTYPEFINDER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/IDE/Events/ExpressionLeftSideTypeFinder.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#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/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Find the type of the expression or sub-expression that a given node
|
||||
* represents.
|
||||
*
|
||||
* The type returned by this worker is a mix of:
|
||||
* - an expected type looking up like a parameter declaration
|
||||
* - an actual type looking down, but only looking at the most left branch
|
||||
* (using ExpressionLeftSideTypeFinder)
|
||||
*
|
||||
* This logic was built with the constraint of following a parser that can't
|
||||
* know the right side. Now that it is extracted, it could be enhanced if needed.
|
||||
*
|
||||
* \see gd::ExpressionParser2
|
||||
*/
|
||||
class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Helper function to find the type of the expression or
|
||||
* sub-expression that a given node represents.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionTypeFinder typeFinder(
|
||||
platform, globalObjectsContainer, objectsContainer, rootType);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
|
||||
virtual ~ExpressionTypeFinder(){};
|
||||
|
||||
protected:
|
||||
ExpressionTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::String &rootType_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
rootType(rootType_),
|
||||
type(ExpressionTypeFinder::unknownType),
|
||||
child(nullptr) {};
|
||||
|
||||
const gd::String &GetType() {
|
||||
return gd::ParameterMetadata::GetExpressionValueType(type);
|
||||
};
|
||||
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
VisitParent(node);
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
VisitParent(node);
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
VisitParent(node);
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override {
|
||||
type = ExpressionTypeFinder::numberType;
|
||||
}
|
||||
void OnVisitTextNode(TextNode& node) override {
|
||||
type = ExpressionTypeFinder::stringType;
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
VisitParent(node);
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
VisitParent(node);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
VisitParent(node);
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
VisitParent(node);
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
VisitParent(node);
|
||||
}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
if (child == nullptr) {
|
||||
type = ExpressionTypeFinder::unknownType;
|
||||
}
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
node);
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
type = leftSideType;
|
||||
}
|
||||
else {
|
||||
type = ExpressionTypeFinder::numberOrStringType;
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (child == nullptr) {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, globalObjectsContainer, objectsContainer, node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
VisitParent(node);
|
||||
}
|
||||
else {
|
||||
type = metadata.GetReturnType();
|
||||
}
|
||||
}
|
||||
else {
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
gd::MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
node,
|
||||
*child);
|
||||
if (parameterMetadata == nullptr || parameterMetadata->GetType().empty()) {
|
||||
type = ExpressionTypeFinder::unknownType;
|
||||
}
|
||||
else {
|
||||
type = parameterMetadata->GetType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
inline void VisitParent(ExpressionNode& node) {
|
||||
child = &node;
|
||||
if (node.parent != nullptr) {
|
||||
node.parent->Visit(*this);
|
||||
}
|
||||
else if (rootType == ExpressionTypeFinder::numberOrStringType) {
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
node);
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
type = leftSideType;
|
||||
}
|
||||
else {
|
||||
type = rootType;
|
||||
}
|
||||
}
|
||||
else {
|
||||
type = rootType;
|
||||
}
|
||||
}
|
||||
|
||||
static const gd::String unknownType;
|
||||
static const gd::String numberType;
|
||||
static const gd::String stringType;
|
||||
static const gd::String numberOrStringType;
|
||||
|
||||
gd::String type;
|
||||
ExpressionNode *child;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EXPRESSIONTYPEFINDER_H
|
300
Core/GDCore/IDE/Events/ExpressionValidator.cpp
Normal file
300
Core/GDCore/IDE/Events/ExpressionValidator.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* Return the minimum number of parameters, starting from a given parameter
|
||||
* (by convention, 1 for object functions and 2 for behavior functions).
|
||||
*/
|
||||
size_t GetMinimumParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
size_t initialParameterIndex) {
|
||||
size_t nb = 0;
|
||||
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
|
||||
if (!parameters[i].optional && !parameters[i].codeOnly) nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of parameters, starting from a given parameter
|
||||
* (by convention, 1 for object functions and 2 for behavior functions).
|
||||
*/
|
||||
size_t GetMaximumParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
size_t initialParameterIndex) {
|
||||
size_t nb = 0;
|
||||
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
|
||||
if (!parameters[i].codeOnly) nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::FunctionCallNode& function) {
|
||||
|
||||
ReportAnyError(function);
|
||||
|
||||
gd::String objectType = function.objectName.empty() ? "" :
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, function.objectName);
|
||||
|
||||
gd::String behaviorType = function.behaviorName.empty() ? "" :
|
||||
GetTypeOfBehavior(globalObjectsContainer, objectsContainer, function.behaviorName);
|
||||
|
||||
const gd::ExpressionMetadata &metadata = function.behaviorName.empty() ?
|
||||
function.objectName.empty() ?
|
||||
MetadataProvider::GetAnyExpressionMetadata(platform, function.functionName) :
|
||||
MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform, objectType, function.functionName) :
|
||||
MetadataProvider::GetBehaviorAnyExpressionMetadata(
|
||||
platform, behaviorType, function.functionName);
|
||||
|
||||
if (!function.objectName.empty()) {
|
||||
// If the function needs a capability on the object that may not be covered
|
||||
// by all objects, check it now.
|
||||
if (!metadata.GetRequiredBaseObjectCapability().empty()) {
|
||||
const gd::ObjectMetadata &objectMetadata =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
|
||||
if (objectMetadata.IsUnsupportedBaseObjectCapability(
|
||||
metadata.GetRequiredBaseObjectCapability())) {
|
||||
RaiseTypeError(
|
||||
_("This expression exists, but it can't be used on this object."),
|
||||
function.objectNameLocation);
|
||||
return StringToType(metadata.GetReturnType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type returnType = StringToType(metadata.GetReturnType());
|
||||
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
RaiseError(
|
||||
"invalid_function_name",
|
||||
_("Cannot find an expression with this name: ") +
|
||||
function.functionName + "\n" +
|
||||
_("Double check that you've not made any typo in the name."),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
|
||||
// Validate the type of the function
|
||||
if (returnType == Type::Number) {
|
||||
if (parentType == Type::String) {
|
||||
RaiseTypeError(
|
||||
_("You tried to use an expression that returns a number, but a "
|
||||
"string is expected. Use `ToString` if you need to convert a "
|
||||
"number to a string."),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
else if (parentType != Type::Number && parentType != Type::NumberOrString) {
|
||||
RaiseTypeError(_("You tried to use an expression that returns a "
|
||||
"number, but another type is expected:") +
|
||||
" " + TypeToString(parentType),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
} else if (returnType == Type::String) {
|
||||
if (parentType == Type::Number) {
|
||||
RaiseTypeError(
|
||||
_("You tried to use an expression that returns a string, but a "
|
||||
"number is expected. Use `ToNumber` if you need to convert a "
|
||||
"string to a number."),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
else if (parentType != Type::String && parentType != Type::NumberOrString) {
|
||||
RaiseTypeError(_("You tried to use an expression that returns a "
|
||||
"string, but another type is expected:") +
|
||||
" " + TypeToString(parentType),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
} else {
|
||||
if (parentType != returnType) {
|
||||
RaiseTypeError(
|
||||
_("You tried to use an expression with the wrong return type:") + " " +
|
||||
TypeToString(returnType),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate parameters count
|
||||
size_t minParametersCount = GetMinimumParametersNumber(
|
||||
metadata.parameters,
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName, function.behaviorName));
|
||||
size_t maxParametersCount = GetMaximumParametersNumber(
|
||||
metadata.parameters,
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName, function.behaviorName));
|
||||
if (function.parameters.size() < minParametersCount ||
|
||||
function.parameters.size() > maxParametersCount) {
|
||||
gd::String expectedCountMessage =
|
||||
minParametersCount == maxParametersCount
|
||||
? _("The number of parameters must be exactly ") +
|
||||
gd::String::From(minParametersCount)
|
||||
: _("The number of parameters must be: ") +
|
||||
gd::String::From(minParametersCount) + "-" +
|
||||
gd::String::From(maxParametersCount);
|
||||
|
||||
if (function.parameters.size() < minParametersCount) {
|
||||
RaiseError(
|
||||
"too_few_parameters",
|
||||
_("You have not entered enough parameters for the expression.") + " " +
|
||||
expectedCountMessage,
|
||||
function.location);
|
||||
}
|
||||
else {
|
||||
RaiseError(
|
||||
"extra_parameter",
|
||||
_("This parameter was not expected by this expression. Remove it "
|
||||
"or verify that you've entered the proper expression name.") + " " +
|
||||
expectedCountMessage,
|
||||
ExpressionParserLocation(
|
||||
function.parameters[maxParametersCount]->location.GetStartPosition(),
|
||||
function.location.GetEndPosition() - 1));
|
||||
}
|
||||
return returnType;
|
||||
}
|
||||
|
||||
// TODO: reverse the order of diagnostic?
|
||||
size_t writtenParametersFirstIndex =
|
||||
ExpressionParser2::WrittenParametersFirstIndex(
|
||||
function.objectName, function.behaviorName);
|
||||
int metadataIndex = writtenParametersFirstIndex;
|
||||
for (int parameterIndex = 0; parameterIndex < function.parameters.size(); parameterIndex++) {
|
||||
auto& parameter = function.parameters[parameterIndex];
|
||||
while (metadata.GetParameters()[metadataIndex].IsCodeOnly()) {
|
||||
// The sizes are already checked above.
|
||||
metadataIndex++;
|
||||
}
|
||||
auto& parameterMetadata = metadata.GetParameters()[metadataIndex];
|
||||
|
||||
if (!parameterMetadata.IsOptional() || dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) {
|
||||
auto currentParentType = parentType;
|
||||
parentType = StringToType(parameterMetadata.GetType());
|
||||
parameter->Visit(*this);
|
||||
parentType = currentParentType;
|
||||
|
||||
const gd::String &expectedParameterType = parameterMetadata.GetType();
|
||||
if (gd::ParameterMetadata::IsExpression(
|
||||
ExpressionValidator::variableTypeString, expectedParameterType)) {
|
||||
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr
|
||||
&& dynamic_cast<VariableNode *>(parameter.get()) == nullptr) {
|
||||
RaiseError(
|
||||
"malformed_variable_parameter",
|
||||
_("A variable name was expected but something else was "
|
||||
"written. Enter just the name of the variable for this "
|
||||
"parameter."),
|
||||
parameter->location);
|
||||
}
|
||||
}
|
||||
else if (gd::ParameterMetadata::IsObject(expectedParameterType)) {
|
||||
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr) {
|
||||
RaiseError(
|
||||
"malformed_object_parameter",
|
||||
_("An object name was expected but something else was "
|
||||
"written. Enter just the name of the object for this "
|
||||
"parameter."),
|
||||
parameter->location);
|
||||
}
|
||||
}
|
||||
// String and number are already checked in children.
|
||||
else if (!gd::ParameterMetadata::IsExpression(
|
||||
ExpressionValidator::numberTypeString, expectedParameterType)
|
||||
&& !gd::ParameterMetadata::IsExpression(
|
||||
ExpressionValidator::stringTypeString, expectedParameterType)) {
|
||||
RaiseError(
|
||||
"unknown_parameter_type",
|
||||
_("This function is improperly set up. Reach out to the "
|
||||
"extension developer or a GDevelop maintainer to fix "
|
||||
"this issue"),
|
||||
parameter->location);
|
||||
}
|
||||
}
|
||||
metadataIndex++;
|
||||
}
|
||||
return returnType;
|
||||
}
|
||||
|
||||
// TODO factorize in a file with an enum and helpers?
|
||||
const gd::String ExpressionValidator::unknownTypeString = "unknown";
|
||||
const gd::String ExpressionValidator::numberTypeString = "number";
|
||||
const gd::String ExpressionValidator::stringTypeString = "string";
|
||||
const gd::String ExpressionValidator::numberOrStringTypeString = "number|string";
|
||||
const gd::String ExpressionValidator::variableTypeString = "variable";
|
||||
const gd::String ExpressionValidator::objectTypeString = "object";
|
||||
const gd::String ExpressionValidator::emptyTypeString = "empty";
|
||||
|
||||
const gd::String &ExpressionValidator::TypeToString(Type type) {
|
||||
switch (type) {
|
||||
case Type::Unknown:
|
||||
return unknownTypeString;
|
||||
case Type::Number:
|
||||
return numberTypeString;
|
||||
case Type::String:
|
||||
return stringTypeString;
|
||||
case Type::NumberOrString:
|
||||
return numberOrStringTypeString;
|
||||
case Type::Variable:
|
||||
return variableTypeString;
|
||||
case Type::Object:
|
||||
return objectTypeString;
|
||||
case Type::Empty:
|
||||
return emptyTypeString;
|
||||
}
|
||||
return unknownTypeString;
|
||||
}
|
||||
|
||||
ExpressionValidator::Type ExpressionValidator::StringToType(const gd::String &type) {
|
||||
if (type == ExpressionValidator::numberTypeString
|
||||
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::numberTypeString, type)) {
|
||||
return Type::Number;
|
||||
}
|
||||
if (type == ExpressionValidator::stringTypeString
|
||||
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::stringTypeString, type)) {
|
||||
return Type::String;
|
||||
}
|
||||
if (type == ExpressionValidator::numberOrStringTypeString) {
|
||||
return Type::NumberOrString;
|
||||
}
|
||||
if (type == ExpressionValidator::variableTypeString
|
||||
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::variableTypeString, type)) {
|
||||
return Type::Variable;
|
||||
}
|
||||
if (type == ExpressionValidator::objectTypeString
|
||||
|| gd::ParameterMetadata::IsObject(type)) {
|
||||
return Type::Object;
|
||||
}
|
||||
return Type::Unknown;
|
||||
}
|
||||
} // namespace gd
|
@@ -10,6 +10,10 @@
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
@@ -28,15 +32,27 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionValidator(){};
|
||||
ExpressionValidator(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::String &rootType_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))),
|
||||
childType(Type::Unknown) {};
|
||||
virtual ~ExpressionValidator(){};
|
||||
|
||||
/**
|
||||
* \brief Helper function to check if a given node does not contain
|
||||
* any error.
|
||||
*/
|
||||
static bool HasNoErrors(gd::ExpressionNode& node) {
|
||||
gd::ExpressionValidator validator;
|
||||
static bool HasNoErrors(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionValidator validator(platform, globalObjectsContainer, objectsContainer, rootType);
|
||||
node.Visit(validator);
|
||||
return validator.GetErrors().empty();
|
||||
}
|
||||
@@ -56,52 +72,247 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
node.expression->Visit(*this);
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
node.leftHandSide->Visit(*this);
|
||||
ReportAnyError(node);
|
||||
|
||||
node.leftHandSide->Visit(*this);
|
||||
const Type leftType = childType;
|
||||
|
||||
if (leftType == Type::Number) {
|
||||
if (node.op == ' ') {
|
||||
RaiseError("syntax_error",
|
||||
"No operator found. Did you forget to enter an operator (like +, -, "
|
||||
"* or /) between numbers or expressions?", node.rightHandSide->location);
|
||||
}
|
||||
}
|
||||
else if (leftType == Type::String) {
|
||||
if (node.op == ' ') {
|
||||
RaiseError("syntax_error",
|
||||
"You must add the operator + between texts or expressions. For "
|
||||
"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."),
|
||||
ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
|
||||
}
|
||||
} else if (leftType == Type::Object) {
|
||||
RaiseOperatorError(
|
||||
_("Operators (+, -, /, *) can't be used with an object name. Remove "
|
||||
"the operator."),
|
||||
node.rightHandSide->location);
|
||||
} else if (leftType == Type::Variable) {
|
||||
RaiseOperatorError(
|
||||
_("Operators (+, -, /, *) can't be used in variable names. Remove "
|
||||
"the operator from the variable name."),
|
||||
node.rightHandSide->location);
|
||||
}
|
||||
|
||||
parentType = leftType;
|
||||
node.rightHandSide->Visit(*this);
|
||||
const Type rightType = childType;
|
||||
|
||||
childType = leftType;
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
node.factor->Visit(*this);
|
||||
const Type rightType = childType;
|
||||
|
||||
if (rightType == Type::Number) {
|
||||
if (node.op != '+' && node.op != '-') {
|
||||
// This is actually a dead code because the parser takes them as
|
||||
// binary operations with an empty left side which makes as much sense.
|
||||
RaiseTypeError(
|
||||
_("You've used an \"unary\" operator that is not supported. Operator "
|
||||
"should be "
|
||||
"either + or -."),
|
||||
node.location);
|
||||
}
|
||||
} else if (rightType == Type::String) {
|
||||
RaiseTypeError(
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts, and must be placed between two texts (or "
|
||||
"expressions)."),
|
||||
node.location);
|
||||
} else if (rightType == Type::Object) {
|
||||
RaiseTypeError(
|
||||
_("Operators (+, -) can't be used with an object name. Remove the "
|
||||
"operator."),
|
||||
node.location);
|
||||
} else if (rightType == Type::Variable) {
|
||||
RaiseTypeError(
|
||||
_("Operators (+, -) can't be used in variable names. Remove "
|
||||
"the operator from the variable name."),
|
||||
node.location);
|
||||
}
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override {
|
||||
ReportAnyError(node);
|
||||
childType = Type::Number;
|
||||
CheckType(parentType, childType, node.location);
|
||||
}
|
||||
void OnVisitTextNode(TextNode& node) override {
|
||||
ReportAnyError(node);
|
||||
childType = Type::String;
|
||||
CheckType(parentType, childType, node.location);
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override { ReportAnyError(node); }
|
||||
void OnVisitTextNode(TextNode& node) override { ReportAnyError(node); }
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
ReportAnyError(node);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
if (node.child) {
|
||||
node.child->Visit(*this);
|
||||
}
|
||||
childType = Type::Variable;
|
||||
CheckType(parentType, childType, node.location);
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
if (node.child) {
|
||||
node.child->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
|
||||
Type currentParentType = parentType;
|
||||
parentType = Type::NumberOrString;
|
||||
node.expression->Visit(*this);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
parentType = currentParentType;
|
||||
|
||||
if (node.child) {
|
||||
node.child->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
ReportAnyError(node);
|
||||
if (parentType == Type::String) {
|
||||
RaiseTypeError(_("You must wrap your text inside double quotes "
|
||||
"(example: \"Hello world\")."),
|
||||
node.location);
|
||||
}
|
||||
else if (parentType == Type::Number) {
|
||||
RaiseTypeError(
|
||||
_("You must enter a number."), node.location);
|
||||
}
|
||||
else if (parentType == Type::NumberOrString) {
|
||||
RaiseTypeError(
|
||||
_("You must enter a number or a text, wrapped inside double quotes "
|
||||
"(example: \"Hello world\")."),
|
||||
node.location);
|
||||
}
|
||||
else if (parentType != Type::Object && parentType != Type::Variable) {
|
||||
// It can't happen.
|
||||
RaiseTypeError(
|
||||
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
|
||||
node.location);
|
||||
}
|
||||
childType = parentType;
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
ReportAnyError(node);
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
ReportAnyError(node);
|
||||
for (auto& parameter : node.parameters) {
|
||||
parameter->Visit(*this);
|
||||
}
|
||||
childType = ValidateFunction(node);
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
ReportAnyError(node);
|
||||
gd::String message;
|
||||
if (parentType == Type::Number) {
|
||||
message = _("You must enter a number or a valid expression call.");
|
||||
} else if (parentType == Type::String) {
|
||||
message = _(
|
||||
"You must enter a text (between quotes) or a valid expression call.");
|
||||
} else if (parentType == Type::Variable) {
|
||||
message = _("You must enter a variable name.");
|
||||
} else if (parentType == Type::Object) {
|
||||
message = _("You must enter a valid object name.");
|
||||
} else {
|
||||
// It can't happen.
|
||||
message = _("You must enter a valid expression.");
|
||||
}
|
||||
RaiseTypeError(message, node.location);
|
||||
childType = Type::Empty;
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override { ReportAnyError(node); }
|
||||
|
||||
private:
|
||||
void ReportAnyError(ExpressionNode& node) {
|
||||
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, Object, Empty};
|
||||
Type ValidateFunction(const gd::FunctionCallNode& function);
|
||||
|
||||
void ReportAnyError(const ExpressionNode& node) {
|
||||
if (node.diagnostic && node.diagnostic->IsError()) {
|
||||
// Syntax errors are holden by the AST nodes.
|
||||
// It's fine to give pointers on them as the AST live longer than errors
|
||||
// handling.
|
||||
errors.push_back(node.diagnostic.get());
|
||||
}
|
||||
}
|
||||
|
||||
void RaiseError(const gd::String &type,
|
||||
const gd::String &message, const ExpressionParserLocation &location) {
|
||||
auto diagnostic = gd::make_unique<ExpressionParserError>(
|
||||
type, message, location);
|
||||
errors.push_back(diagnostic.get());
|
||||
// Errors found by the validator are not holden by the AST nodes.
|
||||
// They must be owned by the validator to keep living while errors are
|
||||
// handled by the caller.
|
||||
supplementalErrors.push_back(std::move(diagnostic));
|
||||
}
|
||||
|
||||
void RaiseTypeError(
|
||||
const gd::String &message, const ExpressionParserLocation &location) {
|
||||
RaiseError("type_error", message, location);
|
||||
}
|
||||
|
||||
void RaiseOperatorError(
|
||||
const gd::String &message, const ExpressionParserLocation &location) {
|
||||
RaiseError("invalid_operator", message, location);
|
||||
}
|
||||
|
||||
void CheckType(Type expect, Type actual, const ExpressionParserLocation &location) {
|
||||
if (actual == Type::String) {
|
||||
if (expect == Type::Number) {
|
||||
RaiseTypeError(_("You entered a text, but a number was expected."),
|
||||
location);
|
||||
}
|
||||
else if (expect != Type::String && expect != Type::NumberOrString) {
|
||||
RaiseTypeError(
|
||||
_("You entered a text, but this type was expected:") + " " + TypeToString(expect),
|
||||
location);
|
||||
}
|
||||
}
|
||||
else if (actual == Type::Number) {
|
||||
if (expect == Type::String) {
|
||||
RaiseTypeError(
|
||||
_("You entered a number, but a text was expected (in quotes)."),
|
||||
location);
|
||||
}
|
||||
else if (expect != Type::Number && expect != Type::NumberOrString) {
|
||||
RaiseTypeError(
|
||||
_("You entered a number, but this type was expected:") + " " + TypeToString(expect),
|
||||
location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Type StringToType(const gd::String &type);
|
||||
static const gd::String &TypeToString(Type type);
|
||||
static const gd::String unknownTypeString;
|
||||
static const gd::String numberTypeString;
|
||||
static const gd::String stringTypeString;
|
||||
static const gd::String numberOrStringTypeString;
|
||||
static const gd::String variableTypeString;
|
||||
static const gd::String objectTypeString;
|
||||
static const gd::String identifierTypeString;
|
||||
static const gd::String emptyTypeString;
|
||||
|
||||
std::vector<ExpressionParserDiagnostic*> errors;
|
||||
std::vector<std::unique_ptr<ExpressionParserDiagnostic>> supplementalErrors;
|
||||
Type childType;
|
||||
Type parentType;
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
172
Core/GDCore/IDE/Events/ExpressionVariableOwnerFinder.h
Normal file
172
Core/GDCore/IDE/Events/ExpressionVariableOwnerFinder.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EXPRESSIONVARIABLEOWNERFINDER_H
|
||||
#define GDCORE_EXPRESSIONVARIABLEOWNERFINDER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#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/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Find the object name that should be used as a context of the
|
||||
* expression or sub-expression that a given node represents.
|
||||
*
|
||||
* \see gd::ExpressionParser2
|
||||
*/
|
||||
class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Helper function to find the object name that should be used as a
|
||||
* context of the expression or sub-expression that a given node represents.
|
||||
*/
|
||||
static const gd::String GetObjectName(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::String& rootObjectName,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionVariableOwnerFinder typeFinder(
|
||||
platform, globalObjectsContainer, objectsContainer, rootObjectName);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetObjectName();
|
||||
}
|
||||
|
||||
virtual ~ExpressionVariableOwnerFinder(){};
|
||||
|
||||
protected:
|
||||
ExpressionVariableOwnerFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::String& rootObjectName_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
rootObjectName(rootObjectName_),
|
||||
objectName(""),
|
||||
variableNode(nullptr) {};
|
||||
|
||||
/**
|
||||
* \brief Get all the errors
|
||||
*
|
||||
* No errors means that the expression is valid.
|
||||
*/
|
||||
const gd::String &GetObjectName() {
|
||||
return objectName;
|
||||
};
|
||||
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
if (variableNode != nullptr) {
|
||||
// This is not possible
|
||||
return;
|
||||
}
|
||||
if (node.parent == nullptr) {
|
||||
objectName = rootObjectName;
|
||||
return;
|
||||
}
|
||||
variableNode = &node;
|
||||
node.parent->Visit(*this);
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
if (variableNode != nullptr) {
|
||||
// This is not possible
|
||||
return;
|
||||
}
|
||||
if (node.parent == nullptr) {
|
||||
objectName = rootObjectName;
|
||||
return;
|
||||
}
|
||||
// This node is not necessarily a variable node.
|
||||
// It will be checked when visiting the FunctionCallNode.
|
||||
variableNode = &node;
|
||||
node.parent->Visit(*this);
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {
|
||||
if (variableNode == nullptr) {
|
||||
return;
|
||||
}
|
||||
int parameterIndex = -1;
|
||||
for (int i = 0; i < functionCall.parameters.size(); i++) {
|
||||
if (functionCall.parameters.at(i).get() == variableNode) {
|
||||
parameterIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parameterIndex < 0) {
|
||||
return;
|
||||
}
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
functionCall,
|
||||
parameterIndex);
|
||||
if (parameterMetadata == nullptr
|
||||
|| parameterMetadata->GetType() != "objectvar") {
|
||||
return;
|
||||
}
|
||||
|
||||
// The object on which the function is called is returned if no previous
|
||||
// parameters are objects.
|
||||
objectName = functionCall.objectName;
|
||||
for (int previousIndex = parameterIndex - 1; previousIndex >= 0; previousIndex--) {
|
||||
const gd::ParameterMetadata* previousParameterMetadata =
|
||||
MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
functionCall,
|
||||
previousIndex);
|
||||
if (previousParameterMetadata != nullptr
|
||||
&& gd::ParameterMetadata::IsObject(previousParameterMetadata->GetType())) {
|
||||
auto previousParameterNode = functionCall.parameters[previousIndex].get();
|
||||
IdentifierNode* objectNode = dynamic_cast<IdentifierNode*>(previousParameterNode);
|
||||
objectName = objectNode->identifierName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
gd::String objectName;
|
||||
gd::ExpressionNode *variableNode;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::String &rootObjectName;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EXPRESSIONVARIABLEOWNERFINDER_H
|
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
@@ -146,17 +145,9 @@ bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
|
||||
pNb < instruction.GetParametersCount();
|
||||
++pNb) {
|
||||
const gd::String& type = metadata.parameters[pNb].type;
|
||||
const gd::String& expression =
|
||||
instruction.GetParameter(pNb).GetPlainString();
|
||||
const gd::Expression& expression = instruction.GetParameter(pNb);
|
||||
|
||||
gd::ExpressionParser2 parser(
|
||||
platform, GetGlobalObjectsContainer(), GetObjectsContainer());
|
||||
|
||||
auto node = gd::ParameterMetadata::IsExpression("number", type)
|
||||
? parser.ParseExpression("number", expression)
|
||||
: (gd::ParameterMetadata::IsExpression("string", type)
|
||||
? parser.ParseExpression("string", expression)
|
||||
: std::unique_ptr<gd::ExpressionNode>());
|
||||
auto node = expression.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionParameterMover mover(GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(),
|
||||
|
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
@@ -156,18 +155,9 @@ bool ExpressionsRenamer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
for (std::size_t pNb = 0; pNb < metadata.parameters.size() &&
|
||||
pNb < instruction.GetParametersCount();
|
||||
++pNb) {
|
||||
const gd::String& type = metadata.parameters[pNb].type;
|
||||
const gd::String& expression =
|
||||
instruction.GetParameter(pNb).GetPlainString();
|
||||
const gd::Expression& expression = instruction.GetParameter(pNb);
|
||||
|
||||
gd::ExpressionParser2 parser(
|
||||
platform, GetGlobalObjectsContainer(), GetObjectsContainer());
|
||||
|
||||
auto node = gd::ParameterMetadata::IsExpression("number", type)
|
||||
? parser.ParseExpression("number", expression)
|
||||
: (gd::ParameterMetadata::IsExpression("string", type)
|
||||
? parser.ParseExpression("string", expression)
|
||||
: std::unique_ptr<gd::ExpressionNode>());
|
||||
auto node = expression.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionFunctionRenamer renamer(GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(),
|
||||
|
@@ -1,10 +1,10 @@
|
||||
#include "UsedExtensionsFinder.h"
|
||||
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/WholeProjectRefactorer.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/Project/BehaviorContent.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
@@ -54,13 +54,12 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
|
||||
metadata.GetMetadata().GetParameter(i).GetType();
|
||||
i++;
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("string", parameterType) ||
|
||||
gd::ParameterMetadata::IsExpression("number", parameterType)) {
|
||||
gd::ExpressionParser2 parser(project.GetCurrentPlatform(),
|
||||
GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer());
|
||||
parser.ParseExpression(parameterType, expression.GetPlainString())
|
||||
->Visit(*this);
|
||||
if (gd::ParameterMetadata::IsExpression("string", parameterType)) {
|
||||
rootType = "string";
|
||||
expression.GetRootNode()->Visit(*this);
|
||||
} else if (gd::ParameterMetadata::IsExpression("number", parameterType)) {
|
||||
rootType = "number";
|
||||
expression.GetRootNode()->Visit(*this);
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", parameterType))
|
||||
usedExtensions.insert("BuiltinVariables");
|
||||
}
|
||||
@@ -113,7 +112,8 @@ void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
|
||||
|
||||
// Add extensions bound to Objects/Behaviors/Functions
|
||||
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
if (gd::ParameterMetadata::IsObject(node.type)) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(project.GetCurrentPlatform(), GetGlobalObjectsContainer(), GetObjectsContainer(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
usedExtensions.insert(gd::MetadataProvider::GetExtensionAndObjectMetadata(
|
||||
project.GetCurrentPlatform(), node.identifierName)
|
||||
.GetExtension()
|
||||
|
@@ -31,6 +31,7 @@ class GD_CORE_API UsedExtensionsFinder
|
||||
private:
|
||||
UsedExtensionsFinder(gd::Project& project_) : project(project_){};
|
||||
gd::Project& project;
|
||||
gd::String rootType;
|
||||
std::set<gd::String> usedExtensions;
|
||||
|
||||
// Object Visitor
|
||||
|
@@ -18,7 +18,7 @@
|
||||
namespace gd {
|
||||
|
||||
void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
|
||||
gd::Project& project,
|
||||
const gd::Project& project,
|
||||
const gd::EventsFunction& eventsFunction,
|
||||
gd::ObjectsContainer& outputGlobalObjectsContainer,
|
||||
gd::ObjectsContainer& outputObjectsContainer) {
|
||||
@@ -36,7 +36,7 @@ void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
|
||||
}
|
||||
|
||||
void EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
|
||||
gd::Project& project,
|
||||
const gd::Project& project,
|
||||
const gd::EventsBasedBehavior& eventsBasedBehavior,
|
||||
const gd::EventsFunction& eventsFunction,
|
||||
gd::ObjectsContainer& outputGlobalObjectsContainer,
|
||||
|
@@ -32,7 +32,7 @@ class GD_CORE_API EventsFunctionTools {
|
||||
* generation for example.
|
||||
*/
|
||||
static void FreeEventsFunctionToObjectsContainer(
|
||||
gd::Project& project,
|
||||
const gd::Project& project,
|
||||
const gd::EventsFunction& eventsFunction,
|
||||
gd::ObjectsContainer& outputGlobalObjectsContainer,
|
||||
gd::ObjectsContainer& outputObjectsContainer);
|
||||
@@ -46,7 +46,7 @@ class GD_CORE_API EventsFunctionTools {
|
||||
* generation for example.
|
||||
*/
|
||||
static void BehaviorEventsFunctionToObjectsContainer(
|
||||
gd::Project& project,
|
||||
const gd::Project& project,
|
||||
const gd::EventsBasedBehavior& eventsBasedBehavior,
|
||||
const gd::EventsFunction& eventsFunction,
|
||||
gd::ObjectsContainer& outputGlobalObjectsContainer,
|
||||
|
@@ -126,9 +126,10 @@ class ResourceWorkerInEventsWorker : public ArbitraryEventsWorker {
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[this, &instruction](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::String& parameterValue,
|
||||
const gd::Expression& parameterExpression,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const String& parameterValue = parameterExpression.GetPlainString();
|
||||
if (parameterMetadata.GetType() ==
|
||||
"police") { // Should be renamed fontResource
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
|
@@ -444,7 +444,7 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
|
||||
type = project.GetObject(name).GetType();
|
||||
|
||||
// Search in groups
|
||||
if (searchInGroups) {
|
||||
else if (searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
|
||||
if (layout.GetObjectGroups()[i].GetName() == name) {
|
||||
// A group has the name searched
|
||||
@@ -505,18 +505,16 @@ gd::String GD_CORE_API GetTypeOfBehavior(const gd::ObjectsContainer& project,
|
||||
gd::String name,
|
||||
bool searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectsCount(); ++i) {
|
||||
vector<gd::String> behaviors = layout.GetObject(i).GetAllBehaviorNames();
|
||||
for (std::size_t j = 0; j < behaviors.size(); ++j) {
|
||||
if (layout.GetObject(i).GetBehavior(behaviors[j]).GetName() == name)
|
||||
return layout.GetObject(i).GetBehavior(behaviors[j]).GetTypeName();
|
||||
const auto &object = layout.GetObject(i);
|
||||
if (object.HasBehaviorNamed(name)) {
|
||||
return object.GetBehavior(name).GetTypeName();
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < project.GetObjectsCount(); ++i) {
|
||||
vector<gd::String> behaviors = project.GetObject(i).GetAllBehaviorNames();
|
||||
for (std::size_t j = 0; j < behaviors.size(); ++j) {
|
||||
if (project.GetObject(i).GetBehavior(behaviors[j]).GetName() == name)
|
||||
return project.GetObject(i).GetBehavior(behaviors[j]).GetTypeName();
|
||||
const auto &object = project.GetObject(i);
|
||||
if (object.HasBehaviorNamed(name)) {
|
||||
return object.GetBehavior(name).GetTypeName();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -86,7 +86,7 @@ std::map<gd::String, gd::PropertyDescriptor> Object::GetProperties() const {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
gd::BehaviorContent* Object::AddNewBehavior(gd::Project& project,
|
||||
gd::BehaviorContent* Object::AddNewBehavior(const gd::Project& project,
|
||||
const gd::String& type,
|
||||
const gd::String& name) {
|
||||
const gd::BehaviorMetadata& behaviorMetadata =
|
||||
|
@@ -242,7 +242,7 @@ class GD_CORE_API Object {
|
||||
* \return A pointer to the newly added behavior content. NULL if the creation
|
||||
* failed.
|
||||
*/
|
||||
gd::BehaviorContent* AddNewBehavior(gd::Project& project,
|
||||
gd::BehaviorContent* AddNewBehavior(const gd::Project& project,
|
||||
const gd::String& type,
|
||||
const gd::String& name);
|
||||
#endif
|
||||
|
@@ -76,7 +76,7 @@ std::size_t ObjectsContainer::GetObjectsCount() const {
|
||||
return initialObjects.size();
|
||||
}
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gd::Object& ObjectsContainer::InsertNewObject(gd::Project& project,
|
||||
gd::Object& ObjectsContainer::InsertNewObject(const gd::Project& project,
|
||||
const gd::String& objectType,
|
||||
const gd::String& name,
|
||||
std::size_t position) {
|
||||
|
@@ -94,7 +94,7 @@ class GD_CORE_API ObjectsContainer {
|
||||
* \note The object is created using the project's current platform.
|
||||
* \return A reference to the object in the list.
|
||||
*/
|
||||
gd::Object& InsertNewObject(gd::Project& project,
|
||||
gd::Object& InsertNewObject(const gd::Project& project,
|
||||
const gd::String& objectType,
|
||||
const gd::String& name,
|
||||
std::size_t position);
|
||||
|
@@ -60,7 +60,7 @@ class GD_CORE_API Resource {
|
||||
* \see gd::Resource::GetFile
|
||||
* \see gd::Resource::SetFile
|
||||
*/
|
||||
virtual bool UseFile() { return false; }
|
||||
virtual bool UseFile() const { return false; }
|
||||
|
||||
/**
|
||||
* \brief Return, if applicable, the String containing the file used by the
|
||||
@@ -184,7 +184,7 @@ class GD_CORE_API ImageResource : public Resource {
|
||||
*/
|
||||
virtual void SetFile(const gd::String& newFile) override;
|
||||
|
||||
virtual bool UseFile() override { return true; }
|
||||
virtual bool UseFile() const override { return true; }
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
|
||||
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
|
||||
@@ -234,7 +234,7 @@ class GD_CORE_API AudioResource : public Resource {
|
||||
virtual const gd::String& GetFile() const override { return file; };
|
||||
virtual void SetFile(const gd::String& newFile) override;
|
||||
|
||||
virtual bool UseFile() override { return true; }
|
||||
virtual bool UseFile() const override { return true; }
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
|
||||
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
|
||||
@@ -286,7 +286,7 @@ class GD_CORE_API FontResource : public Resource {
|
||||
virtual const gd::String& GetFile() const override { return file; };
|
||||
virtual void SetFile(const gd::String& newFile) override;
|
||||
|
||||
virtual bool UseFile() override { return true; }
|
||||
virtual bool UseFile() const override { return true; }
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
|
||||
void UnserializeFrom(const SerializerElement& element) override;
|
||||
@@ -312,7 +312,7 @@ class GD_CORE_API VideoResource : public Resource {
|
||||
virtual const gd::String& GetFile() const override { return file; };
|
||||
virtual void SetFile(const gd::String& newFile) override;
|
||||
|
||||
virtual bool UseFile() override { return true; }
|
||||
virtual bool UseFile() const override { return true; }
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
|
||||
void UnserializeFrom(const SerializerElement& element) override;
|
||||
@@ -338,7 +338,7 @@ class GD_CORE_API JsonResource : public Resource {
|
||||
virtual const gd::String& GetFile() const override { return file; };
|
||||
virtual void SetFile(const gd::String& newFile) override;
|
||||
|
||||
virtual bool UseFile() override { return true; }
|
||||
virtual bool UseFile() const override { return true; }
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
|
||||
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
|
||||
@@ -379,7 +379,7 @@ class GD_CORE_API BitmapFontResource : public Resource {
|
||||
virtual const gd::String& GetFile() const override { return file; };
|
||||
virtual void SetFile(const gd::String& newFile) override;
|
||||
|
||||
virtual bool UseFile() override { return true; }
|
||||
virtual bool UseFile() const override { return true; }
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
|
||||
void UnserializeFrom(const SerializerElement& element) override;
|
||||
|
2
Core/GDCore/TinyXml/tinyxmlparser.cpp
vendored
2
Core/GDCore/TinyXml/tinyxmlparser.cpp
vendored
@@ -354,7 +354,7 @@ const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
|
||||
}
|
||||
else
|
||||
{
|
||||
while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' )
|
||||
while ( (*p && IsWhiteSpace( *p )) || *p == '\n' || *p =='\r' )
|
||||
++p;
|
||||
}
|
||||
|
||||
|
@@ -197,6 +197,17 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
|
||||
.AddParameter("object", _("Object 2 parameter"))
|
||||
.AddParameter("objectvar", _("Variable for object 2"))
|
||||
.SetFunctionName("getStringWith2ObjectParamAnd2ObjectVarParam");
|
||||
extension
|
||||
->AddStrExpression(
|
||||
"GetStringWith1ObjectParamAnd2ObjectVarParam",
|
||||
"Get string with 2 objectvar param one from the same object param",
|
||||
"",
|
||||
"",
|
||||
"")
|
||||
.AddParameter("object", _("Object 1 parameter"))
|
||||
.AddParameter("objectvar", _("Variable for object 1"))
|
||||
.AddParameter("objectvar", _("Variable for object 2"))
|
||||
.SetFunctionName("getStringWith1ObjectParamAnd2ObjectVarParam");
|
||||
|
||||
auto& object = extension->AddObject<gd::Object>(
|
||||
"Sprite", "Dummy Sprite", "Dummy sprite object", "");
|
||||
|
@@ -32,7 +32,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
group.AddObject("MyOtherSpriteObject");
|
||||
group.AddObject("MyFakeObjectWithUnsupportedCapability");
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
unsigned int maxDepth = 0;
|
||||
gd::EventsCodeGenerationContext context(&maxDepth);
|
||||
@@ -40,8 +40,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
SECTION("Valid text generation") {
|
||||
{
|
||||
auto node = parser.ParseExpression("string", "\"hello world\"");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
auto node = parser.ParseExpression("\"hello world\"");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -49,8 +51,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello world\"");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("string", "\"hello\" + \"world\" ");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
auto node = parser.ParseExpression("\"hello\" + \"world\" ");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -59,8 +63,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression(
|
||||
"string", "\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\"");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
"\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\"");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -72,8 +78,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
SECTION("Valid number generation") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "12.45");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
auto node = parser.ParseExpression("12.45");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -85,8 +93,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
SECTION("Invalid operators generation") {
|
||||
// TODO: Should any error return directly 0 or ""?
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "12.45 +");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
auto node = parser.ParseExpression("12.45 +");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -94,8 +104,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "12.45 + 0");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "12.45 * *");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
auto node = parser.ParseExpression("12.45 * *");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -106,16 +118,20 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
SECTION("Valid unary operator generation") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "-12.45");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
auto node = parser.ParseExpression("-12.45");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "-(12.45)");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "12.5 + -2. / (.3)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
auto node = parser.ParseExpression("12.5 + -2. / (.3)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
@@ -124,20 +140,24 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
}
|
||||
|
||||
SECTION("Valid function calls") {
|
||||
{
|
||||
SECTION("without parameter") {
|
||||
auto node =
|
||||
parser.ParseExpression("number", " 1 / MyExtension::GetNumber()");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
parser.ParseExpression(" 1 / MyExtension::GetNumber()");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "1 / getNumber()");
|
||||
}
|
||||
{
|
||||
SECTION("number and string parameters") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "MyExtension::GetNumberWith2Params(12, \"hello world\")");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -145,12 +165,13 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getNumberWith2Params(12, \"hello world\")");
|
||||
}
|
||||
{
|
||||
SECTION("nested function call") {
|
||||
auto node =
|
||||
parser.ParseExpression("number",
|
||||
"MyExtension::GetNumberWith2Params("
|
||||
parser.ParseExpression("MyExtension::GetNumberWith2Params("
|
||||
"MyExtension::GetNumber(), \"hello world\")");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -158,10 +179,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getNumberWith2Params(getNumber(), \"hello world\")");
|
||||
}
|
||||
{
|
||||
SECTION("object function") {
|
||||
auto node =
|
||||
parser.ParseExpression("number", "MySpriteObject.GetObjectNumber()");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
parser.ParseExpression("MySpriteObject.GetObjectNumber()");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -169,11 +192,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"MySpriteObject.getObjectNumber() ?? 0");
|
||||
}
|
||||
{
|
||||
SECTION("object function with nested free function") {
|
||||
auto node = parser.ParseExpression(
|
||||
"string",
|
||||
"MySpriteObject.GetObjectStringWith1Param(MyExtension::GetNumber())");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -183,22 +207,11 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
}
|
||||
}
|
||||
SECTION("Valid function calls with optional arguments") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("number", "MyExtension::MouseX(\"layer1\",)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getMouseX(\"\", \"layer1\", 0)");
|
||||
// (first argument is the currentScene)
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number",
|
||||
"MyExtension::MouseX(\"layer1\",2+2)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
SECTION("with optional parameter set") {
|
||||
auto node = parser.ParseExpression("MyExtension::MouseX(\"layer1\",2+2)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -207,13 +220,39 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"getMouseX(\"\", \"layer1\", 2 + 2)");
|
||||
// (first argument is the currentScene)
|
||||
}
|
||||
}
|
||||
SECTION(
|
||||
"Valid function calls (deprecated way of specifying optional "
|
||||
"arguments)") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "MyExtension::MouseX(,)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
SECTION("with last optional parameter omit") {
|
||||
auto node =
|
||||
parser.ParseExpression("MyExtension::MouseX(\"layer1\")");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getMouseX(\"\", \"layer1\", 0)");
|
||||
// (first argument is the currentScene)
|
||||
}
|
||||
SECTION("with last optional parameter omit (deprecated way)") {
|
||||
auto node =
|
||||
parser.ParseExpression("MyExtension::MouseX(\"layer1\",)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getMouseX(\"\", \"layer1\", 0)");
|
||||
// (first argument is the currentScene)
|
||||
}
|
||||
SECTION("with explicit comma (deprecated way)") {
|
||||
auto node = parser.ParseExpression("MyExtension::MouseX(,)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -223,15 +262,17 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
// (first argument is the currentScene)
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid function calls") {
|
||||
{
|
||||
SECTION("unknown identifier in parameters") {
|
||||
auto node =
|
||||
parser.ParseExpression("string",
|
||||
"MySpriteObject.GetObjectStringWith3Param("
|
||||
parser.ParseExpression("MySpriteObject.GetObjectStringWith3Param("
|
||||
"MySpriteObject.GetObjectNumber() / 2.3, "
|
||||
"MySpriteObject.GetObjectStringWith1Param("
|
||||
"MyExtension::GetNumber()), test)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -243,33 +284,38 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"/* Error during generation, unrecognized identifier type: "
|
||||
"unknown with value test */ \"test\") ?? \"\"");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression(
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
context);
|
||||
SECTION("missing parameter") {
|
||||
{
|
||||
auto node = parser.ParseExpression(
|
||||
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getNumberWith2Params(getNumber(), /* Error during generation, "
|
||||
"parameter not existing in the nodes */ \"\")");
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getNumberWith2Params(getNumber(), /* Error during generation, "
|
||||
"parameter not existing in the nodes */ \"\")");
|
||||
}
|
||||
{
|
||||
// Using GenerateExpressionCode, the default value of 0 should be returned
|
||||
// as expression is invalid.
|
||||
REQUIRE(
|
||||
gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator,
|
||||
context,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())") ==
|
||||
"0");
|
||||
}
|
||||
}
|
||||
{
|
||||
// Using GenerateExpressionCode, the default value of 0 should be returned
|
||||
// as expression is invalid.
|
||||
REQUIRE(
|
||||
gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator,
|
||||
context,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())") ==
|
||||
"0");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "MyExtension::Idontexist()");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
SECTION("unknown function") {
|
||||
auto node = parser.ParseExpression("MyExtension::Idontexist()");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -278,11 +324,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"/* Error during generation, function not found: "
|
||||
"MyExtension::Idontexist */ 0");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number",
|
||||
"MyExtension::GetNumberWith2Params(1, "
|
||||
SECTION("too much parameters") {
|
||||
auto node = parser.ParseExpression("MyExtension::GetNumberWith2Params(1, "
|
||||
"\"2\", MyExtension::GetNumber())");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -291,13 +338,14 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"getNumberWith2Params(1, \"2\")");
|
||||
}
|
||||
}
|
||||
SECTION("Invalid function calls (capabilities)") {
|
||||
{
|
||||
SECTION("function calls (capabilities)") {
|
||||
SECTION("supported capability") {
|
||||
// Capability is supported, so the expression is valid.
|
||||
auto node = parser.ParseExpression(
|
||||
"string",
|
||||
"MySpriteObject.GetSomethingRequiringEffectCapability(123)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -306,26 +354,29 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
expressionCodeGenerator.GetOutput() ==
|
||||
"MySpriteObject.getSomethingRequiringEffectCapability(123) ?? \"\"");
|
||||
}
|
||||
{
|
||||
SECTION("unsupported capability") {
|
||||
// Capability is not supported, so the expression is not even valid.
|
||||
auto node =
|
||||
parser.ParseExpression("string",
|
||||
"MyFakeObjectWithUnsupportedCapability."
|
||||
parser.ParseExpression("MyFakeObjectWithUnsupportedCapability."
|
||||
"GetSomethingRequiringEffectCapability(123)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "\"\"");
|
||||
}
|
||||
{
|
||||
SECTION("group with partial support") {
|
||||
// We use a group, capability is supported only by one object of the
|
||||
// group. The expression itself is valid, but code generation should skip
|
||||
// the objects with unsupported capability.
|
||||
auto node = parser.ParseExpression(
|
||||
"string", "AllObjects.GetSomethingRequiringEffectCapability(123)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
"AllObjects.GetSomethingRequiringEffectCapability(123)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -336,51 +387,87 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"MySpriteObject.getSomethingRequiringEffectCapability(123) ?? \"\"");
|
||||
}
|
||||
}
|
||||
SECTION("Function name") {
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.GetObjectNumber");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "0");
|
||||
}
|
||||
SECTION("Invalid variables") {
|
||||
{
|
||||
// Test an empty expression
|
||||
SECTION("empty variable") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "") == "fakeBadVariable");
|
||||
}
|
||||
{
|
||||
// Test a unary operator
|
||||
SECTION("only an unary operator") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "-") ==
|
||||
"fakeBadVariable");
|
||||
}
|
||||
{
|
||||
// Test an operator
|
||||
SECTION("only a binary operator") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "globalvar", "/") ==
|
||||
"fakeBadVariable");
|
||||
}
|
||||
}
|
||||
SECTION("Invalid variables, using operators") {
|
||||
{
|
||||
// Test a unary operator
|
||||
SECTION("unary operation") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "-(var1)") ==
|
||||
"fakeBadVariable");
|
||||
}
|
||||
{
|
||||
// Test an operator
|
||||
SECTION("binary operation") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "globalvar", "var1+var2") ==
|
||||
"fakeBadVariable");
|
||||
}
|
||||
{
|
||||
// Test multiple operators
|
||||
SECTION("multiple operation") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "globalvar", "var1/var2/var3/var4") ==
|
||||
"fakeBadVariable");
|
||||
}
|
||||
}
|
||||
SECTION("Valid variables") {
|
||||
SECTION("simple variable") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable", "")
|
||||
== "getLayoutVariable(myVariable)");
|
||||
}
|
||||
SECTION("child dot accessor") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable.myChild", "")
|
||||
== "getLayoutVariable(myVariable).getChild(\"myChild\")");
|
||||
}
|
||||
SECTION("2 children") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable.child1.child2", "")
|
||||
== "getLayoutVariable(myVariable).getChild(\"child1\").getChild(\"child2\")");
|
||||
}
|
||||
SECTION("bracket access") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[ \"hello\" + "
|
||||
"\"world\" ]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(\"hello\" + \"world\")");
|
||||
}
|
||||
SECTION("object variable") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "myVariable", "MySpriteObject")
|
||||
== "getVariableForObject(MySpriteObject, myVariable)");
|
||||
}
|
||||
}
|
||||
SECTION("Valid function calls with variables") {
|
||||
SECTION("Simple access") {
|
||||
{
|
||||
SECTION("Scene variable") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "MyExtension::GetVariableAsNumber(myVariable)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
"MyExtension::GetVariableAsNumber(myVariable)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -388,11 +475,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"returnVariable(getLayoutVariable(myVariable))");
|
||||
}
|
||||
{
|
||||
SECTION("Global variable") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number",
|
||||
"MyExtension::GetGlobalVariableAsNumber(myGlobalVariable)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -400,13 +488,76 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"returnVariable(getProjectVariable(myGlobalVariable))");
|
||||
}
|
||||
SECTION("Variables on different objects") {
|
||||
auto node = parser.ParseExpression(
|
||||
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam("
|
||||
"MySpriteObject, myVariable, MyOtherSpriteObject, myOtherVariable)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getStringWith2ObjectParamAnd2ObjectVarParam(fakeObjectListOf_MySpriteObject, "
|
||||
"getVariableForObject(MySpriteObject, myVariable), "
|
||||
"fakeObjectListOf_MyOtherSpriteObject, "
|
||||
"getVariableForObject(MyOtherSpriteObject, myOtherVariable))");
|
||||
}
|
||||
SECTION("Variables on the same object") {
|
||||
auto node = parser.ParseExpression(
|
||||
"MyExtension::GetStringWith1ObjectParamAnd2ObjectVarParam("
|
||||
"MySpriteObject, myVariable, myOtherVariable)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"getStringWith1ObjectParamAnd2ObjectVarParam(fakeObjectListOf_MySpriteObject, "
|
||||
"getVariableForObject(MySpriteObject, myVariable), "
|
||||
"getVariableForObject(MySpriteObject, myOtherVariable))");
|
||||
}
|
||||
SECTION("Object variable with object function call") {
|
||||
auto node = parser.ParseExpression(
|
||||
"MySpriteObject.GetObjectVariableAsNumber(myVariable)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(
|
||||
expressionCodeGenerator.GetOutput() ==
|
||||
"MySpriteObject.returnVariable(getVariableForObject("
|
||||
"MySpriteObject, myVariable)) ?? 0");
|
||||
}
|
||||
}
|
||||
SECTION("Child access") {
|
||||
{
|
||||
SECTION("1 child") {
|
||||
auto node = parser.ParseExpression(
|
||||
"MyExtension::GetVariableAsNumber(myVariable.child1)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() ==
|
||||
"returnVariable(getLayoutVariable(myVariable).getChild("
|
||||
"\"child1\"))");
|
||||
}
|
||||
SECTION("2 children") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(myVariable.child1.child2)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -415,12 +566,13 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"returnVariable(getLayoutVariable(myVariable).getChild("
|
||||
"\"child1\").getChild(\"child2\"))");
|
||||
}
|
||||
{
|
||||
SECTION("bracket access") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(myVariable[ \"hello\" + "
|
||||
"\"world\" ].child2)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -429,13 +581,14 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"returnVariable(getLayoutVariable(myVariable).getChild("
|
||||
"\"hello\" + \"world\").getChild(\"child2\"))");
|
||||
}
|
||||
{
|
||||
SECTION("bracket access with nested variable") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(myVariable[ \"hello\" + "
|
||||
"MySpriteObject.GetObjectStringWith1Param(MyOtherSpriteObject."
|
||||
"GetObjectVariableAsNumber(mySecondVariable)) ].child2)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
@@ -462,8 +615,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
}
|
||||
SECTION("Mixed test (1)") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "-+-MyExtension::MouseX(,)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
|
||||
auto node = parser.ParseExpression("-+-MyExtension::MouseX(,)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
|
@@ -19,16 +19,17 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto& layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MyObject", 0);
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
auto getCompletionsFor = [&](const gd::String& type,
|
||||
const gd::String& expression,
|
||||
size_t location) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
return gd::ExpressionCompletionFinder::GetCompletionDescriptionsFor(
|
||||
*node, location);
|
||||
platform, project, layout1, type, *node, location);
|
||||
};
|
||||
|
||||
const std::vector<gd::ExpressionCompletionDescription>
|
||||
@@ -66,6 +67,24 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
REQUIRE(getCompletionsFor("number|string", "My", 2) ==
|
||||
expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object or expression completions in a variable name") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string", "My", 0, 2),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "My", 0, 2)};
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 52) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 53) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 54) == expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object or expression completions in a variable index") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("number", "My", 0, 2),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"number", "My", 0, 2)};
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 52) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 53) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 54) == expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object when type is an object") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("object", "My", 0, 2)};
|
||||
@@ -143,6 +162,17 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
"MyExtension::GetVariableAsNumber(myVar",
|
||||
33) == expectedCompletions);
|
||||
}
|
||||
SECTION("Object function with a Variable as argument") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForVariable(
|
||||
"objectvar", "myVar", 35, 40, "MyObject")};
|
||||
getCompletionsFor("number",
|
||||
"MyObject.GetObjectVariableAsNumber(myVar",
|
||||
35);
|
||||
REQUIRE(getCompletionsFor("number",
|
||||
"MyObject.GetObjectVariableAsNumber(myVar",
|
||||
35) == expectedCompletions);
|
||||
}
|
||||
SECTION("Function with a Layer as argument") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForText(
|
||||
|
@@ -14,10 +14,9 @@
|
||||
|
||||
template <class TNode>
|
||||
bool CheckNodeAtLocationIs(gd::ExpressionParser2& parser,
|
||||
const gd::String& type,
|
||||
const gd::String& expression,
|
||||
size_t searchPosition) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
return dynamic_cast<TNode*>(
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(
|
||||
@@ -26,10 +25,9 @@ bool CheckNodeAtLocationIs(gd::ExpressionParser2& parser,
|
||||
|
||||
template <class TNode>
|
||||
bool CheckParentNodeAtLocationIs(gd::ExpressionParser2& parser,
|
||||
const gd::String& type,
|
||||
const gd::String& expression,
|
||||
size_t searchPosition) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
return dynamic_cast<TNode*>(
|
||||
gd::ExpressionNodeLocationFinder::GetParentNodeAtPosition(
|
||||
@@ -37,20 +35,18 @@ bool CheckParentNodeAtLocationIs(gd::ExpressionParser2& parser,
|
||||
}
|
||||
|
||||
bool CheckNoNodeAtLocation(gd::ExpressionParser2& parser,
|
||||
const gd::String& type,
|
||||
const gd::String& expression,
|
||||
size_t searchPosition) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
return gd::ExpressionNodeLocationFinder::GetNodeAtPosition(
|
||||
*node, searchPosition) == nullptr;
|
||||
}
|
||||
|
||||
bool CheckNoParentNodeAtLocation(gd::ExpressionParser2& parser,
|
||||
const gd::String& type,
|
||||
const gd::String& expression,
|
||||
size_t searchPosition) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
return gd::ExpressionNodeLocationFinder::GetParentNodeAtPosition(
|
||||
*node, searchPosition) == nullptr;
|
||||
@@ -63,21 +59,21 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
|
||||
auto& layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
SECTION("Empty expressions") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
|
||||
parser, "string", "", 0) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", "", 1) ==
|
||||
parser, "", 0) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "", 1) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", " ", 0) ==
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, " ", 0) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
|
||||
parser, "string", " ", 1) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", " ", 2) ==
|
||||
parser, " ", 1) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, " ", 2) ==
|
||||
true);
|
||||
}
|
||||
}
|
||||
@@ -85,270 +81,255 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
|
||||
SECTION("Valid text") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello world\"", 0) == true);
|
||||
parser, "\"Hello world\"", 0) == true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello world\"", 1) == true);
|
||||
parser, "\"Hello world\"", 1) == true);
|
||||
}
|
||||
SECTION("Test 3") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello world\"", 12) == true);
|
||||
parser, "\"Hello world\"", 12) == true);
|
||||
}
|
||||
SECTION("Test 4") {
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"Hello world\"", 13) ==
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "\"Hello world\"", 13) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Test 5") {
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"Hello world\"", 99) ==
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "\"Hello world\"", 99) ==
|
||||
true);
|
||||
}
|
||||
}
|
||||
SECTION("Valid text operators") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" + \"World\"", 1) == true);
|
||||
parser, "\"Hello \" + \"World\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "string", "\"Hello \" + \"World\"", 8) == true);
|
||||
parser, "\"Hello \" + \"World\"", 8) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" + \"World\"", 15) == true);
|
||||
parser, "\"Hello \" + \"World\"", 15) == true);
|
||||
}
|
||||
}
|
||||
SECTION("Invalid texts") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "string", "\"", 0) ==
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "\"", 0) ==
|
||||
true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "string", "\"a", 1) ==
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "\"a", 1) ==
|
||||
true);
|
||||
}
|
||||
|
||||
SECTION("Invalid parenthesis") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::SubExpressionNode>(
|
||||
parser, "string", "((\"hello\"", 1) == true);
|
||||
parser, "((\"hello\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "((\"hello\"", 2) == true);
|
||||
parser, "((\"hello\"", 2) == true);
|
||||
}
|
||||
|
||||
SECTION("Invalid text operators") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" - \"World\"", 0) == true);
|
||||
parser, "\"Hello \" - \"World\"", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" - \"World\"", 1) == true);
|
||||
parser, "\"Hello \" - \"World\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "string", "\"Hello \" / \"World\"", 8) == true);
|
||||
parser, "\"Hello \" / \"World\"", 8) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" * \"World\"", 15) == true);
|
||||
parser, "\"Hello \" * \"World\"", 15) == true);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid unary operators") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "-123", 0) == true);
|
||||
parser, "-123", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "+123", 0) == true);
|
||||
parser, "+123", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "-123", 1) == true);
|
||||
parser, "-123", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "-123", 2) == true);
|
||||
parser, "-123", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "-123", 3) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "number", "-123", 4) == true);
|
||||
parser, "-123", 3) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "-123", 4) == true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "-+-123", 0) == true);
|
||||
parser, "-+-123", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "-+-123", 1) == true);
|
||||
parser, "-+-123", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "-+-123", 2) == true);
|
||||
parser, "-+-123", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "-+-123", 3) == true);
|
||||
parser, "-+-123", 3) == true);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid number operators") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 ! 34", 0) == true);
|
||||
parser, "12 ! 34", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 ! 34", 1) == true);
|
||||
parser, "12 ! 34", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 ! 34", 2) == true);
|
||||
parser, "12 ! 34", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 ! 34", 3) == true);
|
||||
parser, "12 ! 34", 3) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 ! 34", 4) == true);
|
||||
parser, "12 ! 34", 4) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 ! 34", 5) == true);
|
||||
parser, "12 ! 34", 5) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 ! 34", 6) == true);
|
||||
parser, "12 ! 34", 6) == true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "1 / /2", 0) == true);
|
||||
parser, "1 / /2", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "1 / /2", 1) == true);
|
||||
parser, "1 / /2", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "1 / /2", 2) == true);
|
||||
parser, "1 / /2", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "1 / /2", 3) == true);
|
||||
parser, "1 / /2", 3) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
|
||||
parser, "number", "1 / /2", 4) == true);
|
||||
parser, "1 / /2", 4) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
|
||||
parser, "number", "1 / /2", 5) == true);
|
||||
parser, "1 / /2", 5) == true);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Numbers and texts mismatchs") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(parser, "number", "12+\"hello\"", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(parser, "number", "12+\"hello\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(parser, "number", "12+\"hello\"", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "number", "12+\"hello\"", 3) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(parser, "12+\"hello\"", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(parser, "12+\"hello\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(parser, "12+\"hello\"", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "12+\"hello\"", 3) == true);
|
||||
}
|
||||
|
||||
SECTION("Numbers and texts mismatchs (parent node)") {
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "number", "12+\"hello\"", 0) == true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "number", "12+\"hello\"", 1) == true);
|
||||
REQUIRE(CheckNoParentNodeAtLocation(parser, "number", "12+\"hello\"", 2) == true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "number", "12+\"hello\"", 3) == true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "12+\"hello\"", 0) == true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "12+\"hello\"", 1) == true);
|
||||
REQUIRE(CheckNoParentNodeAtLocation(parser, "12+\"hello\"", 2) == true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "12+\"hello\"", 3) == true);
|
||||
}
|
||||
|
||||
SECTION("Valid objects") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "HelloWorld1", 0) == true);
|
||||
parser, "HelloWorld1", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "HelloWorld1", 1) == true);
|
||||
parser, "HelloWorld1", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "HelloWorld1", 10) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "object", "HelloWorld1", 11) == true);
|
||||
parser, "HelloWorld1", 10) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "HelloWorld1", 11) == true);
|
||||
}
|
||||
SECTION("Valid objects (parent node)") {
|
||||
REQUIRE(CheckNoParentNodeAtLocation(
|
||||
parser, "object", "HelloWorld1", 0) == true);
|
||||
parser, "HelloWorld1", 0) == true);
|
||||
}
|
||||
SECTION("Invalid objects") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "a+b", 0) == true);
|
||||
parser, "a+b", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "object", "a+b", 1) == true);
|
||||
parser, "a+b", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "a+b", 2) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "object", "a+b", 3) == true);
|
||||
parser, "a+b", 2) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "a+b", 3) == true);
|
||||
}
|
||||
SECTION("Valid function calls") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 0) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 0) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 1) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 1) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 2) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 2) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 3) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 3) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 4) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 4) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 5) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 5) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 27) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 27) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 28) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 28) ==
|
||||
true);
|
||||
REQUIRE(CheckNoNodeAtLocation(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 29) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 29) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
33) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
34) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
35) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
36) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
37) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
38) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
39) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
50) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
51) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
52) == true);
|
||||
}
|
||||
SECTION("Parent node") {
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 0) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 0) ==
|
||||
true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 6) ==
|
||||
parser, "12 + MyExtension::GetNumber()", 6) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
35) == true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
35) == true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
39) == true);
|
||||
}
|
||||
@@ -356,66 +337,67 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
|
||||
|
||||
SECTION("Invalid function calls") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 0) == true);
|
||||
parser, "Idontexist(12)", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 1) == true);
|
||||
parser, "Idontexist(12)", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 2) == true);
|
||||
parser, "Idontexist(12)", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 10) == true);
|
||||
parser, "Idontexist(12)", 10) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "Idontexist(12)", 11) == true);
|
||||
parser, "Idontexist(12)", 11) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "Idontexist(12)", 12) == true);
|
||||
parser, "Idontexist(12)", 12) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 13) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "number", "Idontexist(12)", 14) ==
|
||||
parser, "Idontexist(12)", 13) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "Idontexist(12)", 14) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Invalid function calls (parent node)") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "Idontexist(12)", 12) == true);
|
||||
parser, "Idontexist(12)", 12) == true);
|
||||
REQUIRE(CheckParentNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 12) == true);
|
||||
parser, "Idontexist(12)", 12) == true);
|
||||
}
|
||||
|
||||
SECTION("Unterminated function calls") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(", 0) == true);
|
||||
parser, "Idontexist(", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(", 1) == true);
|
||||
parser, "Idontexist(", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(", 10) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "number", "Idontexist(", 11) == true);
|
||||
parser, "Idontexist(", 10) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "Idontexist(", 11) == true);
|
||||
}
|
||||
|
||||
SECTION("Valid variables") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::VariableNode>(
|
||||
parser, "scenevar", "myVariable", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::VariableNode>(
|
||||
parser, "scenevar", "myVariable", 9) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "scenevar", "myVariable", 10) ==
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "myVariable", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "myVariable", 9) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "myVariable", 10) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
auto node = parser.ParseExpression("scenevar", "Var1.Child1");
|
||||
auto node = parser.ParseExpression("Var1.Child1");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
auto var1Node =
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 0);
|
||||
REQUIRE(dynamic_cast<gd::VariableNode*>(var1Node) != nullptr);
|
||||
REQUIRE(dynamic_cast<gd::VariableNode&>(*var1Node).name == "Var1");
|
||||
REQUIRE(dynamic_cast<gd::IdentifierNode*>(var1Node) != nullptr);
|
||||
REQUIRE(dynamic_cast<gd::IdentifierNode&>(*var1Node).identifierName == "Var1");
|
||||
|
||||
// It's actually the same node.
|
||||
auto child1Node =
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 4);
|
||||
REQUIRE(dynamic_cast<gd::VariableAccessorNode*>(child1Node) != nullptr);
|
||||
REQUIRE(dynamic_cast<gd::VariableAccessorNode&>(*child1Node).name ==
|
||||
REQUIRE(dynamic_cast<gd::IdentifierNode*>(child1Node) != nullptr);
|
||||
REQUIRE(dynamic_cast<gd::IdentifierNode&>(*child1Node).childIdentifierName ==
|
||||
"Child1");
|
||||
}
|
||||
SECTION("Test 3") {
|
||||
auto node = parser.ParseExpression(
|
||||
"scenevar", "myVariable[ \"My named children\" ].grandChild");
|
||||
"myVariable[ \"My named children\" ].grandChild");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
auto myVariableNode =
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -21,13 +21,15 @@ TEST_CASE("ExpressionParser2 - Benchmarks", "[common][events]") {
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
auto parseExpression = [&parser](const gd::String &expression) {
|
||||
auto parseExpressionWithType = [&parser,
|
||||
auto parseExpression = [&parser, &project, &platform, &layout1](const gd::String &expression) {
|
||||
auto parseExpressionWithType = [&parser, &project, &platform, &layout1,
|
||||
&expression](const gd::String &type) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
gd::ExpressionValidator validator(platform, project, layout1, type);
|
||||
node->Visit(validator);
|
||||
};
|
||||
|
||||
parseExpressionWithType("number");
|
||||
|
@@ -21,7 +21,7 @@ TEST_CASE("ExpressionParser2 - Naughty strings", "[common][events]") {
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
SECTION("Check that no naughty string crash the parser") {
|
||||
std::string inputFile = std::string(__FILE__) + "-blns.txt";
|
||||
@@ -31,9 +31,9 @@ TEST_CASE("ExpressionParser2 - Naughty strings", "[common][events]") {
|
||||
std::string line;
|
||||
size_t count = 0;
|
||||
while ( std::getline (myfile,line) ) {
|
||||
auto node1 = parser.ParseExpression("string", line.c_str());
|
||||
auto node1 = parser.ParseExpression(line.c_str());
|
||||
REQUIRE(node1 != nullptr);
|
||||
auto node2 = parser.ParseExpression("number", line.c_str());
|
||||
auto node2 = parser.ParseExpression(line.c_str());
|
||||
REQUIRE(node2 != nullptr);
|
||||
|
||||
count++;
|
||||
|
@@ -20,12 +20,12 @@ TEST_CASE("ExpressionParser2NodePrinter", "[common][events]") {
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
auto testPrinter = [&parser](const gd::String &type,
|
||||
const gd::String &expression,
|
||||
const gd::String &expectedOutput = "") {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
gd::ExpressionParser2NodePrinter printer;
|
||||
node->Visit(printer);
|
||||
|
@@ -33,7 +33,6 @@ using namespace std;
|
||||
namespace gdjs {
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateEventsListCompleteFunctionCode(
|
||||
gd::Project& project,
|
||||
gdjs::EventsCodeGenerator& codeGenerator,
|
||||
gd::String fullyQualifiedFunctionName,
|
||||
gd::String functionArgumentsCode,
|
||||
@@ -89,7 +88,7 @@ gd::String EventsCodeGenerator::GenerateEventsListCompleteFunctionCode(
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateLayoutCode(
|
||||
gd::Project& project,
|
||||
const gd::Project& project,
|
||||
const gd::Layout& scene,
|
||||
const gd::String& codeNamespace,
|
||||
std::set<gd::String>& includeFiles,
|
||||
@@ -99,7 +98,6 @@ gd::String EventsCodeGenerator::GenerateLayoutCode(
|
||||
codeGenerator.SetGenerateCodeForRuntime(compilationForRuntime);
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
project,
|
||||
codeGenerator,
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "func",
|
||||
"runtimeScene",
|
||||
@@ -128,7 +126,6 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
|
||||
codeGenerator.SetGenerateCodeForRuntime(compilationForRuntime);
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
project,
|
||||
codeGenerator,
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "func",
|
||||
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
@@ -191,7 +188,6 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
|
||||
"Behavior");
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
project,
|
||||
codeGenerator,
|
||||
fullyQualifiedFunctionName,
|
||||
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
@@ -981,7 +977,7 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
const gd::String& parameter,
|
||||
const gd::Expression& parameter,
|
||||
const gd::ParameterMetadata& metadata,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& lastObjectName,
|
||||
@@ -1224,7 +1220,7 @@ gd::String EventsCodeGenerator::GenerateProfilerSectionEnd(
|
||||
ConvertToStringExplicit(section) + "); }";
|
||||
}
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(gd::Project& project,
|
||||
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project,
|
||||
const gd::Layout& layout)
|
||||
: gd::EventsCodeGenerator(project, layout, JsPlatform::Get()) {}
|
||||
|
||||
|
@@ -44,7 +44,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
*
|
||||
* \return JavaScript code
|
||||
*/
|
||||
static gd::String GenerateLayoutCode(gd::Project& project,
|
||||
static gd::String GenerateLayoutCode(const gd::Project& project,
|
||||
const gd::Layout& scene,
|
||||
const gd::String& codeNamespace,
|
||||
std::set<gd::String>& includeFiles,
|
||||
@@ -174,7 +174,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
|
||||
protected:
|
||||
virtual gd::String GenerateParameterCodes(
|
||||
const gd::String& parameter,
|
||||
const gd::Expression& parameter,
|
||||
const gd::ParameterMetadata& metadata,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& lastObjectName,
|
||||
@@ -289,7 +289,6 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
|
||||
private:
|
||||
static gd::String GenerateEventsListCompleteFunctionCode(
|
||||
gd::Project& project,
|
||||
gdjs::EventsCodeGenerator& codeGenerator,
|
||||
gd::String fullyQualifiedFunctionName,
|
||||
gd::String functionArgumentsCode,
|
||||
@@ -352,7 +351,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
/**
|
||||
* \brief Construct a code generator for the specified project and layout.
|
||||
*/
|
||||
EventsCodeGenerator(gd::Project& project, const gd::Layout& layout);
|
||||
EventsCodeGenerator(const gd::Project& project, const gd::Layout& layout);
|
||||
|
||||
/**
|
||||
* \brief Construct a code generator for the specified objects and groups.
|
||||
|
@@ -22,7 +22,7 @@ namespace gdjs {
|
||||
*/
|
||||
class LayoutCodeGenerator {
|
||||
public:
|
||||
LayoutCodeGenerator(gd::Project& project_)
|
||||
LayoutCodeGenerator(const gd::Project& project_)
|
||||
: project(project_){};
|
||||
|
||||
/**
|
||||
@@ -34,7 +34,7 @@ class LayoutCodeGenerator {
|
||||
bool compilationForRuntime);
|
||||
|
||||
private:
|
||||
gd::Project& project;
|
||||
const gd::Project& project;
|
||||
};
|
||||
|
||||
} // namespace gdjs
|
||||
|
@@ -367,14 +367,16 @@ BaseObjectExtension::BaseObjectExtension() {
|
||||
codeGenerator,
|
||||
context,
|
||||
"number",
|
||||
instruction.GetParameter(2).GetPlainString());
|
||||
instruction.GetParameter(2).GetPlainString(),
|
||||
instruction.GetParameter(0).GetPlainString());
|
||||
|
||||
gd::String expression2Code =
|
||||
gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator,
|
||||
context,
|
||||
"number",
|
||||
instruction.GetParameter(4).GetPlainString());
|
||||
instruction.GetParameter(4).GetPlainString(),
|
||||
instruction.GetParameter(0).GetPlainString());
|
||||
|
||||
gd::String op1 = instruction.GetParameter(1).GetPlainString();
|
||||
gd::String newX =
|
||||
@@ -422,14 +424,16 @@ BaseObjectExtension::BaseObjectExtension() {
|
||||
codeGenerator,
|
||||
context,
|
||||
"number",
|
||||
instruction.GetParameter(2).GetPlainString());
|
||||
instruction.GetParameter(2).GetPlainString(),
|
||||
instruction.GetParameter(0).GetPlainString());
|
||||
|
||||
gd::String expression2Code =
|
||||
gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator,
|
||||
context,
|
||||
"number",
|
||||
instruction.GetParameter(4).GetPlainString());
|
||||
instruction.GetParameter(4).GetPlainString(),
|
||||
instruction.GetParameter(0).GetPlainString());
|
||||
|
||||
gd::String op1 = instruction.GetParameter(1).GetPlainString();
|
||||
gd::String newX = isNotAssignmentOperator(op1)
|
||||
|
@@ -77,6 +77,43 @@ bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
fs.ClearDir(options.exportPath);
|
||||
std::vector<gd::String> includesFiles;
|
||||
|
||||
const gd::Project &immutableProject = options.project;
|
||||
|
||||
// Export engine libraries
|
||||
AddLibsInclude(/*pixiRenderers=*/true,
|
||||
/*includeWebsocketDebuggerClient=*/
|
||||
!options.websocketDebuggerServerAddress.empty(),
|
||||
/*includeWindowMessageDebuggerClient=*/
|
||||
options.useWindowMessageDebuggerClient,
|
||||
immutableProject.GetLoadingScreen().GetGDevelopLogoStyle(),
|
||||
includesFiles);
|
||||
|
||||
// Export files for object and behaviors
|
||||
ExportObjectAndBehaviorsIncludes(immutableProject, includesFiles);
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to
|
||||
// the engine)
|
||||
ExportEffectIncludes(immutableProject, includesFiles);
|
||||
|
||||
previousTime = LogTimeSpent("Include files export", previousTime);
|
||||
|
||||
if (!options.projectDataOnlyExport) {
|
||||
// Generate events code
|
||||
if (!ExportEventsCode(immutableProject, codeOutputDir, includesFiles, true))
|
||||
return false;
|
||||
|
||||
// Export source files
|
||||
if (!ExportExternalSourceFiles(
|
||||
immutableProject, codeOutputDir, includesFiles)) {
|
||||
gd::LogError(
|
||||
_("Error during exporting! Unable to export source files:\n") +
|
||||
lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
previousTime = LogTimeSpent("Events code export", previousTime);
|
||||
}
|
||||
|
||||
gd::Project exportedProject = options.project;
|
||||
|
||||
if (!options.fullLoadingScreen) {
|
||||
@@ -99,41 +136,6 @@ bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
fs, exportedProject.GetResourcesManager(), options.exportPath);
|
||||
// end of compatibility code
|
||||
|
||||
// Export engine libraries
|
||||
AddLibsInclude(/*pixiRenderers=*/true,
|
||||
/*includeWebsocketDebuggerClient=*/
|
||||
!options.websocketDebuggerServerAddress.empty(),
|
||||
/*includeWindowMessageDebuggerClient=*/
|
||||
options.useWindowMessageDebuggerClient,
|
||||
exportedProject.GetLoadingScreen().GetGDevelopLogoStyle(),
|
||||
includesFiles);
|
||||
|
||||
// Export files for object and behaviors
|
||||
ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to
|
||||
// the engine)
|
||||
ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
previousTime = LogTimeSpent("Include files export", previousTime);
|
||||
|
||||
if (!options.projectDataOnlyExport) {
|
||||
// Generate events code
|
||||
if (!ExportEventsCode(exportedProject, codeOutputDir, includesFiles, true))
|
||||
return false;
|
||||
|
||||
// Export source files
|
||||
if (!ExportExternalSourceFiles(
|
||||
exportedProject, codeOutputDir, includesFiles)) {
|
||||
gd::LogError(
|
||||
_("Error during exporting! Unable to export source files:\n") +
|
||||
lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
previousTime = LogTimeSpent("Events code export", previousTime);
|
||||
}
|
||||
|
||||
// Strip the project (*after* generating events as the events may use stripped
|
||||
// things (objects groups...))
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
@@ -623,7 +625,7 @@ void ExporterHelper::RemoveIncludes(bool pixiRenderers,
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportEffectIncludes(
|
||||
gd::Project &project, std::vector<gd::String> &includesFiles) {
|
||||
const gd::Project &project, std::vector<gd::String> &includesFiles) {
|
||||
std::set<gd::String> effectIncludes;
|
||||
|
||||
gd::EffectsCodeGenerator::GenerateEffectsIncludeFiles(
|
||||
@@ -634,7 +636,7 @@ bool ExporterHelper::ExportEffectIncludes(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportEventsCode(gd::Project &project,
|
||||
bool ExporterHelper::ExportEventsCode(const gd::Project &project,
|
||||
gd::String outputDir,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
bool exportForPreview) {
|
||||
@@ -642,7 +644,7 @@ bool ExporterHelper::ExportEventsCode(gd::Project &project,
|
||||
|
||||
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
std::set<gd::String> eventsIncludes;
|
||||
gd::Layout &layout = project.GetLayout(i);
|
||||
const gd::Layout &layout = project.GetLayout(i);
|
||||
LayoutCodeGenerator layoutCodeGenerator(project);
|
||||
gd::String eventsOutput = layoutCodeGenerator.GenerateLayoutCompleteCode(
|
||||
layout, eventsIncludes, !exportForPreview);
|
||||
@@ -664,7 +666,7 @@ bool ExporterHelper::ExportEventsCode(gd::Project &project,
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportExternalSourceFiles(
|
||||
gd::Project &project,
|
||||
const gd::Project &project,
|
||||
gd::String outputDir,
|
||||
std::vector<gd::String> &includesFiles) {
|
||||
const auto &allFiles = project.GetAllSourceFiles();
|
||||
@@ -765,7 +767,7 @@ bool ExporterHelper::ExportIncludesAndLibs(
|
||||
}
|
||||
|
||||
void ExporterHelper::ExportObjectAndBehaviorsIncludes(
|
||||
gd::Project &project, std::vector<gd::String> &includesFiles) {
|
||||
const gd::Project &project, std::vector<gd::String> &includesFiles) {
|
||||
auto addIncludeFiles = [&](const std::vector<gd::String> &newIncludeFiles) {
|
||||
for (const auto &includeFile : newIncludeFiles) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
@@ -798,7 +800,7 @@ void ExporterHelper::ExportObjectAndBehaviorsIncludes(
|
||||
|
||||
addObjectsIncludeFiles(project);
|
||||
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
gd::Layout &layout = project.GetLayout(i);
|
||||
const gd::Layout &layout = project.GetLayout(i);
|
||||
addObjectsIncludeFiles(layout);
|
||||
}
|
||||
}
|
||||
|
@@ -224,7 +224,7 @@ class ExporterHelper {
|
||||
* includesFiles A reference to a vector that will be filled with JS files to
|
||||
* be exported along with the project. ( including "codeX.js" files ).
|
||||
*/
|
||||
bool ExportEventsCode(gd::Project &project,
|
||||
bool ExportEventsCode(const gd::Project &project,
|
||||
gd::String outputDir,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
bool exportForPreview);
|
||||
@@ -232,14 +232,14 @@ class ExporterHelper {
|
||||
/**
|
||||
* \brief Add the project effects include files.
|
||||
*/
|
||||
bool ExportEffectIncludes(gd::Project &project,
|
||||
bool ExportEffectIncludes(const gd::Project &project,
|
||||
std::vector<gd::String> &includesFiles);
|
||||
|
||||
/**
|
||||
* \brief Add the include files for all the objects of the project
|
||||
* and their behaviors.
|
||||
*/
|
||||
void ExportObjectAndBehaviorsIncludes(gd::Project &project,
|
||||
void ExportObjectAndBehaviorsIncludes(const gd::Project &project,
|
||||
std::vector<gd::String> &includesFiles);
|
||||
|
||||
/**
|
||||
@@ -253,7 +253,7 @@ class ExporterHelper {
|
||||
* with JS files to be exported along with the project. (including
|
||||
* "ext-codeX.js" files).
|
||||
*/
|
||||
bool ExportExternalSourceFiles(gd::Project &project,
|
||||
bool ExportExternalSourceFiles(const gd::Project &project,
|
||||
gd::String outputDir,
|
||||
std::vector<gd::String> &includesFiles);
|
||||
|
||||
|
@@ -1074,12 +1074,17 @@ interface Instruction {
|
||||
void SetInverted(boolean inverted);
|
||||
boolean IsInverted();
|
||||
void SetParameter(unsigned long id, [Const] DOMString value);
|
||||
[Const, Ref] DOMString GetParameter(unsigned long id);
|
||||
[Const, Ref] Expression GetParameter(unsigned long id);
|
||||
void SetParametersCount(unsigned long count);
|
||||
unsigned long GetParametersCount();
|
||||
[Ref] InstructionsList GetSubInstructions();
|
||||
};
|
||||
|
||||
interface Expression {
|
||||
[Const, Ref] DOMString GetPlainString();
|
||||
ExpressionNode GetRootNode();
|
||||
};
|
||||
|
||||
interface VectorPairStringTextFormatting {
|
||||
unsigned long size();
|
||||
[Const, Ref] DOMString WRAPPED_GetString(unsigned long id);
|
||||
@@ -1267,6 +1272,7 @@ interface ParameterMetadata {
|
||||
[Ref] ParameterMetadata SetDefaultValue([Const] DOMString defaultValue_);
|
||||
boolean STATIC_IsObject([Const] DOMString param);
|
||||
boolean STATIC_IsBehavior([Const] DOMString param);
|
||||
boolean STATIC_IsExpression([Const] DOMString type_, [Const] DOMString parameterType);
|
||||
|
||||
void SerializeTo([Ref] SerializerElement element);
|
||||
void UnserializeFrom([Const, Ref] SerializerElement element);
|
||||
@@ -2083,7 +2089,7 @@ interface ExpressionParser2NodeWorker {
|
||||
};
|
||||
|
||||
interface ExpressionValidator {
|
||||
void ExpressionValidator();
|
||||
void ExpressionValidator([Const, Ref] Platform platform, [Const, Ref] ObjectsContainer globalObjectsContainer, [Const, Ref] ObjectsContainer objectsContainer, [Const] DOMString rootType);
|
||||
|
||||
[Const, Ref] VectorExpressionParserDiagnostic GetErrors();
|
||||
|
||||
@@ -2117,7 +2123,7 @@ interface VectorExpressionCompletionDescription {
|
||||
};
|
||||
|
||||
interface ExpressionCompletionFinder {
|
||||
[Value] VectorExpressionCompletionDescription STATIC_GetCompletionDescriptionsFor([Ref] ExpressionNode node, unsigned long location);
|
||||
[Value] VectorExpressionCompletionDescription STATIC_GetCompletionDescriptionsFor([Const, Ref] Platform platform, [Const, Ref] ObjectsContainer globalObjectsContainer, [Const, Ref] ObjectsContainer objectsContainer, [Const] DOMString rootType, [Ref] ExpressionNode node, unsigned long location);
|
||||
|
||||
[Const, Ref] VectorExpressionCompletionDescription GetCompletionDescriptions();
|
||||
|
||||
@@ -2133,9 +2139,9 @@ interface UniquePtrExpressionNode {
|
||||
};
|
||||
|
||||
interface ExpressionParser2 {
|
||||
void ExpressionParser2([Const, Ref] Platform platform, [Const, Ref] ObjectsContainer globalObjectsContainer, [Const, Ref] ObjectsContainer objectsContainer);
|
||||
void ExpressionParser2();
|
||||
|
||||
[Value] UniquePtrExpressionNode ParseExpression([Const] DOMString type, [Const] DOMString expression);
|
||||
[Value] UniquePtrExpressionNode ParseExpression([Const] DOMString expression);
|
||||
};
|
||||
|
||||
enum EventsFunction_FunctionType {
|
||||
|
@@ -498,6 +498,7 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
|
||||
#define STATIC_FromJSON(x) FromJSON(x)
|
||||
#define STATIC_IsObject IsObject
|
||||
#define STATIC_IsBehavior IsBehavior
|
||||
#define STATIC_IsExpression IsExpression
|
||||
#define STATIC_Get Get
|
||||
#define STATIC_GetAllUseless GetAllUseless
|
||||
#define STATIC_RemoveAllUseless RemoveAllUseless
|
||||
|
@@ -1954,9 +1954,9 @@ describe('libGD.js', function () {
|
||||
let instr = new gd.Instruction();
|
||||
instr.setParametersCount(3);
|
||||
expect(instr.getParametersCount()).toBe(3);
|
||||
expect(instr.getParameter(1)).toBe('');
|
||||
expect(instr.getParameter(1).getPlainString()).toBe('');
|
||||
instr.setParameter(2, 'MyValue');
|
||||
expect(instr.getParameter(2)).toBe('MyValue');
|
||||
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
|
||||
instr.delete();
|
||||
});
|
||||
it('can be cloned', function () {
|
||||
@@ -1966,14 +1966,14 @@ describe('libGD.js', function () {
|
||||
|
||||
let newInstr = instr.clone();
|
||||
expect(newInstr.getParametersCount()).toBe(3);
|
||||
expect(newInstr.getParameter(1)).toBe('');
|
||||
expect(newInstr.getParameter(2)).toBe('MyValue');
|
||||
expect(newInstr.getParameter(1).getPlainString()).toBe('');
|
||||
expect(newInstr.getParameter(2).getPlainString()).toBe('MyValue');
|
||||
|
||||
newInstr.setParameter(2, 'MyChangedValue');
|
||||
expect(instr.getParameter(2)).toBe('MyValue');
|
||||
expect(newInstr.getParameter(2)).toBe('MyChangedValue');
|
||||
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
|
||||
expect(newInstr.getParameter(2).getPlainString()).toBe('MyChangedValue');
|
||||
newInstr.delete();
|
||||
expect(instr.getParameter(2)).toBe('MyValue');
|
||||
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
|
||||
|
||||
instr.delete();
|
||||
});
|
||||
@@ -2065,9 +2065,9 @@ describe('libGD.js', function () {
|
||||
expect(list2.get(1).getType()).toBe('Type2');
|
||||
expect(list2.get(0).getParametersCount()).toBe(2);
|
||||
expect(list2.get(1).getParametersCount()).toBe(1);
|
||||
expect(list2.get(0).getParameter(0)).toBe('Param1');
|
||||
expect(list2.get(0).getParameter(1)).toBe('Param2');
|
||||
expect(list2.get(1).getParameter(0)).toBe('Param3');
|
||||
expect(list2.get(0).getParameter(0).getPlainString()).toBe('Param1');
|
||||
expect(list2.get(0).getParameter(1).getPlainString()).toBe('Param2');
|
||||
expect(list2.get(1).getParameter(0).getPlainString()).toBe('Param3');
|
||||
|
||||
list2.delete();
|
||||
project.delete();
|
||||
@@ -3289,18 +3289,36 @@ describe('libGD.js', function () {
|
||||
type,
|
||||
expression,
|
||||
expectedError,
|
||||
expectedErrorPosition
|
||||
expectedErrorPosition,
|
||||
expectedError2,
|
||||
expectedErrorPosition2
|
||||
) {
|
||||
const parser = new gd.ExpressionParser2(
|
||||
const parser = new gd.ExpressionParser2();
|
||||
const expressionNode = parser.parseExpression(expression).get();
|
||||
|
||||
const expressionValidator = new gd.ExpressionValidator(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
layout
|
||||
);
|
||||
const expressionNode = parser.parseExpression(type, expression).get();
|
||||
|
||||
const expressionValidator = new gd.ExpressionValidator();
|
||||
layout,
|
||||
type);
|
||||
expressionNode.visit(expressionValidator);
|
||||
if (expectedError) {
|
||||
if (expectedError2) {
|
||||
expect(expressionValidator.getErrors().size()).toBe(2);
|
||||
expect(expressionValidator.getErrors().at(0).getMessage()).toBe(
|
||||
expectedError
|
||||
);
|
||||
if (expectedErrorPosition)
|
||||
expect(expressionValidator.getErrors().at(0).getStartPosition()).toBe(
|
||||
expectedErrorPosition
|
||||
);
|
||||
expect(expressionValidator.getErrors().at(1).getMessage()).toBe(
|
||||
expectedError2
|
||||
);
|
||||
if (expectedErrorPosition2)
|
||||
expect(expressionValidator.getErrors().at(1).getStartPosition()).toBe(
|
||||
expectedErrorPosition2
|
||||
);
|
||||
} else if (expectedError) {
|
||||
expect(expressionValidator.getErrors().size()).toBe(1);
|
||||
expect(expressionValidator.getErrors().at(0).getMessage()).toBe(
|
||||
expectedError
|
||||
@@ -3361,6 +3379,8 @@ describe('libGD.js', function () {
|
||||
testExpression(
|
||||
'number',
|
||||
'3..14',
|
||||
'More than one term was found. Verify that your expression is properly written.',
|
||||
2,
|
||||
'No operator found. Did you forget to enter an operator (like +, -, * or /) between numbers or expressions?',
|
||||
2
|
||||
);
|
||||
@@ -3410,12 +3430,12 @@ describe('libGD.js', function () {
|
||||
testExpression(
|
||||
'number',
|
||||
'abs(-5, 3)',
|
||||
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name."
|
||||
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be exactly 1"
|
||||
);
|
||||
testExpression(
|
||||
'number',
|
||||
'MouseX("", 0, 0) + 1',
|
||||
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name."
|
||||
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be: 0-2"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3434,7 +3454,7 @@ describe('libGD.js', function () {
|
||||
testExpression(
|
||||
'number',
|
||||
'MySpriteObject.PointX("Point", 2)',
|
||||
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name."
|
||||
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be exactly 1"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3465,14 +3485,14 @@ describe('libGD.js', function () {
|
||||
}
|
||||
const expression = expressionWithCaret.replace('|', '');
|
||||
|
||||
const parser = new gd.ExpressionParser2(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
layout
|
||||
);
|
||||
const expressionNode = parser.parseExpression(type, expression).get();
|
||||
const parser = new gd.ExpressionParser2();
|
||||
const expressionNode = parser.parseExpression(expression).get();
|
||||
const completionDescriptions =
|
||||
gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
layout,
|
||||
type,
|
||||
expressionNode,
|
||||
// We're looking for completion for the character just before the caret.
|
||||
Math.max(0, caretPosition - 1)
|
||||
|
7
GDevelop.js/types/gdexpression.js
Normal file
7
GDevelop.js/types/gdexpression.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdExpression {
|
||||
getPlainString(): string;
|
||||
getRootNode(): gdExpressionNode;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
@@ -1,6 +1,6 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdExpressionCompletionFinder {
|
||||
static getCompletionDescriptionsFor(node: gdExpressionNode, location: number): gdVectorExpressionCompletionDescription;
|
||||
static getCompletionDescriptionsFor(platform: gdPlatform, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, rootType: string, node: gdExpressionNode, location: number): gdVectorExpressionCompletionDescription;
|
||||
getCompletionDescriptions(): gdVectorExpressionCompletionDescription;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdExpressionParser2 {
|
||||
constructor(platform: gdPlatform, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer): void;
|
||||
parseExpression(type: string, expression: string): gdUniquePtrExpressionNode;
|
||||
constructor(): void;
|
||||
parseExpression(expression: string): gdUniquePtrExpressionNode;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
@@ -1,6 +1,6 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdExpressionValidator extends gdExpressionParser2NodeWorker {
|
||||
constructor(): void;
|
||||
constructor(platform: gdPlatform, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, rootType: string): void;
|
||||
getErrors(): gdVectorExpressionParserDiagnostic;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
|
@@ -7,7 +7,7 @@ declare class gdInstruction {
|
||||
setInverted(inverted: boolean): void;
|
||||
isInverted(): boolean;
|
||||
setParameter(id: number, value: string): void;
|
||||
getParameter(id: number): string;
|
||||
getParameter(id: number): gdExpression;
|
||||
setParametersCount(count: number): void;
|
||||
getParametersCount(): number;
|
||||
getSubInstructions(): gdInstructionsList;
|
||||
|
@@ -19,6 +19,7 @@ declare class gdParameterMetadata {
|
||||
setDefaultValue(defaultValue_: string): gdParameterMetadata;
|
||||
static isObject(param: string): boolean;
|
||||
static isBehavior(param: string): boolean;
|
||||
static isExpression(type_: string, parameterType: string): boolean;
|
||||
serializeTo(element: gdSerializerElement): void;
|
||||
unserializeFrom(element: gdSerializerElement): void;
|
||||
delete(): void;
|
||||
|
@@ -104,6 +104,7 @@ declare class libGDevelop {
|
||||
Serializer: Class<gdSerializer>;
|
||||
InstructionsList: Class<gdInstructionsList>;
|
||||
Instruction: Class<gdInstruction>;
|
||||
Expression: Class<gdExpression>;
|
||||
VectorPairStringTextFormatting: Class<gdVectorPairStringTextFormatting>;
|
||||
TextFormatting: Class<gdTextFormatting>;
|
||||
InstructionSentenceFormatter: Class<gdInstructionSentenceFormatter>;
|
||||
|
@@ -44,6 +44,18 @@ const styles = {
|
||||
cursor: 'pointer',
|
||||
marginBottom: 1,
|
||||
},
|
||||
input: {
|
||||
fontFamily: '"Lucida Console", Monaco, monospace',
|
||||
lineHeight: 1.4,
|
||||
},
|
||||
backgroundHighlightingInline: {
|
||||
marginTop: 0, //Properly align with the text field
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
},
|
||||
textFieldAndHightlightContainer: {
|
||||
position: 'relative',
|
||||
},
|
||||
};
|
||||
|
||||
export const reactDndInstructionType = 'GD_DRAGGED_INSTRUCTION';
|
||||
@@ -93,6 +105,9 @@ type Props = {|
|
||||
|
||||
screenType: ScreenType,
|
||||
windowWidth: WidthType,
|
||||
|
||||
globalObjectsContainer: gdObjectsContainer,
|
||||
objectsContainer: gdObjectsContainer,
|
||||
|};
|
||||
|
||||
const Instruction = (props: Props) => {
|
||||
@@ -102,6 +117,8 @@ const Instruction = (props: Props) => {
|
||||
onClick,
|
||||
onMoveToInstruction,
|
||||
onContextMenu,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
} = props;
|
||||
|
||||
const instrFormatter = React.useMemo(
|
||||
@@ -149,6 +166,34 @@ const Instruction = (props: Props) => {
|
||||
|
||||
const parameterMetadata = metadata.getParameter(parameterIndex);
|
||||
const parameterType = parameterMetadata.getType();
|
||||
let expressionIsValid = true;
|
||||
if (
|
||||
gd.ParameterMetadata.isExpression('number', parameterType) ||
|
||||
gd.ParameterMetadata.isExpression('string', parameterType) ||
|
||||
gd.ParameterMetadata.isExpression('variable', parameterType)
|
||||
) {
|
||||
const expressionNode = instruction
|
||||
.getParameter(parameterIndex)
|
||||
.getRootNode();
|
||||
const expressionValidator = new gd.ExpressionValidator(
|
||||
gd.JsPlatform.get(),
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
parameterType
|
||||
);
|
||||
expressionNode.visit(expressionValidator);
|
||||
expressionIsValid = expressionValidator.getErrors().size() === 0;
|
||||
} else if (gd.ParameterMetadata.isObject(parameterType)) {
|
||||
const objectOrGroupName = instruction
|
||||
.getParameter(parameterIndex)
|
||||
.getPlainString();
|
||||
expressionIsValid =
|
||||
globalObjectsContainer.hasObjectNamed(objectOrGroupName) ||
|
||||
objectsContainer.hasObjectNamed(objectOrGroupName) ||
|
||||
globalObjectsContainer.getObjectGroups().has(objectOrGroupName) ||
|
||||
objectsContainer.getObjectGroups().has(objectOrGroupName);
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
key={i}
|
||||
@@ -177,6 +222,7 @@ const Instruction = (props: Props) => {
|
||||
>
|
||||
{ParameterRenderingService.renderInlineParameter({
|
||||
value: formattedTexts.getString(i),
|
||||
expressionIsValid,
|
||||
parameterMetadata,
|
||||
renderObjectThumbnail,
|
||||
InvalidParameterValue,
|
||||
@@ -352,6 +398,8 @@ const Instruction = (props: Props) => {
|
||||
renderObjectThumbnail={props.renderObjectThumbnail}
|
||||
screenType={props.screenType}
|
||||
windowWidth={props.windowWidth}
|
||||
globalObjectsContainer={props.globalObjectsContainer}
|
||||
objectsContainer={props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@@ -48,6 +48,9 @@ type Props = {
|
||||
|
||||
screenType: ScreenType,
|
||||
windowWidth: WidthType,
|
||||
|
||||
globalObjectsContainer: gdObjectsContainer,
|
||||
objectsContainer: gdObjectsContainer,
|
||||
};
|
||||
|
||||
const DropTarget = makeDropTarget<{
|
||||
@@ -74,6 +77,8 @@ export default function InstructionsList({
|
||||
renderObjectThumbnail,
|
||||
screenType,
|
||||
windowWidth,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
}: Props) {
|
||||
const [canPaste, setCanPaste] = React.useState(false);
|
||||
|
||||
@@ -143,6 +148,8 @@ export default function InstructionsList({
|
||||
renderObjectThumbnail={renderObjectThumbnail}
|
||||
screenType={screenType}
|
||||
windowWidth={windowWidth}
|
||||
globalObjectsContainer={globalObjectsContainer}
|
||||
objectsContainer={objectsContainer}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@@ -299,6 +299,8 @@ export default class ForEachChildVariableEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
renderActionsList={({ className }) => (
|
||||
@@ -327,6 +329,8 @@ export default class ForEachChildVariableEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@@ -156,6 +156,8 @@ export default class ForEachEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
renderActionsList={({ className }) => (
|
||||
@@ -184,6 +186,8 @@ export default class ForEachEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@@ -155,6 +155,8 @@ export default class RepeatEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
renderActionsList={({ className }) => (
|
||||
@@ -183,6 +185,8 @@ export default class RepeatEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@@ -53,6 +53,8 @@ export default class StandardEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
renderActionsList={({ className }) => (
|
||||
@@ -79,6 +81,8 @@ export default class StandardEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@@ -71,6 +71,8 @@ export default class ForEachEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
<div
|
||||
className={classNames({
|
||||
@@ -104,6 +106,8 @@ export default class ForEachEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
renderActionsList={({ className }) => (
|
||||
@@ -132,6 +136,8 @@ export default class ForEachEvent extends React.Component<
|
||||
renderObjectThumbnail={this.props.renderObjectThumbnail}
|
||||
screenType={this.props.screenType}
|
||||
windowWidth={this.props.windowWidth}
|
||||
globalObjectsContainer={this.props.globalObjectsContainer}
|
||||
objectsContainer={this.props.objectsContainer}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@@ -123,7 +123,7 @@ export default class InlineParameterEditor extends React.Component<
|
||||
instruction,
|
||||
instructionMetadata,
|
||||
objectParameterIndex !== -1
|
||||
? instruction.getParameter(objectParameterIndex)
|
||||
? instruction.getParameter(objectParameterIndex).getPlainString()
|
||||
: null
|
||||
);
|
||||
}
|
||||
@@ -150,7 +150,9 @@ export default class InlineParameterEditor extends React.Component<
|
||||
instructionMetadata={this.state.instructionMetadata}
|
||||
parameterMetadata={this.state.parameterMetadata}
|
||||
parameterIndex={this.props.parameterIndex}
|
||||
value={instruction.getParameter(this.props.parameterIndex)}
|
||||
value={instruction
|
||||
.getParameter(this.props.parameterIndex)
|
||||
.getPlainString()}
|
||||
onChange={this.props.onChange}
|
||||
onRequestClose={this.props.onRequestClose}
|
||||
onApply={this._onApply}
|
||||
|
@@ -323,9 +323,12 @@ export default class InstructionParametersEditor extends React.Component<
|
||||
instruction={instruction}
|
||||
parameterMetadata={parameterMetadata}
|
||||
parameterIndex={i}
|
||||
value={instruction.getParameter(i)}
|
||||
value={instruction.getParameter(i).getPlainString()}
|
||||
onChange={value => {
|
||||
if (instruction.getParameter(i) !== value) {
|
||||
if (
|
||||
instruction.getParameter(i).getPlainString() !==
|
||||
value
|
||||
) {
|
||||
instruction.setParameter(i, value);
|
||||
this.setState({
|
||||
isDirty: true,
|
||||
|
@@ -145,7 +145,7 @@ export const useNewInstructionEditor = ({
|
||||
);
|
||||
if (objectParameterIndex !== -1) {
|
||||
return getChosenObjectState(
|
||||
instruction.getParameter(objectParameterIndex),
|
||||
instruction.getParameter(objectParameterIndex).getPlainString(),
|
||||
false /* Even if the instruction is invalid for the object, show it as it's what we have already */
|
||||
);
|
||||
}
|
||||
|
@@ -39,12 +39,16 @@ export default class DefaultField extends React.Component<
|
||||
|
||||
export const renderInlineDefaultField = ({
|
||||
value,
|
||||
expressionIsValid,
|
||||
parameterMetadata,
|
||||
InvalidParameterValue,
|
||||
MissingParameterValue,
|
||||
}: ParameterInlineRendererProps) => {
|
||||
if (!value && !parameterMetadata.isOptional()) {
|
||||
return <MissingParameterValue />;
|
||||
}
|
||||
|
||||
if (!expressionIsValid) {
|
||||
return <InvalidParameterValue>{value}</InvalidParameterValue>;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
@@ -116,12 +116,21 @@ type Props = {|
|
||||
const MAX_ERRORS_COUNT = 10;
|
||||
|
||||
const extractErrors = (
|
||||
platform: gdPlatform,
|
||||
globalObjectsContainer: gdObjectsContainer,
|
||||
objectsContainer: gdObjectsContainer,
|
||||
expressionType: string,
|
||||
expressionNode: gdExpressionNode
|
||||
): {|
|
||||
errorText: ?string,
|
||||
errorHighlights: Array<Highlight>,
|
||||
|} => {
|
||||
const expressionValidator = new gd.ExpressionValidator();
|
||||
const expressionValidator = new gd.ExpressionValidator(
|
||||
gd.JsPlatform.get(),
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
expressionType
|
||||
);
|
||||
expressionNode.visit(expressionValidator);
|
||||
const errors = expressionValidator.getErrors();
|
||||
|
||||
@@ -370,17 +379,17 @@ export default class ExpressionField extends React.Component<Props, State> {
|
||||
// Parsing can be time consuming (~1ms for simple expression,
|
||||
// a few milliseconds for complex ones).
|
||||
|
||||
const parser = new gd.ExpressionParser2(
|
||||
const parser = new gd.ExpressionParser2();
|
||||
|
||||
const expressionNode = parser.parseExpression(expression).get();
|
||||
|
||||
const { errorText, errorHighlights } = extractErrors(
|
||||
gd.JsPlatform.get(),
|
||||
globalObjectsContainer,
|
||||
objectsContainer
|
||||
objectsContainer,
|
||||
expressionType,
|
||||
expressionNode
|
||||
);
|
||||
|
||||
const expressionNode = parser
|
||||
.parseExpression(expressionType, expression)
|
||||
.get();
|
||||
|
||||
const { errorText, errorHighlights } = extractErrors(expressionNode);
|
||||
const extraErrorText = onExtractAdditionalErrors
|
||||
? onExtractAdditionalErrors(expression, expressionNode)
|
||||
: null;
|
||||
@@ -406,6 +415,10 @@ export default class ExpressionField extends React.Component<Props, State> {
|
||||
? this._inputElement.selectionStart
|
||||
: 0;
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
expressionType,
|
||||
expressionNode,
|
||||
cursorPosition - 1
|
||||
);
|
||||
|
@@ -88,6 +88,8 @@ export const renderInlineObjectWithThumbnail = ({
|
||||
value,
|
||||
parameterMetadata,
|
||||
renderObjectThumbnail,
|
||||
expressionIsValid,
|
||||
InvalidParameterValue,
|
||||
MissingParameterValue,
|
||||
}: ParameterInlineRendererProps) => {
|
||||
if (!value && !parameterMetadata.isOptional()) {
|
||||
@@ -102,7 +104,11 @@ export const renderInlineObjectWithThumbnail = ({
|
||||
})}
|
||||
>
|
||||
{renderObjectThumbnail(value)}
|
||||
{value}
|
||||
{expressionIsValid ? (
|
||||
value
|
||||
) : (
|
||||
<InvalidParameterValue>{value}</InvalidParameterValue>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
@@ -12,6 +12,7 @@ export type InvalidParameterValueProps = {|
|
||||
export type ParameterInlineRendererProps = {|
|
||||
parameterMetadata: gdParameterMetadata,
|
||||
value: string,
|
||||
expressionIsValid: boolean,
|
||||
renderObjectThumbnail: string => React.Node,
|
||||
InvalidParameterValue: InvalidParameterValueProps => React.Node,
|
||||
MissingParameterValue: () => React.Node,
|
||||
|
@@ -39,7 +39,9 @@ export const getLastObjectParameterValue = ({
|
||||
objectParameterIndex >= 0 &&
|
||||
objectParameterIndex < instruction.getParametersCount()
|
||||
) {
|
||||
objectName = instruction.getParameter(objectParameterIndex);
|
||||
objectName = instruction
|
||||
.getParameter(objectParameterIndex)
|
||||
.getPlainString();
|
||||
}
|
||||
} else if (expressionMetadata && expression) {
|
||||
const objectParameterIndex = gd.ParameterMetadataTools.getObjectParameterIndexFor(
|
||||
@@ -95,7 +97,7 @@ export const getPreviousParameterValue = ({
|
||||
parameterIndex >= 1 &&
|
||||
parameterIndex < instruction.getParametersCount()
|
||||
) {
|
||||
return instruction.getParameter(parameterIndex - 1);
|
||||
return instruction.getParameter(parameterIndex - 1).getPlainString();
|
||||
}
|
||||
} else if (expression) {
|
||||
if (
|
||||
|
@@ -203,6 +203,8 @@ export const renderVariableWithIcon = (
|
||||
{
|
||||
value,
|
||||
parameterMetadata,
|
||||
expressionIsValid,
|
||||
InvalidParameterValue,
|
||||
MissingParameterValue,
|
||||
}: ParameterInlineRendererProps,
|
||||
iconPath: string,
|
||||
@@ -229,7 +231,11 @@ export const renderVariableWithIcon = (
|
||||
src={iconPath}
|
||||
alt=""
|
||||
/>
|
||||
{value}
|
||||
{expressionIsValid ? (
|
||||
value
|
||||
) : (
|
||||
<InvalidParameterValue>{value}</InvalidParameterValue>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
@@ -47,11 +47,7 @@ const makeTestContext = () => {
|
||||
'Draggable'
|
||||
);
|
||||
|
||||
const parser = new gd.ExpressionParser2(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout
|
||||
);
|
||||
const parser = new gd.ExpressionParser2();
|
||||
|
||||
return {
|
||||
project,
|
||||
@@ -65,8 +61,12 @@ describe('ExpressionAutocompletion', () => {
|
||||
const { project, testLayout, parser } = makeTestContext();
|
||||
const scope = { layout: testLayout };
|
||||
|
||||
const expressionNode = parser.parseExpression('number', 'My').get();
|
||||
const expressionNode = parser.parseExpression('My').get();
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode,
|
||||
1
|
||||
);
|
||||
@@ -96,10 +96,12 @@ describe('ExpressionAutocompletion', () => {
|
||||
])
|
||||
);
|
||||
|
||||
const expressionNode2 = parser
|
||||
.parseExpression('number', 'MySpriteObjectW')
|
||||
.get();
|
||||
const expressionNode2 = parser.parseExpression('MySpriteObjectW').get();
|
||||
const completionDescriptions2 = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode2,
|
||||
1
|
||||
);
|
||||
@@ -129,8 +131,12 @@ describe('ExpressionAutocompletion', () => {
|
||||
const { project, testLayout, parser } = makeTestContext();
|
||||
const scope = { layout: testLayout };
|
||||
|
||||
const expressionNode = parser.parseExpression('number', 'To').get();
|
||||
const expressionNode = parser.parseExpression('To').get();
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode,
|
||||
1
|
||||
);
|
||||
@@ -169,8 +175,12 @@ describe('ExpressionAutocompletion', () => {
|
||||
const { project, testLayout, parser } = makeTestContext();
|
||||
const scope = { layout: testLayout };
|
||||
|
||||
const expressionNode = parser.parseExpression('number', 'MouseX("Ba').get();
|
||||
const expressionNode = parser.parseExpression('MouseX("Ba').get();
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode,
|
||||
9
|
||||
);
|
||||
@@ -198,10 +208,12 @@ describe('ExpressionAutocompletion', () => {
|
||||
const { project, testLayout, parser } = makeTestContext();
|
||||
const scope = { layout: testLayout };
|
||||
|
||||
const expressionNode = parser
|
||||
.parseExpression('number', 'MySpriteObject.Po')
|
||||
.get();
|
||||
const expressionNode = parser.parseExpression('MySpriteObject.Po').get();
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode,
|
||||
16
|
||||
);
|
||||
@@ -236,9 +248,13 @@ describe('ExpressionAutocompletion', () => {
|
||||
const scope = { layout: testLayout };
|
||||
|
||||
const expressionNode = parser
|
||||
.parseExpression('number', 'MySpriteObject.PointX("He')
|
||||
.parseExpression('MySpriteObject.PointX("He')
|
||||
.get();
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode,
|
||||
24
|
||||
);
|
||||
@@ -267,9 +283,13 @@ describe('ExpressionAutocompletion', () => {
|
||||
const scope = { layout: testLayout };
|
||||
|
||||
const expressionNode = parser
|
||||
.parseExpression('number', 'MySpriteObjectWithBehaviors.Plat')
|
||||
.parseExpression('MySpriteObjectWithBehaviors.Plat')
|
||||
.get();
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode,
|
||||
28
|
||||
);
|
||||
@@ -299,9 +319,13 @@ describe('ExpressionAutocompletion', () => {
|
||||
const scope = { layout: testLayout };
|
||||
|
||||
const expressionNode = parser
|
||||
.parseExpression('number', 'MySpriteObjectWithBehaviors.a')
|
||||
.parseExpression('MySpriteObjectWithBehaviors.a')
|
||||
.get();
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode,
|
||||
28
|
||||
);
|
||||
@@ -336,12 +360,13 @@ describe('ExpressionAutocompletion', () => {
|
||||
const scope = { layout: testLayout };
|
||||
|
||||
const expressionNode = parser
|
||||
.parseExpression(
|
||||
'number',
|
||||
'MySpriteObjectWithBehaviors.PlatformerObject::Jum'
|
||||
)
|
||||
.parseExpression('MySpriteObjectWithBehaviors.PlatformerObject::Jum')
|
||||
.get();
|
||||
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
|
||||
gd.JsPlatform.get(),
|
||||
project,
|
||||
testLayout,
|
||||
'number',
|
||||
expressionNode,
|
||||
47
|
||||
);
|
||||
|
@@ -66,9 +66,9 @@ export const setupInstructionParameters = (
|
||||
});
|
||||
|
||||
if (behaviorNames.length > 0) {
|
||||
const currentParameterValue = instruction.getParameter(
|
||||
maybeBehaviorParameterIndex
|
||||
);
|
||||
const currentParameterValue = instruction
|
||||
.getParameter(maybeBehaviorParameterIndex)
|
||||
.getPlainString();
|
||||
|
||||
// Set the behavior to the first matching behavior, in case a matching behavior name
|
||||
// is not already set.
|
||||
|
@@ -35,11 +35,11 @@ describe('setupInstructionParameters', () => {
|
||||
|
||||
// Check that parameters were created
|
||||
expect(instruction.getParametersCount()).toBe(5);
|
||||
expect(instruction.getParameter(0)).toBe('');
|
||||
expect(instruction.getParameter(1)).toBe('');
|
||||
expect(instruction.getParameter(2)).toBe('');
|
||||
expect(instruction.getParameter(3)).toBe('');
|
||||
expect(instruction.getParameter(4)).toBe('');
|
||||
expect(instruction.getParameter(0).getPlainString()).toBe('');
|
||||
expect(instruction.getParameter(1).getPlainString()).toBe('');
|
||||
expect(instruction.getParameter(2).getPlainString()).toBe('');
|
||||
expect(instruction.getParameter(3).getPlainString()).toBe('');
|
||||
expect(instruction.getParameter(4).getPlainString()).toBe('');
|
||||
});
|
||||
|
||||
it('sets the proper number of parameters and the object name', () => {
|
||||
@@ -74,8 +74,8 @@ describe('setupInstructionParameters', () => {
|
||||
|
||||
// Check that parameters were created and the object name set
|
||||
expect(instruction.getParametersCount()).toBe(2);
|
||||
expect(instruction.getParameter(0)).toBe(objectName);
|
||||
expect(instruction.getParameter(1)).toBe('');
|
||||
expect(instruction.getParameter(0).getPlainString()).toBe(objectName);
|
||||
expect(instruction.getParameter(1).getPlainString()).toBe('');
|
||||
});
|
||||
|
||||
it('sets the proper parameters for a behavior', () => {
|
||||
@@ -116,10 +116,12 @@ describe('setupInstructionParameters', () => {
|
||||
|
||||
// Check that parameters were created, the object name and behavior set
|
||||
expect(instruction.getParametersCount()).toBe(4);
|
||||
expect(instruction.getParameter(0)).toBe(objectName);
|
||||
expect(instruction.getParameter(1)).toBe('PlatformerObject');
|
||||
expect(instruction.getParameter(2)).toBe(''); // In the future, this could be set to a default value.
|
||||
expect(instruction.getParameter(3)).toBe('');
|
||||
expect(instruction.getParameter(0).getPlainString()).toBe(objectName);
|
||||
expect(instruction.getParameter(1).getPlainString()).toBe(
|
||||
'PlatformerObject'
|
||||
);
|
||||
expect(instruction.getParameter(2).getPlainString()).toBe(''); // In the future, this could be set to a default value.
|
||||
expect(instruction.getParameter(3).getPlainString()).toBe('');
|
||||
});
|
||||
|
||||
it('sets the proper parameters for a behavior, selecting the first behavior if multiple', () => {
|
||||
@@ -165,8 +167,10 @@ describe('setupInstructionParameters', () => {
|
||||
|
||||
// Check that parameters were created, the object name and behavior set
|
||||
expect(instruction.getParametersCount()).toBe(4);
|
||||
expect(instruction.getParameter(0)).toBe(objectName);
|
||||
expect(instruction.getParameter(1)).toBe('FirstPlatformerObject');
|
||||
expect(instruction.getParameter(0).getPlainString()).toBe(objectName);
|
||||
expect(instruction.getParameter(1).getPlainString()).toBe(
|
||||
'FirstPlatformerObject'
|
||||
);
|
||||
});
|
||||
|
||||
it('sets the proper parameters for a behavior, changing it if a wrong behavior name is entered', () => {
|
||||
@@ -215,8 +219,10 @@ describe('setupInstructionParameters', () => {
|
||||
|
||||
// Check that parameters were created, the object name and behavior set
|
||||
expect(instruction.getParametersCount()).toBe(4);
|
||||
expect(instruction.getParameter(0)).toBe(objectName);
|
||||
expect(instruction.getParameter(1)).toBe('FirstPlatformerObject');
|
||||
expect(instruction.getParameter(0).getPlainString()).toBe(objectName);
|
||||
expect(instruction.getParameter(1).getPlainString()).toBe(
|
||||
'FirstPlatformerObject'
|
||||
);
|
||||
});
|
||||
|
||||
it('sets the proper parameters for a behavior, letting an existing behavior name if it is valid', () => {
|
||||
@@ -265,7 +271,9 @@ describe('setupInstructionParameters', () => {
|
||||
|
||||
// Check that parameters were created, the object name and behavior set
|
||||
expect(instruction.getParametersCount()).toBe(4);
|
||||
expect(instruction.getParameter(0)).toBe(objectName);
|
||||
expect(instruction.getParameter(1)).toBe('OtherPlatformerObject');
|
||||
expect(instruction.getParameter(0).getPlainString()).toBe(objectName);
|
||||
expect(instruction.getParameter(1).getPlainString()).toBe(
|
||||
'OtherPlatformerObject'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -119,6 +119,7 @@ describe('ResourceUtils', () => {
|
||||
.getActions()
|
||||
.get(0)
|
||||
.getParameter(1)
|
||||
.getPlainString()
|
||||
).toBe('Audio1');
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user