diff --git a/Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.cpp b/Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.cpp index 0f4ac3960d..c201669aa2 100644 --- a/Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.cpp +++ b/Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.cpp @@ -605,6 +605,10 @@ gd::String EventsCodeGenerator::GenerateParameterCodes( } else if (ParameterMetadata::IsExpression("variable", metadata.type)) { argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode( *this, context, metadata.type, parameter, previousParameter); + } 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); } else if (metadata.type == "relationalOperator") { argOutput += parameter == "=" ? "==" : parameter; if (argOutput != "==" && argOutput != "<" && argOutput != ">" && @@ -623,7 +627,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes( } argOutput = "\"" + argOutput + "\""; - } else if (metadata.type == "object" || metadata.type == "behavior") { + } else if (metadata.type == "behavior") { argOutput = "\"" + ConvertToString(parameter) + "\""; } else if (metadata.type == "key") { argOutput = "\"" + ConvertToString(parameter) + "\""; diff --git a/Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.h b/Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.h index d94df32b10..c145a20667 100644 --- a/Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.h +++ b/Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.h @@ -423,6 +423,7 @@ class GD_CORE_API EventsCodeGenerator { virtual gd::String GetCodeNamespace() { return ""; }; enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE }; + protected: /** * \brief Generate the code for a single parameter. @@ -482,10 +483,10 @@ class GD_CORE_API EventsCodeGenerator { * \brief Generate the code to get a variable. */ virtual gd::String GenerateGetVariable( - gd::String variableName, + const gd::String& variableName, const VariableScope& scope, gd::EventsCodeGenerationContext& context, - gd::String objectName) { + const gd::String& objectName) { if (scope == LAYOUT_VARIABLE) { return "getLayoutVariable(" + variableName + ")"; @@ -513,13 +514,30 @@ class GD_CORE_API EventsCodeGenerator { }; /** - * \brief Generate the code to reference a variable which is + * \brief Generate the code to reference a variable which is * in an empty/null state. */ - virtual gd::String GenerateBadVariable() { - return "fakeBadVariable"; + virtual gd::String GenerateBadVariable() { return "fakeBadVariable"; } + + /** + * \brief Generate the code to reference an object. + * \param objectName the name of the object. + * \param type what is the expected type (object, objectPtr...) in which the + * object must be generated. + * \param context The context for code generation + */ + virtual gd::String GenerateObject(const gd::String& objectName, + const gd::String& type, + gd::EventsCodeGenerationContext& context) { + return "fakeObjectListOf_" + objectName; } + /** + * \brief Generate the code to reference an object which is + * in an empty/null state. + */ + virtual gd::String GenerateBadObject() { return "fakeNullObject"; } + /** * \brief Call a function of the current object. * \note The current object is the object being manipulated by a condition or diff --git a/Core/GDCore/Events/CodeGeneration/ExpressionCodeGenerator.cpp b/Core/GDCore/Events/CodeGeneration/ExpressionCodeGenerator.cpp index ccda779e9e..402069c72f 100644 --- a/Core/GDCore/Events/CodeGeneration/ExpressionCodeGenerator.cpp +++ b/Core/GDCore/Events/CodeGeneration/ExpressionCodeGenerator.cpp @@ -219,7 +219,15 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode( } void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) { - output += codeGenerator.ConvertToStringExplicit(node.identifierName); + if (gd::ParameterMetadata::IsObject(node.type)) { + output += + codeGenerator.GenerateObject(node.identifierName, node.type, context); + } else { + output += "/* Error during generation, unrecognized identifier type: " + + codeGenerator.ConvertToString(node.type) + " with value " + + codeGenerator.ConvertToString(node.identifierName) + " */ " + + codeGenerator.ConvertToStringExplicit(node.identifierName); + } } void ExpressionCodeGenerator::OnVisitFunctionNode(FunctionNode& node) { @@ -443,8 +451,11 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue( if (gd::ParameterMetadata::IsExpression("variable", type)) { return codeGenerator.GenerateBadVariable(); } + if (gd::ParameterMetadata::IsObject(type)) { + return codeGenerator.GenerateBadObject(); + } - return (type == "string" || type == "identifier") ? "\"\"" : "0"; + return (type == "string") ? "\"\"" : "0"; } void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) { diff --git a/Core/GDCore/Events/Parsers/ExpressionParser2.h b/Core/GDCore/Events/Parsers/ExpressionParser2.h index 9205b34e65..4faafc0377 100644 --- a/Core/GDCore/Events/Parsers/ExpressionParser2.h +++ b/Core/GDCore/Events/Parsers/ExpressionParser2.h @@ -47,15 +47,15 @@ class GD_CORE_API ExpressionParser2 { * Parse the given expression with the specified type. * * \param type Type of the expression: "string", "number", - * "identifier", "scenevar", "globalvar", "objectvar" or "unknown". - * \param expression The expression to parse - * \param objectName Specify the object name, only for the case of "objectvar" - * type. + * type supported by gd::ParameterMetadata::IsObject, types supported by + * gd::ParameterMetadata::IsExpression or "unknown". \param expression The + * expression to parse \param objectName Specify the object name, only for the + * case of "objectvar" type. * * \return The node representing the expression as a parsed tree. */ std::unique_ptr ParseExpression( - const gd::String &type, // TODO: enumify type? + const gd::String &type, const gd::String &expression_, const gd::String &objectName = "") { expression = expression_; @@ -244,7 +244,7 @@ class GD_CORE_API ExpressionParser2 { return ObjectFunctionOrBehaviorFunction( type, name, identifierStartPosition); } else { - auto identifier = gd::make_unique(name); + auto identifier = gd::make_unique(name, type); if (type == "string") { identifier->diagnostic = RaiseTypeError(_("You must wrap your text inside double quotes " @@ -253,10 +253,9 @@ class GD_CORE_API ExpressionParser2 { } else if (type == "number") { identifier->diagnostic = RaiseTypeError(_("You must enter a number."), identifierStartPosition); - } else if (type != "identifier") { + } else if (!gd::ParameterMetadata::IsObject(type)) { identifier->diagnostic = RaiseTypeError( - _("You've entered an identifier, but this type was expected:") + - type, + _("You've entered a name, but this type was expected:") + type, identifierStartPosition); } @@ -454,8 +453,19 @@ class GD_CORE_API ExpressionParser2 { parameters.push_back(Expression("string")); } else if (gd::ParameterMetadata::IsExpression("variable", type)) { parameters.push_back(Expression(type, objectName)); + } else if (gd::ParameterMetadata::IsObject(type)) { + parameters.push_back(Expression(type)); } else { - parameters.push_back(Expression("identifier")); + size_t parameterStartPosition = GetCurrentPosition(); + parameters.push_back(Expression("unknown")); + parameters.back()->diagnostic = + gd::make_unique( + "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(); @@ -512,10 +522,10 @@ class GD_CORE_API ExpressionParser2 { _("You've used an operator that is not supported. Only + can be used " "to concatenate texts."), GetCurrentPosition()); - } else if (type == "identifier") { + } else if (gd::ParameterMetadata::IsObject(type)) { return gd::make_unique( "invalid_operator", - _("Operators (+, -, /, *) can't be used there. Remove the 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( @@ -548,10 +558,10 @@ class GD_CORE_API ExpressionParser2 { "to concatenate texts, and must be placed between two texts (or " "expressions)."), GetCurrentPosition()); - } else if (type == "identifier") { + } else if (gd::ParameterMetadata::IsObject(type)) { return gd::make_unique( "invalid_operator", - _("Operators (+, -) can't be used there. Remove the 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( @@ -708,11 +718,12 @@ class GD_CORE_API ExpressionParser2 { 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."); + 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 (type == "identifier") { - message = _("You must enter a valid name."); + } else if (gd::ParameterMetadata::IsObject(type)) { + message = _("You must enter a valid object name."); } else { message = _("You must enter a valid expression."); } diff --git a/Core/GDCore/Events/Parsers/ExpressionParser2Node.h b/Core/GDCore/Events/Parsers/ExpressionParser2Node.h index 73f6ce8893..d188c7ff0a 100644 --- a/Core/GDCore/Events/Parsers/ExpressionParser2Node.h +++ b/Core/GDCore/Events/Parsers/ExpressionParser2Node.h @@ -60,7 +60,7 @@ struct ExpressionParserError : public ExpressionParserDiagnostic { size_t GetEndPosition() override { return endPosition; } private: - gd::String type; // TODO: Enumify the error type? + gd::String type; gd::String message; size_t startPosition; size_t endPosition; @@ -99,7 +99,7 @@ struct OperatorNode : public ExpressionNode { std::unique_ptr leftHandSide; std::unique_ptr rightHandSide; - gd::String::value_type op; // TODO: Enumify? + gd::String::value_type op; }; /** @@ -113,7 +113,7 @@ struct UnaryOperatorNode : public ExpressionNode { }; std::unique_ptr factor; - gd::String::value_type op; // TODO: Enumify? + gd::String::value_type op; }; /** @@ -206,17 +206,18 @@ struct VariableBracketAccessorNode struct IdentifierOrFunctionOrEmptyNode : public ExpressionNode {}; /** - * \brief An identifier number node. For example: "layer1". + * \brief An identifier node, usually representing an object. */ struct IdentifierNode : public IdentifierOrFunctionOrEmptyNode { - IdentifierNode(const gd::String &identifierName_) - : identifierName(identifierName_){}; + IdentifierNode(const gd::String &identifierName_, const gd::String &type_) + : identifierName(identifierName_), type(type_){}; virtual ~IdentifierNode(){}; virtual void Visit(ExpressionParser2NodeWorker &worker) { worker.OnVisitIdentifierNode(*this); }; gd::String identifierName; + gd::String type; }; struct FunctionOrEmptyNode : public IdentifierOrFunctionOrEmptyNode { diff --git a/Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp b/Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp index 6aaa3459c0..2a690e1a78 100644 --- a/Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp +++ b/Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp @@ -57,13 +57,16 @@ class GD_CORE_API ExpressionObjectsAnalyzer node.expression->Visit(*this); if (node.child) node.child->Visit(*this); } - void OnVisitIdentifierNode(IdentifierNode& node) override {} + void OnVisitIdentifierNode(IdentifierNode& node) override { + if (gd::ParameterMetadata::IsObject(node.type)) { + context.AddObjectName(node.identifierName); + } + } void OnVisitFunctionNode(FunctionNode& node) override { if (!node.objectName.empty()) { context.AddObjectName(node.objectName); } - // TODO: we're potentially missing objects that are asked in parameters - // of type "object", "objectPtr", "objectList". + for (auto& parameter : node.parameters) { parameter->Visit(*this); } diff --git a/Core/GDCore/IDE/Events/EventsRefactorer.cpp b/Core/GDCore/IDE/Events/EventsRefactorer.cpp index e2f77a9e48..4e0fe30de6 100644 --- a/Core/GDCore/IDE/Events/EventsRefactorer.cpp +++ b/Core/GDCore/IDE/Events/EventsRefactorer.cpp @@ -71,7 +71,12 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker { node.expression->Visit(*this); if (node.child) node.child->Visit(*this); } - void OnVisitIdentifierNode(IdentifierNode& node) override {} + void OnVisitIdentifierNode(IdentifierNode& node) override { + if (gd::ParameterMetadata::IsObject(node.type) && node.identifierName == objectName) { + hasDoneRenaming = true; + node.identifierName = objectNewName; + } + } void OnVisitFunctionNode(FunctionNode& node) override { if (node.objectName == objectName) { hasDoneRenaming = true; @@ -138,7 +143,11 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker { node.expression->Visit(*this); if (node.child) node.child->Visit(*this); } - void OnVisitIdentifierNode(IdentifierNode& node) override {} + void OnVisitIdentifierNode(IdentifierNode& node) override { + if (gd::ParameterMetadata::IsObject(node.type) && node.identifierName == objectName) { + hasObject = true; + } + } void OnVisitFunctionNode(FunctionNode& node) override { if (node.objectName == objectName) { hasObject = true; diff --git a/Core/tests/DummyPlatform.cpp b/Core/tests/DummyPlatform.cpp index 5121f9e921..7145a69bcf 100644 --- a/Core/tests/DummyPlatform.cpp +++ b/Core/tests/DummyPlatform.cpp @@ -106,6 +106,16 @@ void SetupProjectWithDummyPlatform(gd::Project &project, .AddParameter("string", _("String parameter")) .AddParameter("", _("Identifier parameter")) .SetFunctionName("getObjectStringWith3Param"); + object + .AddStrExpression("GetObjectStringWith2ObjectParam", + "Get string from object with a 2 objects param", + "", + "", + "") + .AddParameter("object", _("Object"), "Sprite") + .AddParameter("object", _("Object parameter")) + .AddParameter("objectPtr", _("Object parameter")) + .SetFunctionName("getObjectStringWith2ObjectParam"); // auto behavior = extension->AddBehavior("MyBehavior", "Dummy behavior", // "MyBehavior", "", "", "","", // gd::make_unique(), diff --git a/Core/tests/ExpressionCodeGenerator.cpp b/Core/tests/ExpressionCodeGenerator.cpp index 6d3a8386d4..528b576cbb 100644 --- a/Core/tests/ExpressionCodeGenerator.cpp +++ b/Core/tests/ExpressionCodeGenerator.cpp @@ -47,12 +47,14 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") { REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + \"world\""); } { - auto node = parser.ParseExpression("string", "\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\""); + auto node = parser.ParseExpression( + "string", "\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\""); gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator, context); node->Visit(expressionCodeGenerator); - REQUIRE(expressionCodeGenerator.GetOutput() == "\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\""); + REQUIRE(expressionCodeGenerator.GetOutput() == + "\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\""); } } @@ -159,23 +161,6 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") { REQUIRE(expressionCodeGenerator.GetOutput() == "MySpriteObject.getObjectStringWith1Param(getNumber()) ?? \"\""); } - { - auto node = - parser.ParseExpression("string", - "MySpriteObject.GetObjectStringWith3Param(" - "MySpriteObject.GetObjectNumber() / 2.3, " - "MySpriteObject.GetObjectStringWith1Param(" - "MyExtension::GetNumber()), test)"); - gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator, - context); - - node->Visit(expressionCodeGenerator); - REQUIRE(expressionCodeGenerator.GetOutput() == - "MySpriteObject.getObjectStringWith3Param(MySpriteObject." - "getObjectNumber() ?? 0 / 2.3, " - "MySpriteObject.getObjectStringWith1Param(getNumber()) ?? \"\", " - "\"test\") ?? \"\""); - } } SECTION("Valid function calls with optional arguments") { { @@ -216,6 +201,24 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") { } } SECTION("Invalid function calls") { + { + auto node = + parser.ParseExpression("string", + "MySpriteObject.GetObjectStringWith3Param(" + "MySpriteObject.GetObjectNumber() / 2.3, " + "MySpriteObject.GetObjectStringWith1Param(" + "MyExtension::GetNumber()), test)"); + gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator, + context); + + node->Visit(expressionCodeGenerator); + REQUIRE(expressionCodeGenerator.GetOutput() == + "MySpriteObject.getObjectStringWith3Param(MySpriteObject." + "getObjectNumber() ?? 0 / 2.3, " + "MySpriteObject.getObjectStringWith1Param(getNumber()) ?? \"\", " + "/* Error during generation, unrecognized identifier type: " + "unknown with value test */ \"test\") ?? \"\""); + } { auto node = parser.ParseExpression( "number", @@ -370,6 +373,16 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") { } } } + SECTION("Objects") { + gd::String output = gd::ExpressionCodeGenerator::GenerateExpressionCode( + codeGenerator, + context, + "string", + "MySpriteObject.GetObjectStringWith2ObjectParam(Object1, Object2)"); + REQUIRE(output == + "MySpriteObject.getObjectStringWith2ObjectParam(fakeObjectListOf_" + "Object1, fakeObjectListOf_Object2) ?? \"\""); + } SECTION("Mixed test (1)") { { auto node = parser.ParseExpression("number", "-+-MyExtension::MouseX(,)"); diff --git a/Core/tests/ExpressionParser2.cpp b/Core/tests/ExpressionParser2.cpp index a2fa99d143..5592af893d 100644 --- a/Core/tests/ExpressionParser2.cpp +++ b/Core/tests/ExpressionParser2.cpp @@ -510,7 +510,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { SECTION("Valid identifiers") { { - auto node = parser.ParseExpression("identifier", "HelloWorld1"); + auto node = parser.ParseExpression("object", "HelloWorld1"); REQUIRE(node != nullptr); auto &identifierNode = dynamic_cast(*node); REQUIRE(identifierNode.identifierName == "HelloWorld1"); @@ -521,7 +521,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { } { - auto node = parser.ParseExpression("identifier", "Hello World 1 "); + auto node = parser.ParseExpression("object", "Hello World 1 "); REQUIRE(node != nullptr); auto &identifierNode = dynamic_cast(*node); REQUIRE(identifierNode.identifierName == "Hello World 1"); @@ -534,17 +534,17 @@ TEST_CASE("ExpressionParser2", "[common][events]") { SECTION("Invalid identifiers") { { - auto node = parser.ParseExpression("identifier", ""); + auto node = parser.ParseExpression("object", ""); REQUIRE(node != nullptr); gd::ExpressionValidator validator; node->Visit(validator); REQUIRE(validator.GetErrors().size() == 1); REQUIRE(validator.GetErrors()[0]->GetMessage() == - "You must enter a valid name."); + "You must enter a valid object name."); } { - auto node = parser.ParseExpression("identifier", "Hello + World1"); + auto node = parser.ParseExpression("object", "Hello + World1"); REQUIRE(node != nullptr); gd::ExpressionValidator validator; @@ -552,7 +552,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { REQUIRE(validator.GetErrors().size() == 1); REQUIRE( validator.GetErrors()[0]->GetMessage() == - "Operators (+, -, /, *) can't be used there. Remove the operator."); + "Operators (+, -, /, *) can't be used with an object name. Remove the operator."); } } @@ -826,7 +826,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") { testExpressionWithType("scenevar"); testExpressionWithType("globalvar"); testExpressionWithType("objectvar"); - testExpressionWithType("identifier"); + testExpressionWithType("object"); + testExpressionWithType("objectPtr"); testExpressionWithType("unknown"); }; diff --git a/Core/tests/ExpressionParser2NodePrinter.cpp b/Core/tests/ExpressionParser2NodePrinter.cpp index 5fa1784b8f..995fc5c281 100644 --- a/Core/tests/ExpressionParser2NodePrinter.cpp +++ b/Core/tests/ExpressionParser2NodePrinter.cpp @@ -141,12 +141,14 @@ TEST_CASE("ExpressionParser2NodePrinter", "[common][events]") { testPrinter("string", "((\"hello world\") + (123))"); } - SECTION("Valid identifiers") { - testPrinter("identifier", "HelloWorld1"); - testPrinter("identifier", "Hello World 1 ", "Hello World 1"); + SECTION("Valid objects") { + testPrinter("object", "HelloWorld1"); + testPrinter("object", "Hello World 1 ", "Hello World 1"); + testPrinter("objectPtr", "HelloWorld1"); + testPrinter("objectPtr", "Hello World 1 ", "Hello World 1"); } - SECTION("Invalid identifiers") { - testPrinter("identifier", "Hello + World1"); + SECTION("Invalid objects") { + testPrinter("object", "Hello + World1"); } SECTION("Valid function calls") { testPrinter("number", "MyExtension::GetNumber()"); diff --git a/GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.cpp b/GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.cpp index f8b22f2f62..3d9b7420c8 100644 --- a/GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.cpp +++ b/GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.cpp @@ -331,7 +331,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction( if (find(behaviors.begin(), behaviors.end(), behaviorName) == behaviors.end()) { cout << "Error: bad behavior \"" << behaviorName - << "\" requested for object \'" << objectName + << "\" requested for object \'" << objectName << "\" (action: " << instrInfos.GetFullName() << ")." << endl; } else { actionCode += "for(std::size_t i = 0;i < " + ManObjListName(objectName) + @@ -356,50 +356,6 @@ gd::String EventsCodeGenerator::GenerateParameterCodes( // Code only parameter type if (metadata.type == "currentScene" || metadata.type == "objectsContext") { argOutput += "*runtimeContext->scene"; - } - // Code only parameter type - else if (metadata.type == "objectList") { - std::vector realObjects = ExpandObjectsName(parameter, context); - - argOutput += "runtimeContext->ClearObjectListsMap()"; - for (std::size_t i = 0; i < realObjects.size(); ++i) { - context.ObjectsListNeeded(realObjects[i]); - argOutput += ".AddObjectListToMap(\"" + ConvertToString(realObjects[i]) + - "\", " + ManObjListName(realObjects[i]) + ")"; - } - argOutput += ".ReturnObjectListsMap()"; - } - // Code only parameter type - else if (metadata.type == "objectListWithoutPicking") { - std::vector realObjects = ExpandObjectsName(parameter, context); - - argOutput += "runtimeContext->ClearObjectListsMap()"; - for (std::size_t i = 0; i < realObjects.size(); ++i) { - context.EmptyObjectsListNeeded(realObjects[i]); - argOutput += ".AddObjectListToMap(\"" + ConvertToString(realObjects[i]) + - "\", " + ManObjListName(realObjects[i]) + ")"; - } - argOutput += ".ReturnObjectListsMap()"; - } - // Code only parameter type - else if (metadata.type == "objectPtr") { - std::vector realObjects = ExpandObjectsName(parameter, context); - - if (find(realObjects.begin(), - realObjects.end(), - context.GetCurrentObject()) != realObjects.end() && - !context.GetCurrentObject().empty()) { - // If object currently used by instruction is available, use it directly. - argOutput += ManObjListName(context.GetCurrentObject()) + "[i]"; - } else { - for (std::size_t i = 0; i < realObjects.size(); ++i) { - context.ObjectsListNeeded(realObjects[i]); - argOutput += "(!" + ManObjListName(realObjects[i]) + ".empty() ? " + - ManObjListName(realObjects[i]) + "[0] : "; - } - argOutput += "NULL"; - for (std::size_t i = 0; i < realObjects.size(); ++i) argOutput += ")"; - } } else { argOutput += gd::EventsCodeGenerator::GenerateParameterCodes( parameter, @@ -412,11 +368,62 @@ gd::String EventsCodeGenerator::GenerateParameterCodes( return argOutput; } +gd::String EventsCodeGenerator::GenerateObject( + const gd::String& objectName, + const gd::String& type, + gd::EventsCodeGenerationContext& context) { + gd::String output; + if (type == "objectList") { + std::vector realObjects = + ExpandObjectsName(objectName, context); + + output += "runtimeContext->ClearObjectListsMap()"; + for (std::size_t i = 0; i < realObjects.size(); ++i) { + context.ObjectsListNeeded(realObjects[i]); + output += ".AddObjectListToMap(\"" + ConvertToString(realObjects[i]) + + "\", " + ManObjListName(realObjects[i]) + ")"; + } + output += ".ReturnObjectListsMap()"; + } else if (type == "objectListWithoutPicking") { + std::vector realObjects = + ExpandObjectsName(objectName, context); + + output += "runtimeContext->ClearObjectListsMap()"; + for (std::size_t i = 0; i < realObjects.size(); ++i) { + context.EmptyObjectsListNeeded(realObjects[i]); + output += ".AddObjectListToMap(\"" + ConvertToString(realObjects[i]) + + "\", " + ManObjListName(realObjects[i]) + ")"; + } + output += ".ReturnObjectListsMap()"; + } else if (type == "objectPtr") { + std::vector realObjects = + ExpandObjectsName(objectName, context); + + if (find(realObjects.begin(), + realObjects.end(), + context.GetCurrentObject()) != realObjects.end() && + !context.GetCurrentObject().empty()) { + // If object currently used by instruction is available, use it directly. + output += ManObjListName(context.GetCurrentObject()) + "[i]"; + } else { + for (std::size_t i = 0; i < realObjects.size(); ++i) { + context.ObjectsListNeeded(realObjects[i]); + output += "(!" + ManObjListName(realObjects[i]) + ".empty() ? " + + ManObjListName(realObjects[i]) + "[0] : "; + } + output += GenerateBadObject(); + for (std::size_t i = 0; i < realObjects.size(); ++i) output += ")"; + } + } + + return output; +} + gd::String EventsCodeGenerator::GenerateGetVariable( - gd::String variableName, + const gd::String& variableName, const VariableScope& scope, gd::EventsCodeGenerationContext& context, - gd::String objectName) { + const gd::String& objectName) { gd::String output; const gd::VariablesContainer* variables = NULL; if (scope == LAYOUT_VARIABLE) { diff --git a/GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.h b/GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.h index 87d3c45697..d20f35e9b8 100644 --- a/GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.h +++ b/GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.h @@ -130,10 +130,10 @@ class GD_API EventsCodeGenerator : public gd::EventsCodeGenerator { gd::EventsCodeGenerationContext& context); virtual gd::String GenerateGetVariable( - gd::String variableName, + const gd::String& variableName, const VariableScope& scope, gd::EventsCodeGenerationContext& context, - gd::String objectName); + const gd::String& objectName); virtual gd::String GenerateVariableAccessor(gd::String childName) { return ".GetChild(" + ConvertToStringExplicit(childName) + ")"; @@ -148,6 +148,12 @@ class GD_API EventsCodeGenerator : public gd::EventsCodeGenerator { return "runtimeContext->GetGameVariables().GetBadVariable()"; } + virtual gd::String GenerateBadObject() { return "NULL"; } + + virtual gd::String GenerateObject(const gd::String& objectName, + const gd::String& type, + gd::EventsCodeGenerationContext& context); + /** * \brief Construct a code generator for the specified project and layout. */ diff --git a/GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.cpp b/GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.cpp index 4288ac5eb4..a15592e902 100644 --- a/GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.cpp +++ b/GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.cpp @@ -155,7 +155,8 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionParameterDeclarationsList( const vector& parameters) { gd::String declaration = "runtimeScene"; for (const auto& parameter : parameters) { - declaration += ", " + (parameter.GetName().empty() ? "_" : parameter.GetName()); + declaration += + ", " + (parameter.GetName().empty() ? "_" : parameter.GetName()); } declaration += ", parentEventsFunctionContext"; @@ -454,7 +455,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition( if (find(behaviors.begin(), behaviors.end(), behaviorName) == behaviors.end()) { cout << "Error: bad behavior \"" << behaviorName - << "\" requested for object \'" << objectName + << "\" requested for object \'" << objectName << "\" (condition: " << instrInfos.GetFullName() << ")." << endl; } else { conditionCode += @@ -578,7 +579,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction( if (find(behaviors.begin(), behaviors.end(), behaviorName) == behaviors.end()) { cout << "Error: bad behavior \"" << behaviorName - << "\" requested for object \'" << objectName + << "\" requested for object \'" << objectName << "\" (action: " << instrInfos.GetFullName() << ")." << endl; } else { actionCode += @@ -739,6 +740,38 @@ gd::String EventsCodeGenerator::GenerateParameterCodes( const gd::String& previousParameter, std::vector >* supplementaryParametersTypes) { + gd::String argOutput; + + // Code only parameter type + if (metadata.type == "currentScene") { + argOutput = "runtimeScene"; + } + // Code only parameter type + else if (metadata.type == "objectsContext") { + argOutput = + "(typeof eventsFunctionContext !== 'undefined' ? eventsFunctionContext " + ": runtimeScene)"; + } + // Code only parameter type + else if (metadata.type == "eventsFunctionContext") { + argOutput = + "(typeof eventsFunctionContext !== 'undefined' ? eventsFunctionContext " + ": undefined)"; + } else + return gd::EventsCodeGenerator::GenerateParameterCodes( + parameter, + metadata, + context, + previousParameter, + supplementaryParametersTypes); + + return argOutput; +} + +gd::String EventsCodeGenerator::GenerateObject( + const gd::String& objectName, + const gd::String& type, + gd::EventsCodeGenerationContext& context) { //*Optimization:* when a function need objects, it receive a map of //(references to) objects lists. We statically declare and construct them to // avoid re-creating them at runtime. Arrays are passed as reference in JS and @@ -763,78 +796,52 @@ gd::String EventsCodeGenerator::GenerateParameterCodes( return objectsMapName; }; - gd::String argOutput; - - // Code only parameter type - if (metadata.type == "currentScene") { - argOutput = "runtimeScene"; - } - // Code only parameter type - else if (metadata.type == "objectsContext") { - argOutput = - "(typeof eventsFunctionContext !== 'undefined' ? eventsFunctionContext " - ": runtimeScene)"; - } - // Code only parameter type - else if (metadata.type == "eventsFunctionContext") { - argOutput = - "(typeof eventsFunctionContext !== 'undefined' ? eventsFunctionContext " - ": undefined)"; - } - // Code only parameter type - else if (metadata.type == "objectList") { - std::vector realObjects = ExpandObjectsName(parameter, context); + gd::String output; + if (type == "objectList") { + std::vector realObjects = + ExpandObjectsName(objectName, context); for (auto& objectName : realObjects) context.ObjectsListNeeded(objectName); gd::String objectsMapName = declareMapOfObjects(realObjects, context); - argOutput = objectsMapName; - } - // Code only parameter type - else if (metadata.type == "objectListWithoutPicking") { - std::vector realObjects = ExpandObjectsName(parameter, context); + output = objectsMapName; + } else if (type == "objectListWithoutPicking") { + std::vector realObjects = + ExpandObjectsName(objectName, context); for (auto& objectName : realObjects) context.EmptyObjectsListNeeded(objectName); gd::String objectsMapName = declareMapOfObjects(realObjects, context); - argOutput = objectsMapName; - } - // Code only parameter type - else if (metadata.type == "objectPtr") { - std::vector realObjects = ExpandObjectsName(parameter, context); + output = objectsMapName; + } else if (type == "objectPtr") { + std::vector realObjects = + ExpandObjectsName(objectName, context); if (find(realObjects.begin(), realObjects.end(), context.GetCurrentObject()) != realObjects.end() && !context.GetCurrentObject().empty()) { // If object currently used by instruction is available, use it directly. - argOutput = - GetObjectListName(context.GetCurrentObject(), context) + "[i]"; + output = GetObjectListName(context.GetCurrentObject(), context) + "[i]"; } else { for (std::size_t i = 0; i < realObjects.size(); ++i) { context.ObjectsListNeeded(realObjects[i]); - argOutput += "(" + GetObjectListName(realObjects[i], context) + - ".length !== 0 ? " + - GetObjectListName(realObjects[i], context) + "[0] : "; + output += "(" + GetObjectListName(realObjects[i], context) + + ".length !== 0 ? " + + GetObjectListName(realObjects[i], context) + "[0] : "; } - argOutput += "null"; - for (std::size_t i = 0; i < realObjects.size(); ++i) argOutput += ")"; + output += GenerateBadObject(); + for (std::size_t i = 0; i < realObjects.size(); ++i) output += ")"; } - } else - return gd::EventsCodeGenerator::GenerateParameterCodes( - parameter, - metadata, - context, - previousParameter, - supplementaryParametersTypes); + } - return argOutput; + return output; } gd::String EventsCodeGenerator::GenerateGetVariable( - gd::String variableName, + const gd::String& variableName, const VariableScope& scope, gd::EventsCodeGenerationContext& context, - gd::String objectName) { + const gd::String& objectName) { gd::String output; const gd::VariablesContainer* variables = NULL; if (scope == LAYOUT_VARIABLE) { @@ -860,11 +867,10 @@ gd::String EventsCodeGenerator::GenerateGetVariable( // Generate the call to GetVariables() method. if (context.GetCurrentObject() == realObjects[i] && !context.GetCurrentObject().empty()) - output = GetObjectListName(realObjects[i], context) + - "[i].getVariables()"; + output = + GetObjectListName(realObjects[i], context) + "[i].getVariables()"; else - output = "((" + - GetObjectListName(realObjects[i], context) + + output = "((" + GetObjectListName(realObjects[i], context) + ".length === 0 ) ? " + output + " : " + GetObjectListName(realObjects[i], context) + "[0].getVariables())"; @@ -876,8 +882,7 @@ gd::String EventsCodeGenerator::GenerateGetVariable( variables = &GetLayout().GetObject(objectName).GetVariables(); else if (GetProject().HasObjectNamed( objectName)) // Then the global objects list. - variables = - &GetProject().GetObject(objectName).GetVariables(); + variables = &GetProject().GetObject(objectName).GetVariables(); } } diff --git a/GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.h b/GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.h index 16b7e8b0ea..17be8db761 100644 --- a/GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.h +++ b/GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.h @@ -213,10 +213,10 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator { gd::EventsCodeGenerationContext& context); virtual gd::String GenerateGetVariable( - gd::String variableName, + const gd::String& variableName, const VariableScope& scope, gd::EventsCodeGenerationContext& context, - gd::String objectName); + const gd::String& objectName); virtual gd::String GenerateVariableAccessor(gd::String childName) { return ".getChild(" + ConvertToStringExplicit(childName) + ")"; @@ -226,11 +226,17 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator { gd::String expressionCode) { return ".getChild(" + expressionCode + ")"; }; - + virtual gd::String GenerateBadVariable() { - return "gdjs.VariablesContainer.badVariable"; + return "gdjs.VariablesContainer.badVariable"; } + virtual gd::String GenerateBadObject() { return "null"; } + + virtual gd::String GenerateObject(const gd::String& objectName, + const gd::String& type, + gd::EventsCodeGenerationContext& context); + virtual gd::String GenerateNegatedPredicat(const gd::String& predicat) const { return "!(" + predicat + ")"; };