Compare commits

..

2 Commits

Author SHA1 Message Date
Florian Rival
ac97470140 Run autoformatting with the latest Prettier
cd newIDE/app && npm run format
cd newIDE/electron-app && npm run format
cd GDJS && npm run format
2022-05-21 14:00:46 +00:00
Florian Rival
bde4e9254d Update to latest Prettier 2022-05-21 11:20:19 +00:00
1148 changed files with 18412 additions and 47663 deletions

View File

@@ -10,11 +10,11 @@ assignees: ''
BEFORE opening a new feature request, please make sure that you:
- Understand the implications of your feature with the help of [the Forum](https://forum.gdevelop.io/c/gdevelop-general/feature-requests/35), OR
- Peer-reviewed it with other users on Discord,
- Consider commenting on the [Feature Request Forum](https://forum.gdevelop.io/c/gdevelop-general/feature-requests/35) if something is important for you
- Discussed it on the discord or the forum,
- There is not already a suggestion about it in the issues or in the roadmap: https://trello.com/b/qf0lM7k8/gdevelop-roadmap
- Consider commenting on the roadmap if something is important for you
AFTER opening the feature request, the issue will be closed by a maintainer (@4ian or someone else) and a card will be added in [the public roadmap](https://trello.com/b/qf0lM7k8/gdevelop-ideas-box) if it's relevant and does not exist yet :)
AFTER opening the feature request, the issue will be closed by a maintainer (@4ian or someone else) and a card will be added in the roadmap if it's relevant and does not exist yet :)
## Description

View File

@@ -4,14 +4,14 @@ contact_links:
url: https://discord.gg/rjdYHvj
about: Discuss on the forum or on the Discord to get help creating a game or identifying a bug.
- name: GDevelop Forums
url: https://forum.gdevelop.io
url: https://forum.gdevelop-app.com
about: You can also discuss game creation, new feature requests and bugs on the forum.
- name: GDevelop Roadmap
url: https://trello.com/b/qf0lM7k8/gdevelop-roadmap
about: Look at the roadmap and vote on features that you want to see in GDevelop.
- name: Submit a new game example that you created
url: https://github.com/GDevelopApp/GDevelop-examples/issues/new/choose
about: You can submit a game that you made to be added to examples in the "GDevelop-examples" repository
about: You can submit a game that you made to be added to examples in the "GDevelop-examples" repository
- name: Submit a new game extension that you created
url: https://github.com/4ian/GDevelop-extensions/issues/new/choose
about: You can submit an extension that you made in the "GDevelop-extensions" repository
about: You can submit an extension that you made in the "GDevelop-extensions" repository

View File

@@ -12,7 +12,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*Scroll down to '\\.\\.\\.\\.'.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop.io/), the Discord chat or [read the documentation](https://wiki.gdevelop.io/gdevelop5/start) to learn more about GDevelop. Thanks!"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](https://wiki.gdevelop.io/gdevelop5/start) to learn more about GDevelop. Thanks!"
- name: Autoclose known beta 105 web-app update bug
uses: arkon/issue-closer-action@v1.1
with:

View File

@@ -23,9 +23,8 @@ vector<gd::String> CommentEvent::GetAllSearchableStrings() const {
bool CommentEvent::ReplaceAllSearchableStrings(
std::vector<gd::String> newSearchableString) {
if (newSearchableString[0] == com1) return false;
SetComment(newSearchableString[0]);
return true;
return newSearchableString[0] == com1;
}
void CommentEvent::SerializeTo(SerializerElement &element) const {

View File

@@ -53,10 +53,8 @@ void ForEachChildVariableEvent::SerializeTo(SerializerElement& element) const {
conditions, element.AddChild("conditions"));
gd::EventsListSerialization::SerializeInstructionsTo(
actions, element.AddChild("actions"));
if (!events.IsEmpty())
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
}
void ForEachChildVariableEvent::UnserializeFrom(gd::Project& project,
@@ -68,12 +66,8 @@ void ForEachChildVariableEvent::UnserializeFrom(gd::Project& project,
project, conditions, element.GetChild("conditions", 0, "Conditions"));
gd::EventsListSerialization::UnserializeInstructionsFrom(
project, actions, element.GetChild("actions", 0, "Actions"));
events.Clear();
if (element.HasChild("events", "Events")) {
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
} // namespace gd

View File

@@ -74,10 +74,8 @@ void ForEachEvent::SerializeTo(SerializerElement& element) const {
conditions, element.AddChild("conditions"));
gd::EventsListSerialization::SerializeInstructionsTo(
actions, element.AddChild("actions"));
if (!events.IsEmpty())
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
}
void ForEachEvent::UnserializeFrom(gd::Project& project,
@@ -88,12 +86,8 @@ void ForEachEvent::UnserializeFrom(gd::Project& project,
project, conditions, element.GetChild("conditions", 0, "Conditions"));
gd::EventsListSerialization::UnserializeInstructionsFrom(
project, actions, element.GetChild("actions", 0, "Actions"));
events.Clear();
if (element.HasChild("events", "Events")) {
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
} // namespace gd

View File

@@ -29,9 +29,8 @@ vector<gd::String> GroupEvent::GetAllSearchableStrings() const {
bool GroupEvent::ReplaceAllSearchableStrings(
std::vector<gd::String> newSearchableString) {
if (newSearchableString[0] == name) return false;
SetName(newSearchableString[0]);
return true;
return newSearchableString[0] == name;
}
void GroupEvent::SerializeTo(SerializerElement& element) const {

View File

@@ -75,10 +75,8 @@ void RepeatEvent::SerializeTo(SerializerElement& element) const {
conditions, element.AddChild("conditions"));
gd::EventsListSerialization::SerializeInstructionsTo(
actions, element.AddChild("actions"));
if (!events.IsEmpty())
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
}
void RepeatEvent::UnserializeFrom(gd::Project& project,
@@ -91,12 +89,8 @@ void RepeatEvent::UnserializeFrom(gd::Project& project,
project, conditions, element.GetChild("conditions", 0, "Conditions"));
gd::EventsListSerialization::UnserializeInstructionsFrom(
project, actions, element.GetChild("actions", 0, "Actions"));
events.Clear();
if (element.HasChild("events", "Events")) {
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
} // namespace gd

View File

@@ -53,10 +53,8 @@ void StandardEvent::SerializeTo(SerializerElement& element) const {
conditions, element.AddChild("conditions"));
gd::EventsListSerialization::SerializeInstructionsTo(
actions, element.AddChild("actions"));
if (!events.IsEmpty())
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
}
void StandardEvent::UnserializeFrom(gd::Project& project,

View File

@@ -52,10 +52,8 @@ void WhileEvent::SerializeTo(SerializerElement& element) const {
conditions, element.AddChild("conditions"));
gd::EventsListSerialization::SerializeInstructionsTo(
actions, element.AddChild("actions"));
if (!events.IsEmpty())
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
}
void WhileEvent::UnserializeFrom(gd::Project& project,
@@ -70,12 +68,8 @@ void WhileEvent::UnserializeFrom(gd::Project& project,
project, conditions, element.GetChild("conditions", 0, "Conditions"));
gd::EventsListSerialization::UnserializeInstructionsFrom(
project, actions, element.GetChild("actions", 0, "Actions"));
events.Clear();
if (element.HasChild("events", "Events")) {
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
} // namespace gd

View File

@@ -468,14 +468,6 @@ gd::String EventsCodeGenerator::GenerateActionCode(
action, *this, context);
}
// Get the correct function name depending on whether it should be async or
// not.
const gd::String& functionCallName =
instrInfos.IsAsync() &&
(!instrInfos.IsOptionallyAsync() || action.IsAwaited())
? instrInfos.codeExtraInformation.asyncFunctionCallName
: instrInfos.codeExtraInformation.functionCallName;
// Be sure there is no lack of parameter.
while (action.GetParameters().size() < instrInfos.parameters.size()) {
vector<gd::Expression> parameters = action.GetParameters();
@@ -530,7 +522,6 @@ gd::String EventsCodeGenerator::GenerateActionCode(
action.GetParameters(), instrInfos.parameters, context);
actionCode += GenerateObjectAction(realObjects[i],
objInfo,
functionCallName,
arguments,
instrInfos,
context,
@@ -565,7 +556,6 @@ gd::String EventsCodeGenerator::GenerateActionCode(
GenerateBehaviorAction(realObjects[i],
action.GetParameter(1).GetPlainString(),
autoInfo,
functionCallName,
arguments,
instrInfos,
context,
@@ -577,11 +567,8 @@ gd::String EventsCodeGenerator::GenerateActionCode(
} else {
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(), instrInfos.parameters, context);
actionCode += GenerateFreeAction(functionCallName,
arguments,
instrInfos,
context,
optionalAsyncCallbackName);
actionCode +=
GenerateFreeAction(arguments, instrInfos, context, optionalAsyncCallbackName);
}
return actionCode;
@@ -671,7 +658,7 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
}
gd::String EventsCodeGenerator::GenerateParameterCodes(
const gd::Expression& parameter,
const gd::String& parameter,
const gd::ParameterMetadata& metadata,
gd::EventsCodeGenerationContext& context,
const gd::String& lastObjectName,
@@ -681,21 +668,19 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
if (ParameterMetadata::IsExpression("number", metadata.type)) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "number", parameter, lastObjectName);
*this, context, "number", parameter);
} else if (ParameterMetadata::IsExpression("string", metadata.type)) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "string", parameter, lastObjectName);
*this, context, "string", parameter);
} 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.GetPlainString(), metadata.type, context);
argOutput = GenerateObject(parameter, metadata.type, context);
} else if (metadata.type == "relationalOperator") {
auto parameterString = parameter.GetPlainString();
argOutput += parameterString == "=" ? "==" : parameterString;
argOutput += parameter == "=" ? "==" : parameter;
if (argOutput != "==" && argOutput != "<" && argOutput != ">" &&
argOutput != "<=" && argOutput != ">=" && argOutput != "!=") {
cout << "Warning: Bad relational operator: Set to == by default." << endl;
@@ -704,7 +689,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput = "\"" + argOutput + "\"";
} else if (metadata.type == "operator") {
argOutput += parameter.GetPlainString();
argOutput += parameter;
if (argOutput != "=" && argOutput != "+" && argOutput != "-" &&
argOutput != "/" && argOutput != "*") {
cout << "Warning: Bad operator: Set to = by default." << endl;
@@ -713,9 +698,9 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput = "\"" + argOutput + "\"";
} else if (ParameterMetadata::IsBehavior(metadata.type)) {
argOutput = GenerateGetBehaviorNameCode(parameter.GetPlainString());
argOutput = GenerateGetBehaviorNameCode(parameter);
} else if (metadata.type == "key") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
argOutput = "\"" + ConvertToString(parameter) + "\"";
} else if (metadata.type == "audioResource" ||
metadata.type == "bitmapFontResource" ||
metadata.type == "fontResource" ||
@@ -725,20 +710,16 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
// Deprecated, old parameter names:
metadata.type == "password" || metadata.type == "musicfile" ||
metadata.type == "soundfile" || metadata.type == "police") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
argOutput = "\"" + ConvertToString(parameter) + "\"";
} else if (metadata.type == "mouse") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
argOutput = "\"" + ConvertToString(parameter) + "\"";
} else if (metadata.type == "yesorno") {
auto parameterString = parameter.GetPlainString();
argOutput += (parameterString == "yes" || parameterString == "oui")
? GenerateTrue()
: GenerateFalse();
argOutput += (parameter == "yes" || parameter == "oui") ? GenerateTrue()
: GenerateFalse();
} else if (metadata.type == "trueorfalse") {
auto parameterString = parameter.GetPlainString();
// This is duplicated in AdvancedExtension.cpp for GDJS
argOutput += (parameterString == "True" || parameterString == "Vrai")
? GenerateTrue()
: GenerateFalse();
argOutput += (parameter == "True" || parameter == "Vrai") ? GenerateTrue()
: GenerateFalse();
}
// Code only parameter type
else if (metadata.type == "inlineCode") {
@@ -757,7 +738,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
if (!metadata.type.empty())
cout << "Warning: Unknown type of parameter \"" << metadata.type
<< "\"." << std::endl;
argOutput += "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
argOutput += "\"" + ConvertToString(parameter) + "\"";
}
}
@@ -777,7 +758,7 @@ vector<gd::String> EventsCodeGenerator::GenerateParametersCodes(
parametersInfo,
[this, &context, &supplementaryParametersTypes, &arguments](
const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
const gd::String& parameterValue,
const gd::String& lastObjectName) {
gd::String argOutput =
GenerateParameterCodes(parameterValue,
@@ -1098,7 +1079,6 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
}
gd::String EventsCodeGenerator::GenerateFreeAction(
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -1112,21 +1092,21 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
call = GenerateOperatorCall(
instrInfos,
arguments,
functionCallName,
instrInfos.codeExtraInformation.functionCallName,
instrInfos.codeExtraInformation.optionalAssociatedInstruction);
else if (instrInfos.codeExtraInformation.accessType ==
gd::InstructionMetadata::ExtraInformation::Mutators)
call =
GenerateMutatorCall(instrInfos,
arguments,
functionCallName);
instrInfos.codeExtraInformation.functionCallName);
else
call = GenerateCompoundOperatorCall(
instrInfos,
arguments,
functionCallName);
instrInfos.codeExtraInformation.functionCallName);
} else {
call = functionCallName + "(" +
call = instrInfos.codeExtraInformation.functionCallName + "(" +
GenerateArgumentsList(arguments) + ")";
}
@@ -1140,7 +1120,6 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
gd::String EventsCodeGenerator::GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -1154,25 +1133,27 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
call = GenerateOperatorCall(
instrInfos,
arguments,
functionCallName,
instrInfos.codeExtraInformation.functionCallName,
instrInfos.codeExtraInformation.optionalAssociatedInstruction,
2);
else
call = GenerateCompoundOperatorCall(
instrInfos, arguments, functionCallName, 2);
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName,
2);
return "For each picked object \"" + objectName + "\", call " + call +
".\n";
} else {
gd::String argumentsStr = GenerateArgumentsList(arguments, 1);
call = functionCallName + "(" + argumentsStr + ")";
call = instrInfos.codeExtraInformation.functionCallName + "(" +
argumentsStr + ")";
return "For each picked object \"" + objectName + "\", call " + call + "(" +
argumentsStr + ")" +
(optionalAsyncCallbackName.empty()
? ""
: (", then call" + optionalAsyncCallbackName)) +
(optionalAsyncCallbackName.empty() ? "" : (", then call" + optionalAsyncCallbackName)) +
".\n";
}
}
@@ -1181,7 +1162,6 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
const gd::String& objectName,
const gd::String& behaviorName,
const gd::BehaviorMetadata& autoInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -1195,28 +1175,26 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
call = GenerateOperatorCall(
instrInfos,
arguments,
functionCallName,
instrInfos.codeExtraInformation.functionCallName,
instrInfos.codeExtraInformation.optionalAssociatedInstruction,
2);
else
call = GenerateCompoundOperatorCall(
instrInfos,
arguments,
functionCallName,
instrInfos.codeExtraInformation.functionCallName,
2);
return "For each picked object \"" + objectName + "\", call " + call +
" for behavior \"" + behaviorName + "\".\n";
} else {
gd::String argumentsStr = GenerateArgumentsList(arguments, 2);
call = functionCallName + "(" +
call = instrInfos.codeExtraInformation.functionCallName + "(" +
argumentsStr + ")";
return "For each picked object \"" + objectName + "\", call " + call + "(" +
argumentsStr + ")" + " for behavior \"" + behaviorName + "\"" +
(optionalAsyncCallbackName.empty()
? ""
: (", then call" + optionalAsyncCallbackName)) +
(optionalAsyncCallbackName.empty() ? "" : (", then call" + optionalAsyncCallbackName)) +
".\n";
}
}
@@ -1265,7 +1243,7 @@ gd::String EventsCodeGenerator::GenerateArgumentsList(
return argumentsStr;
}
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
EventsCodeGenerator::EventsCodeGenerator(gd::Project& project_,
const gd::Layout& layout,
const gd::Platform& platform_)
: platform(platform_),
@@ -1282,7 +1260,7 @@ EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
EventsCodeGenerator::EventsCodeGenerator(
const gd::Platform& platform_,
const gd::ObjectsContainer& globalObjectsAndGroups_,
gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& objectsAndGroups_)
: platform(platform_),
globalObjectsAndGroups(globalObjectsAndGroups_),

View File

@@ -48,7 +48,7 @@ class GD_CORE_API EventsCodeGenerator {
* \brief Construct a code generator for the specified
* platform/project/layout.
*/
EventsCodeGenerator(const gd::Project& project_,
EventsCodeGenerator(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,
const gd::ObjectsContainer& globalObjectsAndGroups_,
gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& objectsAndGroups_);
virtual ~EventsCodeGenerator(){};
@@ -154,10 +154,9 @@ class GD_CORE_API EventsCodeGenerator {
* \param context Context used for generation
* \return Code
*/
gd::String GenerateActionCode(
gd::Instruction& action,
EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
gd::String GenerateActionCode(gd::Instruction& action,
EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
struct CallbackDescriptor {
CallbackDescriptor(const gd::String functionName_,
@@ -175,8 +174,7 @@ class GD_CORE_API EventsCodeGenerator {
*/
const gd::String argumentsList;
/**
* A set of all objects that need to be backed up to be passed to the
* callback code.
* A set of all objects that need to be backed up to be passed to the callback code.
*/
const std::set<gd::String> requiredObjects;
};
@@ -329,7 +327,7 @@ class GD_CORE_API EventsCodeGenerator {
/**
* \brief Get the global objects/groups used for code generation.
*/
const gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
return globalObjectsAndGroups;
}
@@ -350,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.
*/
const gd::Project& GetProject() const { return *project; }
gd::Project& GetProject() const { return *project; }
/**
* \brief Get the layout the code is being generated for.
@@ -509,17 +507,17 @@ class GD_CORE_API EventsCodeGenerator {
* - currentScene: Reference to the current runtime scene.
* - objectList : a map containing lists of objects which are specified by the
object name in another parameter.
* - objectListOrEmptyIfJustDeclared : Same as `objectList` but do not pick
object if they are not already picked.
* - objectPtr: Return a reference to the object specified by the object name
in another parameter. Example:
* - objectListOrEmptyIfJustDeclared : Same as `objectList` but do not pick object if
they are not already picked.
* - objectPtr: Return a reference to the object specified by the object name in
another parameter. Example:
* \code
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Target object"))
* \endcode
*/
virtual gd::String GenerateParameterCodes(
const gd::Expression& parameter,
const gd::String& parameter,
const gd::ParameterMetadata& metadata,
gd::EventsCodeGenerationContext& context,
const gd::String& lastObjectName,
@@ -702,7 +700,6 @@ class GD_CORE_API EventsCodeGenerator {
gd::EventsCodeGenerationContext& context);
virtual gd::String GenerateFreeAction(
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -711,7 +708,6 @@ class GD_CORE_API EventsCodeGenerator {
virtual gd::String GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -721,7 +717,6 @@ class GD_CORE_API EventsCodeGenerator {
const gd::String& objectName,
const gd::String& behaviorName,
const gd::BehaviorMetadata& autoInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -775,13 +770,13 @@ class GD_CORE_API EventsCodeGenerator {
const gd::Platform& platform; ///< The platform being used.
const gd::ObjectsContainer& globalObjectsAndGroups;
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.
const gd::Project* project; ///< The project being used.
const gd::Layout* scene; ///< The scene being generated.
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.
bool compilationForRuntime; ///< Is set to true if the code generation is

View File

@@ -25,38 +25,36 @@
#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& rootType,
const gd::Expression& expression,
const gd::String& rootObjectName) {
ExpressionCodeGenerator generator(rootType, rootObjectName, codeGenerator, 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);
auto node = expression.GetRootNode();
auto node = parser.ParseExpression(type, expression, objectName);
if (!node) {
std::cout << "Error: error while parsing: \"" << expression.GetPlainString()
<< "\" (" << rootType << ")" << std::endl;
std::cout << "Error: error while parsing: \"" << expression << "\" ("
<< type << ")" << std::endl;
return generator.GenerateDefaultValue(rootType);
return generator.GenerateDefaultValue(type);
}
gd::ExpressionValidator validator(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType);
gd::ExpressionValidator validator;
node->Visit(validator);
if (!validator.GetErrors().empty()) {
std::cout << "Error: \"" << validator.GetErrors()[0]->GetMessage()
<< "\" in: \"" << expression.GetPlainString() << "\" ("
<< rootType << ")" << std::endl;
<< "\" in: \"" << expression << "\" (" << type << ")"
<< std::endl;
return generator.GenerateDefaultValue(rootType);
return generator.GenerateDefaultValue(type);
}
node->Visit(generator);
@@ -99,24 +97,15 @@ 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 =
type == "globalvar"
node.type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: ((type == "scenevar")
: ((node.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, objectName);
node.name, scope, context, node.objectName);
if (node.child) node.child->Visit(*this);
}
@@ -128,7 +117,7 @@ void ExpressionCodeGenerator::OnVisitVariableAccessorNode(
void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
ExpressionCodeGenerator generator("string", "", codeGenerator, context);
ExpressionCodeGenerator generator(codeGenerator, context);
node.expression->Visit(generator);
output +=
codeGenerator.GenerateVariableBracketAccessor(generator.GetOutput());
@@ -136,79 +125,39 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
}
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType,
node);
if (gd::ParameterMetadata::IsObject(type)) {
if (gd::ParameterMetadata::IsObject(node.type)) {
output +=
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()) {
codeGenerator.GenerateObject(node.identifierName, node.type, context);
} else {
output += "/* Error during generation, unrecognized identifier type: " +
codeGenerator.ConvertToString(type) + " with value " +
codeGenerator.ConvertToString(node.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) {
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)) {
if (gd::MetadataProvider::IsBadExpressionMetadata(node.expressionMetadata)) {
output += "/* Error during generation, function not found: " +
codeGenerator.ConvertToString(node.functionName) + " */ " +
GenerateDefaultValue(type);
GenerateDefaultValue(node.type);
return;
}
if (!node.objectName.empty()) {
if (!node.behaviorName.empty()) {
output += GenerateBehaviorFunctionCode(type,
output += GenerateBehaviorFunctionCode(node.type,
node.objectName,
node.behaviorName,
node.parameters,
metadata);
node.expressionMetadata);
} else {
output += GenerateObjectFunctionCode(
type, node.objectName, node.parameters, metadata);
node.type, node.objectName, node.parameters, node.expressionMetadata);
}
} else {
output +=
GenerateFreeFunctionCode(node.parameters, metadata);
GenerateFreeFunctionCode(node.parameters, node.expressionMetadata);
}
}
@@ -356,21 +305,18 @@ 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;
auto node = parser.ParseExpression(parameterMetadata.GetDefaultValue());
ExpressionParser2 parser(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups());
auto node = parser.ParseExpression(parameterMetadata.GetType(),
parameterMetadata.GetDefaultValue());
node->Visit(generator);
parametersCode += generator.GetOutput();
@@ -428,22 +374,12 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
}
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType,
node);
output += GenerateDefaultValue(type);
output += GenerateDefaultValue(node.type);
}
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(
ObjectFunctionNameNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType,
node);
output += GenerateDefaultValue(type);
output += GenerateDefaultValue(node.type);
}
} // namespace gd

View File

@@ -9,6 +9,7 @@
#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"
@@ -34,11 +35,9 @@ namespace gd {
*/
class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
public:
ExpressionCodeGenerator(const gd::String &rootType_,
const gd::String &rootObjectName_,
EventsCodeGenerator& codeGenerator_,
ExpressionCodeGenerator(EventsCodeGenerator& codeGenerator_,
EventsCodeGenerationContext& context_)
: rootType(rootType_), rootObjectName(rootObjectName_), codeGenerator(codeGenerator_), context(context_){};
: codeGenerator(codeGenerator_), context(context_){};
virtual ~ExpressionCodeGenerator(){};
/**
@@ -58,7 +57,7 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
static gd::String GenerateExpressionCode(EventsCodeGenerator& codeGenerator,
EventsCodeGenerationContext& context,
const gd::String& type,
const gd::Expression& expression,
const gd::String& expression,
const gd::String& objectName = "");
const gd::String& GetOutput() { return output; };
@@ -104,8 +103,6 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
gd::String output;
EventsCodeGenerator& codeGenerator;
EventsCodeGenerationContext& context;
const gd::String rootType;
const gd::String rootObjectName;
};
} // namespace gd

View File

@@ -75,8 +75,7 @@ void BaseEvent::PreprocessAsyncActions(const gd::Platform& platform) {
const auto& action = actionsList->at(aId);
const gd::InstructionMetadata& actionMetadata =
gd::MetadataProvider::GetActionMetadata(platform, action.GetType());
if (actionMetadata.IsAsync() &&
(!actionMetadata.IsOptionallyAsync() || action.IsAwaited())) {
if (actionMetadata.IsAsync()) {
gd::InstructionsList remainingActions;
remainingActions.InsertInstructions(
*actionsList, aId + 1, actionsList->size() - 1);

View File

@@ -1,41 +0,0 @@
/*
* 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

View File

@@ -6,15 +6,7 @@
#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 {
@@ -32,49 +24,32 @@ class GD_CORE_API Expression {
/**
* \brief Construct an empty expression
*/
Expression();
Expression(){};
/**
* \brief Construct an expression from a string
*/
Expression(gd::String plainString_);
Expression(gd::String plainString_) : plainString(plainString_){};
/**
* \brief Construct an expression from a const char *
*/
Expression(const char* plainString_);
/**
* \brief Copy construct an expression.
*/
Expression(const Expression& copy);
/**
* \brief Expression affectation overriding.
*/
Expression& operator=(const Expression& expression);
Expression(const char* plainString_) : plainString(plainString_){};
/**
* \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

View File

@@ -72,22 +72,6 @@ class GD_CORE_API Instruction {
*/
void SetInverted(bool inverted_) { inverted = inverted_; }
/**
* \brief Return true if the async instruction should be awaited.
* This is not relevant if the instruction is not optionally asynchronous.
*
* \return true if the instruction is to be awaited
*/
bool IsAwaited() const { return awaitAsync; }
/**
* \brief Set if the async instruction is to be awaited or not.
* This is not relevant if the instruction is not optionally asynchronous.
*
* \param inverted true if the instruction must be awaited
*/
void SetAwaited(bool awaited) { awaitAsync = awaited; }
/**
* \brief Return the number of parameters of the instruction.
*/
@@ -155,9 +139,7 @@ class GD_CORE_API Instruction {
* Useful to get reference to the original instruction in memory during code
* generation, to ensure stable unique identifiers.
*/
std::weak_ptr<Instruction> GetOriginalInstruction() {
return originalInstruction;
};
std::weak_ptr<Instruction> GetOriginalInstruction() { return originalInstruction; };
friend std::shared_ptr<Instruction> CloneRememberingOriginalElement(
std::shared_ptr<Instruction> instruction);
@@ -166,9 +148,6 @@ class GD_CORE_API Instruction {
gd::String type; ///< Instruction type
bool inverted; ///< True if the instruction if inverted. Only applicable for
///< instruction used as conditions by events
bool awaitAsync =
false; ///< Tells the code generator whether the optionally asynchronous
///< instruction should be generated as asynchronous (awaited) or not.
mutable std::vector<gd::Expression>
parameters; ///< Vector containing the parameters
gd::InstructionsList subInstructions; ///< Sub instructions, if applicable.

View File

@@ -26,16 +26,132 @@ namespace gd {
gd::String ExpressionParser2::NAMESPACE_SEPARATOR = "::";
ExpressionParser2::ExpressionParser2()
ExpressionParser2::ExpressionParser2(
const gd::Platform& platform_,
const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_)
: expression(""),
currentPosition(0) {}
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>();
}
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 =

View File

@@ -40,7 +40,9 @@ namespace gd {
*/
class GD_CORE_API ExpressionParser2 {
public:
ExpressionParser2();
ExpressionParser2(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_);
virtual ~ExpressionParser2(){};
/**
@@ -56,11 +58,13 @@ class GD_CORE_API ExpressionParser2 {
* \return The node representing the expression as a parsed tree.
*/
std::unique_ptr<ExpressionNode> ParseExpression(
const gd::String &expression_) {
const gd::String &type,
const gd::String &expression_,
const gd::String &objectName = "") {
expression = expression_;
currentPosition = 0;
return Start();
return Start(type, objectName);
}
/**
@@ -84,16 +88,18 @@ class GD_CORE_API ExpressionParser2 {
* Each method is a part of the grammar.
*/
///@{
std::unique_ptr<ExpressionNode> Start() {
std::unique_ptr<ExpressionNode> Start(const gd::String &type,
const gd::String &objectName = "") {
size_t expressionStartPosition = GetCurrentPosition();
auto expression = Expression();
auto expression = Expression(type, objectName);
const gd::String &inferredType = expression->type;
// Check for extra characters at the end of the expression
if (!IsEndReached()) {
auto op = gd::make_unique<OperatorNode>(' ');
auto op = gd::make_unique<OperatorNode>(inferredType, ' ');
op->leftHandSide = std::move(expression);
op->rightHandSide = ReadUntilEnd();
op->rightHandSide->parent = op.get();
op->rightHandSide = ReadUntilEnd("unknown");
op->rightHandSide->diagnostic = RaiseSyntaxError(
_("The expression has extra character at the end that should be "
@@ -107,49 +113,61 @@ class GD_CORE_API ExpressionParser2 {
return expression;
}
std::unique_ptr<ExpressionNode> Expression() {
std::unique_ptr<ExpressionNode> Expression(
const gd::String &type, const gd::String &objectName = "") {
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> leftHandSide = Term();
std::unique_ptr<ExpressionNode> leftHandSide = Term(type, objectName);
const gd::String &inferredType = leftHandSide->type;
SkipAllWhitespaces();
if (IsEndReached()) return leftHandSide;
if (CheckIfChar(IsExpressionEndingChar)) return leftHandSide;
if (CheckIfChar(IsExpressionOperator)) {
auto op = gd::make_unique<OperatorNode>(GetCurrentChar());
auto op = gd::make_unique<OperatorNode>(inferredType, GetCurrentChar());
op->leftHandSide = std::move(leftHandSide);
op->leftHandSide->parent = op.get();
op->diagnostic = ValidateOperator(GetCurrentChar());
op->diagnostic = ValidateOperator(inferredType, GetCurrentChar());
SkipChar();
op->rightHandSide = Expression();
op->rightHandSide->parent = op.get();
op->rightHandSide = Expression(inferredType, objectName);
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
return std::move(op);
}
leftHandSide->diagnostic = RaiseSyntaxError(
"More than one term was found. Verify that your expression is "
"properly written.");
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.");
}
auto op = gd::make_unique<OperatorNode>(' ');
auto op = gd::make_unique<OperatorNode>(inferredType, ' ');
op->leftHandSide = std::move(leftHandSide);
op->leftHandSide->parent = op.get();
op->rightHandSide = Expression();
op->rightHandSide->parent = op.get();
op->rightHandSide = Expression(inferredType, objectName);
op->location =
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
return std::move(op);
}
std::unique_ptr<ExpressionNode> Term() {
std::unique_ptr<ExpressionNode> Term(const gd::String &type,
const gd::String &objectName) {
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> factor = Factor();
std::unique_ptr<ExpressionNode> factor = Factor(type, objectName);
const gd::String &inferredType = factor->type;
SkipAllWhitespaces();
@@ -157,13 +175,11 @@ 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>(GetCurrentChar());
auto op = gd::make_unique<OperatorNode>(inferredType, GetCurrentChar());
op->leftHandSide = std::move(factor);
op->leftHandSide->parent = op.get();
op->diagnostic = ValidateOperator(GetCurrentChar());
op->diagnostic = ValidateOperator(inferredType, GetCurrentChar());
SkipChar();
op->rightHandSide = Factor();
op->rightHandSide->parent = op.get();
op->rightHandSide = Factor(inferredType, objectName);
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
SkipAllWhitespaces();
@@ -174,35 +190,54 @@ class GD_CORE_API ExpressionParser2 {
return factor;
};
std::unique_ptr<ExpressionNode> Factor() {
std::unique_ptr<ExpressionNode> Factor(const gd::String &type,
const gd::String &objectName) {
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();
auto operatorOperand = Factor(type, objectName);
const gd::String &inferredType = operatorOperand->type;
auto unaryOperator = gd::make_unique<UnaryOperatorNode>(
unaryOperatorCharacter);
inferredType, unaryOperatorCharacter);
unaryOperator->diagnostic = ValidateUnaryOperator(
unaryOperatorCharacter, expressionStartPosition);
inferredType, 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();
std::unique_ptr<ExpressionNode> factor = SubExpression(type, objectName);
if (!CheckIfChar(IsClosingParenthesis)) {
factor->diagnostic =
@@ -212,20 +247,29 @@ class GD_CORE_API ExpressionParser2 {
SkipIfChar(IsClosingParenthesis);
return factor;
} else if (IsIdentifierAllowedChar()) {
return Identifier();
// 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);
}
}
std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace();
std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace(type);
factor->diagnostic = RaiseEmptyError(type, expressionStartPosition);
return factor;
}
std::unique_ptr<SubExpressionNode> SubExpression() {
std::unique_ptr<SubExpressionNode> SubExpression(
const gd::String &type, const gd::String &objectName) {
size_t expressionStartPosition = GetCurrentPosition();
auto expression = Expression();
auto expression = Expression(type, objectName);
const gd::String &inferredType = expression->type;
auto subExpression =
gd::make_unique<SubExpressionNode>(std::move(expression));
gd::make_unique<SubExpressionNode>(inferredType, std::move(expression));
subExpression->location =
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
@@ -233,7 +277,7 @@ class GD_CORE_API ExpressionParser2 {
};
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
Identifier() {
Identifier(const gd::String &type) {
auto identifierAndLocation = ReadIdentifierName();
gd::String name = identifierAndLocation.name;
auto nameLocation = identifierAndLocation.location;
@@ -260,28 +304,47 @@ class GD_CORE_API ExpressionParser2 {
if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
return FreeFunction(name, nameLocation, openingParenthesisLocation);
return FreeFunction(type, name, nameLocation, openingParenthesisLocation);
} else if (CheckIfChar(IsDot)) {
ExpressionParserLocation dotLocation = SkipChar();
SkipAllWhitespaces();
return ObjectFunctionOrBehaviorFunction(
name, nameLocation, dotLocation);
} else if (CheckIfChar(IsOpeningSquareBracket)) {
return Variable(name, nameLocation);
}
else {
auto identifier = gd::make_unique<IdentifierNode>(name);
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());
}
identifier->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
identifier->identifierNameLocation = identifier->location;
return std::move(identifier);
}
}
std::unique_ptr<VariableNode> Variable(const gd::String &name, gd::ExpressionParserLocation nameLocation) {
auto variable = gd::make_unique<VariableNode>(name);
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);
variable->child = VariableAccessorOrVariableBracketAccessor();
variable->child->parent = variable.get();
variable->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
@@ -296,8 +359,8 @@ class GD_CORE_API ExpressionParser2 {
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket)) {
SkipChar();
auto child = gd::make_unique<VariableBracketAccessorNode>(Expression());
child->expression->parent = child.get();
auto child = gd::make_unique<VariableBracketAccessorNode>(
Expression("number|string"));
if (!CheckIfChar(IsClosingSquareBracket)) {
child->diagnostic =
@@ -306,7 +369,6 @@ class GD_CORE_API ExpressionParser2 {
}
SkipIfChar(IsClosingSquareBracket);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
child->location =
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
@@ -319,7 +381,6 @@ 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 =
@@ -328,21 +389,40 @@ class GD_CORE_API ExpressionParser2 {
return std::move(child);
}
return std::move(gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>());
return std::move(
std::unique_ptr<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>(functionFullName);
auto parametersNode = Parameters(function.get());
function->parameters = std::move(parametersNode.parameters);
gd::make_unique<FunctionCallNode>(returnType,
std::move(parametersNode.parameters),
metadata,
functionFullName);
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());
@@ -354,15 +434,16 @@ class GD_CORE_API ExpressionParser2 {
return std::move(function);
}
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode>
ObjectFunctionOrBehaviorFunction(
const gd::String &parentIdentifier,
const ExpressionParserLocation &parentIdentifierLocation,
const ExpressionParserLocation &parentIdentifierDotLocation) {
auto childIdentifierAndLocation = ReadIdentifierName();
const gd::String &childIdentifierName = childIdentifierAndLocation.name;
const auto &childIdentifierNameLocation =
childIdentifierAndLocation.location;
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;
SkipAllWhitespaces();
@@ -370,68 +451,87 @@ class GD_CORE_API ExpressionParser2 {
ExpressionParserLocation namespaceSeparatorLocation =
SkipNamespaceSeparator();
SkipAllWhitespaces();
return BehaviorFunction(parentIdentifier,
childIdentifierName,
parentIdentifierLocation,
parentIdentifierDotLocation,
childIdentifierNameLocation,
return BehaviorFunction(type,
objectName,
objectFunctionOrBehaviorName,
objectNameLocation,
objectNameDotLocation,
objectFunctionOrBehaviorNameLocation,
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>(
parentIdentifier,
childIdentifierName);
auto parametersNode = Parameters(function.get(), parentIdentifier);
function->parameters = std::move(parametersNode.parameters),
returnType,
objectName,
std::move(parametersNode.parameters),
metadata,
objectFunctionOrBehaviorName);
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(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
function->objectNameLocation = parentIdentifierLocation;
function->objectNameDotLocation = parentIdentifierDotLocation;
function->functionNameLocation = childIdentifierNameLocation;
objectNameLocation.GetStartPosition(), GetCurrentPosition());
function->objectNameLocation = objectNameLocation;
function->objectNameDotLocation = objectNameDotLocation;
function->functionNameLocation = objectFunctionOrBehaviorNameLocation;
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<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."));
}
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)."));
node->location = ExpressionParserLocation(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
node->identifierNameLocation = parentIdentifierLocation;
node->identifierNameDotLocation = parentIdentifierDotLocation;
node->childIdentifierNameLocation = childIdentifierNameLocation;
objectNameLocation.GetStartPosition(), GetCurrentPosition());
node->objectNameLocation = objectNameLocation;
node->objectNameDotLocation = objectNameDotLocation;
node->objectFunctionOrBehaviorNameLocation =
objectFunctionOrBehaviorNameLocation;
return std::move(node);
}
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode> BehaviorFunction(
const gd::String &type,
const gd::String &objectName,
const gd::String &behaviorName,
const ExpressionParserLocation &objectNameLocation,
@@ -447,14 +547,35 @@ 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());
@@ -470,7 +591,7 @@ class GD_CORE_API ExpressionParser2 {
return std::move(function);
} else {
auto node = gd::make_unique<ObjectFunctionNameNode>(
objectName, behaviorName, functionName);
type, objectName, behaviorName, functionName);
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis was expected here to call a function."));
@@ -494,7 +615,7 @@ class GD_CORE_API ExpressionParser2 {
};
ParametersNode Parameters(
FunctionCallNode *functionCallNode,
std::vector<gd::ParameterMetadata> parameterMetadata,
const gd::String &objectName = "",
const gd::String &behaviorName = "") {
std::vector<std::unique_ptr<ExpressionNode>> parameters;
@@ -505,25 +626,77 @@ class GD_CORE_API ExpressionParser2 {
size_t parameterIndex =
WrittenParametersFirstIndex(objectName, behaviorName);
bool previousCharacterIsParameterSeparator = false;
while (!IsEndReached()) {
SkipAllWhitespaces();
if (CheckIfChar(IsClosingParenthesis) && !previousCharacterIsParameterSeparator) {
if (CheckIfChar(IsClosingParenthesis)) {
auto closingParenthesisLocation = SkipChar();
return ParametersNode{
std::move(parameters), nullptr, closingParenthesisLocation};
}
bool isEmptyParameter = CheckIfChar(IsParameterSeparator)
|| (CheckIfChar(IsClosingParenthesis) && previousCharacterIsParameterSeparator);
auto parameter = isEmptyParameter ? gd::make_unique<EmptyNode>() : Expression();
parameter->parent = functionCallNode;
parameters.push_back(std::move(parameter));
} 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);
SkipAllWhitespaces();
previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
SkipIfChar(IsParameterSeparator);
parameterIndex++;
// 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++;
}
}
ExpressionParserLocation invalidClosingParenthesisLocation;
@@ -535,32 +708,92 @@ 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(
gd::String::value_type operatorChar) {
if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
operatorChar == '*') {
return gd::make_unique<ExpressionParserDiagnostic>();
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());
}
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an operator that is not supported. Operator should be "
"either +, -, / or *."),
GetCurrentPosition());
return gd::make_unique<ExpressionParserDiagnostic>();
}
std::unique_ptr<ExpressionParserDiagnostic> ValidateUnaryOperator(
const gd::String &type,
gd::String::value_type operatorChar,
size_t position) {
if (operatorChar == '+' || operatorChar == '-') {
return gd::make_unique<ExpressionParserDiagnostic>();
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);
}
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an \"unary\" operator that is not supported. Operator "
"should be "
"either + or -."),
position);
return gd::make_unique<ExpressionParserDiagnostic>();
}
///@}
@@ -748,7 +981,7 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<NumberNode> ReadNumber();
std::unique_ptr<EmptyNode> ReadUntilWhitespace() {
std::unique_ptr<EmptyNode> ReadUntilWhitespace(gd::String type) {
size_t startPosition = GetCurrentPosition();
gd::String text;
while (currentPosition < expression.size() &&
@@ -757,13 +990,13 @@ class GD_CORE_API ExpressionParser2 {
currentPosition++;
}
auto node = gd::make_unique<EmptyNode>(text);
auto node = gd::make_unique<EmptyNode>(type, text);
node->location =
ExpressionParserLocation(startPosition, GetCurrentPosition());
return node;
}
std::unique_ptr<EmptyNode> ReadUntilEnd() {
std::unique_ptr<EmptyNode> ReadUntilEnd(gd::String type) {
size_t startPosition = GetCurrentPosition();
gd::String text;
while (currentPosition < expression.size()) {
@@ -771,7 +1004,7 @@ class GD_CORE_API ExpressionParser2 {
currentPosition++;
}
auto node = gd::make_unique<EmptyNode>(text);
auto node = gd::make_unique<EmptyNode>(type, text);
node->location =
ExpressionParserLocation(startPosition, GetCurrentPosition());
return node;
@@ -804,11 +1037,34 @@ 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;
};

View File

@@ -17,7 +17,6 @@ class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
struct FunctionCallNode;
} // namespace gd
namespace gd {
@@ -58,10 +57,6 @@ 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_)
@@ -91,7 +86,7 @@ struct GD_CORE_API ExpressionParserError : public ExpressionParserDiagnostic {
* an expression inherits from.
*/
struct GD_CORE_API ExpressionNode {
ExpressionNode() : parent(nullptr) {};
ExpressionNode(const gd::String &type_) : type(type_){};
virtual ~ExpressionNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker){};
@@ -102,12 +97,17 @@ struct GD_CORE_API ExpressionNode {
/// function can store the position of the
/// object name, the dot, the function
/// name, etc...
ExpressionNode *parent;
gd::String type; // Actual type of the node.
// "string", "number", type supported by
// gd::ParameterMetadata::IsObject, types supported by
// gd::ParameterMetadata::IsExpression or "unknown".
};
struct GD_CORE_API SubExpressionNode : public ExpressionNode {
SubExpressionNode(std::unique_ptr<ExpressionNode> expression_)
: ExpressionNode(), expression(std::move(expression_)){};
SubExpressionNode(const gd::String &type_,
std::unique_ptr<ExpressionNode> expression_)
: ExpressionNode(type_), 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(gd::String::value_type op_)
: ExpressionNode(), op(op_){};
OperatorNode(const gd::String &type_, gd::String::value_type op_)
: ExpressionNode(type_), 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(gd::String::value_type op_)
: ExpressionNode(), op(op_){};
UnaryOperatorNode(const gd::String &type_, gd::String::value_type op_)
: ExpressionNode(type_), 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_){};
: ExpressionNode("number"), 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(), text(text_){};
TextNode(const gd::String &text_) : ExpressionNode("string"), text(text_){};
virtual ~TextNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitTextNode(*this);
@@ -177,88 +177,32 @@ 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 with bracket accessor or at least 2 "dot" accessors.
* \brief A variable, potentially with accessor to its children.
*
* Example: MyVariable or MyVariable.MyChildren
*
* 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 FunctionCallOrObjectFunctionNameOrEmptyNode {
VariableNode(const gd::String &name_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(), name(name_){};
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_){};
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
@@ -272,8 +216,7 @@ struct GD_CORE_API VariableNode : public FunctionCallOrObjectFunctionNameOrEmpty
*/
struct GD_CORE_API VariableAccessorNode
: public VariableAccessorOrVariableBracketAccessorNode {
VariableAccessorNode(const gd::String &name_)
: VariableAccessorOrVariableBracketAccessorNode(), name(name_){};
VariableAccessorNode(const gd::String &name_) : name(name_){};
virtual ~VariableAccessorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitVariableAccessorNode(*this);
@@ -291,7 +234,7 @@ struct GD_CORE_API VariableAccessorNode
struct GD_CORE_API VariableBracketAccessorNode
: public VariableAccessorOrVariableBracketAccessorNode {
VariableBracketAccessorNode(std::unique_ptr<ExpressionNode> expression_)
: VariableAccessorOrVariableBracketAccessorNode(), expression(std::move(expression_)){};
: expression(std::move(expression_)){};
virtual ~VariableBracketAccessorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitVariableBracketAccessorNode(*this);
@@ -300,26 +243,55 @@ 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.Physics::LinearVelocity".
*
* Other cases like "MyObject.Function" or "MyObject.Physics" are IdentifierNode
* to allow handling ambiguities.
*
* \see gd::IdentifierNode
* For example: "MyObject.Function" or "MyObject.Physics" or
* "MyObject.Physics::LinearVelocity".
*/
struct GD_CORE_API ObjectFunctionNameNode
: public FunctionCallOrObjectFunctionNameOrEmptyNode {
ObjectFunctionNameNode(const gd::String &objectName_,
ObjectFunctionNameNode(const gd::String &type_,
const gd::String &objectName_,
const gd::String &objectFunctionOrBehaviorName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
objectName(objectName_),
objectFunctionOrBehaviorName(objectFunctionOrBehaviorName_) {}
ObjectFunctionNameNode(const gd::String &objectName_,
ObjectFunctionNameNode(const gd::String &type_,
const gd::String &objectName_,
const gd::String &behaviorName_,
const gd::String &behaviorFunctionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
objectName(objectName_),
objectFunctionOrBehaviorName(behaviorName_),
behaviorFunctionName(behaviorFunctionName_) {}
@@ -362,24 +334,39 @@ struct GD_CORE_API ObjectFunctionNameNode
*/
struct GD_CORE_API FunctionCallNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
/** \brief Construct a free function call node. */
FunctionCallNode(const gd::String &functionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
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_),
functionName(functionName_){};
/** \brief Construct an object function call node. */
FunctionCallNode(const gd::String &objectName_,
FunctionCallNode(const gd::String &type_,
const gd::String &objectName_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
objectName(objectName_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
functionName(functionName_){};
/** \brief Construct a behavior function call node. */
FunctionCallNode(const gd::String &objectName_,
FunctionCallNode(const gd::String &type_,
const gd::String &objectName_,
const gd::String &behaviorName_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
objectName(objectName_),
behaviorName(behaviorName_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
functionName(functionName_){};
virtual ~FunctionCallNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
@@ -389,6 +376,7 @@ 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
@@ -413,8 +401,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 &text_ = "")
: FunctionCallOrObjectFunctionNameOrEmptyNode(), text(text_){};
EmptyNode(const gd::String &type_, const gd::String &text_ = "")
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_), text(text_){};
virtual ~EmptyNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitEmptyNode(*this);

View File

@@ -91,9 +91,6 @@ 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()) {

View File

@@ -4,7 +4,6 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Events/Serialization.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
@@ -233,8 +232,7 @@ void EventsListSerialization::SerializeEventsTo(const EventsList& list,
const gd::BaseEvent& event = list.GetEvent(j);
SerializerElement& eventElem = events.AddChild("event");
if (event.IsDisabled())
eventElem.SetAttribute("disabled", event.IsDisabled());
if (event.IsDisabled()) eventElem.SetAttribute("disabled", event.IsDisabled());
if (event.IsFolded()) eventElem.SetAttribute("folded", event.IsFolded());
eventElem.AddChild("type").SetValue(event.GetType());
@@ -269,9 +267,6 @@ void gd::EventsListSerialization::UnserializeInstructionsFrom(
instrElement.GetChild("type", 0, "Type")
.GetBoolAttribute("inverted", false, "Contraire"));
instruction.SetAwaited(
instrElement.GetChild("type", 0, "Type").GetBoolAttribute("await"));
// Read parameters
vector<gd::Expression> parameters;
@@ -345,12 +340,9 @@ void gd::EventsListSerialization::SerializeInstructionsTo(
instructions.ConsiderAsArrayOf("instruction");
for (std::size_t k = 0; k < list.size(); k++) {
SerializerElement& instruction = instructions.AddChild("instruction");
instruction.AddChild("type").SetAttribute("value", list[k].GetType());
if (list[k].IsInverted())
instruction.GetChild("type").SetAttribute("inverted", true);
if (list[k].IsAwaited())
instruction.GetChild("type").SetAttribute("await", true);
instruction.AddChild("type")
.SetAttribute("value", list[k].GetType())
.SetAttribute("inverted", list[k].IsInverted());
// Parameters
SerializerElement& parameters = instruction.AddChild("parameters");
@@ -360,10 +352,9 @@ void gd::EventsListSerialization::SerializeInstructionsTo(
.SetValue(list[k].GetParameter(l).GetPlainString());
// Sub instructions
if (!list[k].GetSubInstructions().empty()) {
SerializeInstructionsTo(list[k].GetSubInstructions(),
instruction.AddChild("subInstructions"));
}
SerializerElement& subInstructions =
instruction.AddChild("subInstructions");
SerializeInstructionsTo(list[k].GetSubInstructions(), subInstructions);
}
}

View File

@@ -14,14 +14,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
extension
.SetExtensionInformation(
"BuiltinAudio",
_("Sounds and music"),
_("Sounds and musics"),
_("GDevelop provides several conditions and actions to play audio "
"files. They can be either long music or short sound effects."),
"files. They can be either long musics or short sound effects."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/audio")
.SetCategory("Audio");
extension.AddInstructionOrExpressionGroupMetadata(_("Sounds and music"))
extension.AddInstructionOrExpressionGroupMetadata(_("Sounds and musics"))
.SetIcon("res/actions/music24.png");
extension

View File

@@ -219,7 +219,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("SetAngle",
_("Angle"),
_("Change the angle of rotation of an object (in degrees)."),
_("Change the angle of rotation of an object."),
_("the angle"),
_("Angle"),
"res/actions/direction24.png",
@@ -289,8 +289,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Speed on X axis (in pixels per second)"))
.AddParameter("expression", _("Speed on Y axis (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0");
.AddParameter("forceMultiplier", _("Force multiplier"));
obj.AddAction("AddForceAL",
_("Add a force (angle)"),
@@ -306,8 +305,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.AddParameter("forceMultiplier", _("Force multiplier"))
.MarkAsAdvanced();
obj.AddAction(
@@ -325,8 +323,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.AddParameter("forceMultiplier", _("Force multiplier"))
.MarkAsAdvanced();
obj.AddAction(
@@ -759,8 +756,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Target Object"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.AddParameter("forceMultiplier", _("Force multiplier"))
.MarkAsAdvanced();
obj.AddAction(
@@ -780,8 +776,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectPtr", _("Rotate around this object"))
.AddParameter("expression", _("Speed (in degrees per second)"))
.AddParameter("expression", _("Distance (in pixels)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.AddParameter("forceMultiplier", _("Force multiplier"))
.MarkAsAdvanced();
obj.AddAction("MettreAutour",
@@ -998,7 +993,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddExpression("ForceAngle",
_("Angle of the sum of forces"),
_("Angle of the sum of forces (in degrees)"),
_("Angle of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
@@ -1131,9 +1126,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddExpression("AngleToObject",
_("Angle between two objects"),
_("Compute the angle between two objects (in degrees). "
"If you need the angle to an arbitrary position, "
"use AngleToPosition."),
_("Compute the angle between two objects. If you need the "
"angle to an arbitrary position, use AngleToPosition."),
_("Angle"),
"res/actions/position.png")
.AddParameter("object", _("Object"))
@@ -1166,8 +1160,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddExpression("AngleToPosition",
_("Angle between an object and a position"),
_("Compute the angle between the object center and a "
"\"target\" position (in degrees). If you need the angle "
"between two objects, use AngleToObject."),
"\"target\" position. If you need the angle between two "
"objects, use AngleToObject."),
_("Angle"),
"res/actions/position.png")
.AddParameter("object", _("Object"))

View File

@@ -29,7 +29,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension
.AddExpressionAndConditionAndAction(
"number",
"CameraCenterX",
"CameraX",
_("Camera center X position"),
_("the X position of the center of a camera"),
_("the X position of camera _PARAM4_ (layer: _PARAM3_)"),
@@ -43,25 +43,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.SetDefaultValue("0")
.MarkAsAdvanced();
// Compatibility with GD <= 5.0.135
extension.AddDuplicatedCondition("CameraX", "CameraCenterX")
.SetHidden(); // Deprecated
extension.AddDuplicatedExpression("CameraX", "CameraCenterX")
.SetHidden(); // Deprecated
extension.AddDuplicatedAction("SetCameraX", "SetCameraCenterX")
.SetHidden(); // Deprecated
extension.AddDuplicatedAction("CameraX", "SetCameraX")
.SetHidden(); // Deprecated
extension.AddDuplicatedExpression("VueX", "CameraX")
.SetHidden(); // Deprecated
// end of compatibility code
extension
.AddExpressionAndConditionAndAction(
"number",
"CameraCenterY",
"CameraY",
_("Camera center Y position"),
_("the Y position of the center of a camera"),
_("the Y position of camera _PARAM4_ (layer: _PARAM3_)"),
@@ -75,20 +65,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.SetDefaultValue("0")
.MarkAsAdvanced();
// Compatibility with GD <= 5.0.135
extension.AddDuplicatedCondition("CameraY", "CameraCenterY")
.SetHidden(); // Deprecated
extension.AddDuplicatedExpression("CameraY", "CameraCenterY")
.SetHidden(); // Deprecated
extension.AddDuplicatedAction("SetCameraY", "SetCameraCenterY")
.SetHidden(); // Deprecated
extension.AddDuplicatedAction("CameraY", "SetCameraY")
.SetHidden(); // Deprecated
extension.AddDuplicatedExpression("VueY", "CameraY")
.SetHidden(); // Deprecated
// end of compatibility code
extension
.AddExpressionAndCondition(
@@ -100,9 +80,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"",
"res/conditions/camera24.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.AddParameter("layer", _("Layer (base layer if empty)"))
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"), "", true)
.AddParameter("expression", _("Camera number"))
.UseStandardParameters("number")
.MarkAsAdvanced();
@@ -116,77 +96,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"",
"res/conditions/camera24.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.AddParameter("layer", _("Layer (base layer if empty)"))
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"), "", true)
.UseStandardParameters("number")
.MarkAsAdvanced();
extension
.AddExpressionAndCondition(
"number",
"CameraBorderLeft",
_("Camera left border position"),
_("the position of the left border of a camera"),
_("the position of the left border of camera _PARAM2_ of layer "
"_PARAM1_"),
"",
"res/conditions/camera24.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"), "", true)
.UseStandardParameters("number")
.MarkAsAdvanced();
extension
.AddExpressionAndCondition(
"number",
"CameraBorderRight",
_("Camera right border position"),
_("the position of the right border of a camera"),
_("the position of the right border of camera _PARAM2_ of layer "
"_PARAM1_"),
"",
"res/conditions/camera24.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"), "", true)
.UseStandardParameters("number")
.MarkAsAdvanced();
extension
.AddExpressionAndCondition(
"number",
"CameraBorderTop",
_("Camera top border position"),
_("the position of the top border of a camera"),
_("the position of the top border of camera _PARAM2_ of layer "
"_PARAM1_"),
"",
"res/conditions/camera24.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"), "", true)
.UseStandardParameters("number")
.MarkAsAdvanced();
extension
.AddExpressionAndCondition(
"number",
"CameraBorderBottom",
_("Camera bottom border position"),
_("the position of the bottom border of a camera"),
_("the position of the bottom border of camera _PARAM2_ of layer "
"_PARAM1_"),
"",
"res/conditions/camera24.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"), "", true)
.AddParameter("expression", _("Camera number"))
.UseStandardParameters("number")
.MarkAsAdvanced();
@@ -195,7 +107,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"number",
"CameraAngle",
_("Angle of a camera of a layer"),
_("the angle of rotation of a camera (in degrees)"),
_("the angle of rotation of a camera"),
_("the angle of camera (layer: _PARAM3_, camera: _PARAM4_)"),
"",
"res/conditions/camera24.png")

View File

@@ -96,13 +96,13 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
_("Difference between two angles"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("First angle, in degrees"))
.AddParameter("expression", _("Second angle, in degrees"));
.AddParameter("expression", _("First angle"))
.AddParameter("expression", _("Second angle"));
extension
.AddExpression("AngleBetweenPositions",
_("Angle between two positions"),
_("Compute the angle between two positions (in degrees)."),
_("Compute the angle between two positions."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("First point X position"))
@@ -159,8 +159,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("acos",
_("Arccosine"),
_("Arccosine, return an angle (in radian). "
"`ToDeg` allows to convert it to degrees."),
_("Arccosine"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -176,8 +175,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("asin",
_("Arcsine"),
_("Arcsine, return an angle (in radian). "
"`ToDeg` allows to convert it to degrees."),
_("Arcsine"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -193,8 +191,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("atan",
_("Arctangent"),
_("Arctangent, return an angle (in radian). "
"`ToDeg` allows to convert it to degrees."),
_("Arctangent"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -261,8 +258,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("cos",
_("Cosine"),
_("Cosine of an angle (in radian). "
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
_("Cosine of a number"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -404,8 +400,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("sin",
_("Sine"),
_("Sine of an angle (in radian). "
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
_("Sine of a number"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -429,8 +424,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("tan",
_("Tangent"),
_("Tangent of an angle (in radian). "
"If you want to use degrees, use`ToRad`: `tan(ToRad(45))`."),
_("Tangent of a number"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));

View File

@@ -4,10 +4,8 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Extensions/Builtin/SpriteExtension/Direction.h"
#include <iostream>
#include <vector>
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/Sprite.h"
#include "GDCore/Serialization/SerializerElement.h"
@@ -31,15 +29,6 @@ const Sprite& Direction::GetSprite(std::size_t nb) const { return sprites[nb]; }
Sprite& Direction::GetSprite(std::size_t nb) { return sprites[nb]; }
const std::vector<gd::String>& Direction::GetSpriteNames() const {
static std::vector<gd::String> spriteNames;
spriteNames.clear();
for (std::size_t i = 0; i < sprites.size(); ++i) {
spriteNames.push_back(sprites[i].GetImageName());
}
return spriteNames;
}
void Direction::RemoveSprite(std::size_t index) {
if (index < sprites.size()) sprites.erase(sprites.begin() + index);
}

View File

@@ -72,13 +72,6 @@ class GD_CORE_API Direction {
*/
Sprite& GetSprite(std::size_t nb);
/**
* \brief Return a vector of references to sprite names.
*
* \return A vector of all sprite names references.
*/
const std::vector<gd::String>& GetSpriteNames() const;
/**
* \brief Check if the direction contains sprites.
*

View File

@@ -21,7 +21,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/timers-and-time");
extension.AddInstructionOrExpressionGroupMetadata(_("Timers and time"))
extension.AddInstructionOrExpressionGroupMetadata(
_("Timers and time")
)
.SetIcon("res/conditions/timer24.png");
// Deprecated and replaced by CompareTimer
@@ -156,7 +158,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/timer.svg",
"res/timer.svg")
.AddParameter("expression", "Time to wait in seconds")
.SetHelpPath("/all-features/timers-and-time/wait-action");
.SetHelpPath("/all-features/timers-and-time/wait-action")
.SetAsync();
extension
.AddExpression("TimeDelta",

View File

@@ -22,6 +22,7 @@ InstructionMetadata::InstructionMetadata()
canHaveSubInstructions(false),
hidden(true),
usageComplexity(5),
isAsync(false),
isPrivate(false),
isObjectInstruction(false),
isBehaviorInstruction(false) {}
@@ -45,6 +46,7 @@ InstructionMetadata::InstructionMetadata(const gd::String& extensionNamespace_,
extensionNamespace(extensionNamespace_),
hidden(false),
usageComplexity(5),
isAsync(false),
isPrivate(false),
isObjectInstruction(false),
isBehaviorInstruction(false) {}

View File

@@ -6,10 +6,10 @@
#ifndef INSTRUCTIONMETADATA_H
#define INSTRUCTIONMETADATA_H
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <algorithm>
#include "GDCore/Events/Instruction.h"
#include "GDCore/String.h"
@@ -104,16 +104,16 @@ class GD_CORE_API InstructionMetadata {
* background, executing the instructions following it before the frame after
* it resolved.
*/
bool IsAsync() const {
return !codeExtraInformation.asyncFunctionCallName.empty();
}
bool IsAsync() const { return isAsync; }
/**
* Check if the instruction asynchronicity is optional. If it is, it can either
* be used synchronously or asynchronously, with one function for each.
* Set that the instruction is asynchronous - it will be running in the
* background, executing the instructions following it before the frame after
* it resolved.
*/
bool IsOptionallyAsync() const {
return IsAsync() && !codeExtraInformation.functionCallName.empty();
InstructionMetadata &SetAsync() {
isAsync = true;
return *this;
}
/**
@@ -319,26 +319,14 @@ class GD_CORE_API InstructionMetadata {
virtual ~ExtraInformation(){};
/**
* Set the name of the function which will be called in the generated code.
* \param functionName the name of the function to call.
* Set the function name which will be used when generating the code.
* \param functionName the name of the function to call
*/
ExtraInformation &SetFunctionName(const gd::String &functionName_) {
functionCallName = functionName_;
return *this;
}
/**
* Set the name of the function, doing asynchronous work, which will be called in
* the generated code. This function should return an asynchronous task
* (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
*
* \param functionName the name of the function doing asynchronous work to call.
*/
ExtraInformation &SetAsyncFunctionName(const gd::String &functionName_) {
asyncFunctionCallName = functionName_;
return *this;
}
/**
* Declare if the instruction being declared is somewhat manipulating in a
* standard way.
@@ -366,7 +354,7 @@ class GD_CORE_API InstructionMetadata {
* .AddParameter("object", _("Object"), "Text", false)
* .AddParameter("operator", _("Modification operator"), "string")
* .AddParameter("string", _("String"))
* .SetFunctionName("SetString").SetManipulatedType("string").SetGetter("GetString");
* .SetFunctionName("SetString").SetManipulatedType("string").SetGetter("GetString").SetIncludeFile("MyExtension/TextObject.h");
*
* DECLARE_END_OBJECT_ACTION()
* \endcode
@@ -434,7 +422,6 @@ class GD_CORE_API InstructionMetadata {
bool HasCustomCodeGenerator() const { return hasCustomCodeGenerator; }
gd::String functionCallName;
gd::String asyncFunctionCallName;
gd::String type;
AccessType accessType;
gd::String optionalAssociatedInstruction;
@@ -467,26 +454,15 @@ class GD_CORE_API InstructionMetadata {
}
/**
* Set the name of the function which will be called in the generated code.
* \param functionName the name of the function to call.
* \brief Set the function that should be called when generating the source
* code from events.
* \param functionName the name of the function to call
* \note Shortcut for `codeExtraInformation.SetFunctionName`.
*/
ExtraInformation &SetFunctionName(const gd::String &functionName) {
return codeExtraInformation.SetFunctionName(functionName);
}
/**
* Set the name of the function, doing asynchronous work, which will be called in
* the generated code. This function should return an asynchronous task
* (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
*
* \param functionName the name of the function doing asynchronous work to call.
* \note Shortcut for `codeExtraInformation.SetAsyncFunctionName`.
*/
ExtraInformation &SetAsyncFunctionName(const gd::String &functionName) {
return codeExtraInformation.SetAsyncFunctionName(functionName);
}
std::vector<ParameterMetadata> parameters;
private:
@@ -503,6 +479,7 @@ class GD_CORE_API InstructionMetadata {
int usageComplexity; ///< Evaluate the instruction from 0 (simple&easy to
///< use) to 10 (complex to understand)
bool isPrivate;
bool isAsync;
bool isObjectInstruction;
bool isBehaviorInstruction;
gd::String requiredBaseObjectCapability;

View File

@@ -13,9 +13,7 @@
#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;
@@ -32,9 +30,12 @@ ExtensionAndMetadata<BehaviorMetadata>
MetadataProvider::GetExtensionAndBehaviorMetadata(const gd::Platform& platform,
gd::String behaviorType) {
for (auto& extension : platform.GetAllPlatformExtensions()) {
if (extension->HasBehavior(behaviorType))
return ExtensionAndMetadata<BehaviorMetadata>(
*extension, extension->GetBehaviorMetadata(behaviorType));
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));
}
}
return ExtensionAndMetadata<BehaviorMetadata>(badExtension, badBehaviorMetadata);
@@ -201,7 +202,8 @@ MetadataProvider::GetExtensionAndBehaviorExpressionMetadata(
const gd::Platform& platform, gd::String autoType, gd::String exprType) {
auto& extensions = platform.GetAllPlatformExtensions();
for (auto& extension : extensions) {
if (extension->HasBehavior(autoType)) {
const auto& autos = extension->GetBehaviorsTypes();
if (find(autos.begin(), autos.end(), autoType) != autos.end()) {
const auto& allAutoExpressions =
extension->GetAllExpressionsForBehavior(autoType);
if (allAutoExpressions.find(exprType) != allAutoExpressions.end())
@@ -290,7 +292,8 @@ MetadataProvider::GetExtensionAndBehaviorStrExpressionMetadata(
const gd::Platform& platform, gd::String autoType, gd::String exprType) {
auto& extensions = platform.GetAllPlatformExtensions();
for (auto& extension : extensions) {
if (extension->HasBehavior(autoType)) {
const auto& autos = extension->GetBehaviorsTypes();
if (find(autos.begin(), autos.end(), autoType) != autos.end()) {
const auto& allBehaviorStrExpressions =
extension->GetAllStrExpressionsForBehavior(autoType);
if (allBehaviorStrExpressions.find(exprType) !=
@@ -347,30 +350,28 @@ 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);
if (&stringExpressionMetadata != &badExpressionMetadata) {
return stringExpressionMetadata;
}
return badExpressionMetadata;
return &numberExpressionMetadata != &badExpressionMetadata
? numberExpressionMetadata
: &stringExpressionMetadata != &badExpressionMetadata
? stringExpressionMetadata
: 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);
if (&stringExpressionMetadata != &badExpressionMetadata) {
return stringExpressionMetadata;
}
return badExpressionMetadata;
return &numberExpressionMetadata != &badExpressionMetadata
? numberExpressionMetadata
: &stringExpressionMetadata != &badExpressionMetadata
? stringExpressionMetadata
: badExpressionMetadata;
}
const gd::ExpressionMetadata&
@@ -379,98 +380,14 @@ 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;
}
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() == &parameter) {
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;
return &numberExpressionMetadata != &badExpressionMetadata
? numberExpressionMetadata
: &stringExpressionMetadata != &badExpressionMetadata
? stringExpressionMetadata
: badExpressionMetadata;
}
MetadataProvider::~MetadataProvider() {}

View File

@@ -15,8 +15,6 @@ class ExpressionMetadata;
class ExpressionMetadata;
class Platform;
class PlatformExtension;
struct FunctionCallNode;
struct ExpressionNode;
} // namespace gd
namespace gd {
@@ -236,26 +234,6 @@ 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.

View File

@@ -35,18 +35,4 @@ 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 &parameterType) {
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

View File

@@ -207,15 +207,6 @@ 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 &parameterType);
static const gd::String numberType;
static const gd::String stringType;
/** \name Serialization
*/
///@{

View File

@@ -14,7 +14,7 @@
namespace gd {
void ParameterMetadataTools::ParametersToObjectsContainer(
const gd::Project& project,
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::Expression& parameterValue,
const gd::String& parameterValue,
const gd::String& lastObjectName)> fn) {
IterateOverParametersWithIndex(
parameters,
parametersMetadata,
[&fn](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
const gd::String& 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::Expression& parameterValue,
const gd::String& 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::Expression& parameterValue =
const gd::String& parameterValue =
pNb < parameters.size() ? parameters[pNb].GetPlainString() : "";
const gd::Expression& parameterValueOrDefault =
parameterValue.GetPlainString().empty() && parameterMetadata.optional
? Expression(parameterMetadata.GetDefaultValue())
const gd::String& parameterValueOrDefault =
parameterValue.empty() && parameterMetadata.optional
? 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.GetPlainString();
lastObjectName = parameterValueOrDefault;
}
}

View File

@@ -19,7 +19,7 @@ namespace gd {
class GD_CORE_API ParameterMetadataTools {
public:
static void ParametersToObjectsContainer(
const gd::Project& project,
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::Expression& parameterValue,
const gd::String& 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::Expression& parameterValue,
const gd::String& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName)> fn);

View File

@@ -346,11 +346,6 @@ 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())
@@ -380,7 +375,7 @@ gd::InstructionMetadata& PlatformExtension::AddDuplicatedAction(
auto copiedAction = actionsInfos.find(copiedNameWithNamespace);
if (copiedAction == actionsInfos.end()) {
gd::LogError("Could not find an action with name " +
gd::LogWarning("Could not find an action with name " +
copiedNameWithNamespace + " to copy.");
} else {
actionsInfos[newNameWithNamespace] = copiedAction->second;
@@ -400,7 +395,7 @@ gd::InstructionMetadata& PlatformExtension::AddDuplicatedCondition(
auto copiedCondition = conditionsInfos.find(copiedNameWithNamespace);
if (copiedCondition == conditionsInfos.end()) {
gd::LogError("Could not find a condition with name " +
gd::LogWarning("Could not find a condition with name " +
copiedNameWithNamespace + " to copy.");
} else {
conditionsInfos[newNameWithNamespace] = copiedCondition->second;
@@ -417,7 +412,7 @@ gd::ExpressionMetadata& PlatformExtension::AddDuplicatedExpression(
auto copiedExpression = expressionsInfos.find(copiedNameWithNamespace);
if (copiedExpression == expressionsInfos.end()) {
gd::LogError("Could not find an expression with name " +
gd::LogWarning("Could not find an expression with name " +
copiedNameWithNamespace + " to copy.");
} else {
expressionsInfos[newNameWithNamespace] = copiedExpression->second;
@@ -434,7 +429,7 @@ gd::ExpressionMetadata& PlatformExtension::AddDuplicatedStrExpression(
auto copiedExpression = strExpressionsInfos.find(copiedNameWithNamespace);
if (copiedExpression == strExpressionsInfos.end()) {
gd::LogError("Could not find a string expression with name " +
gd::LogWarning("Could not find a string expression with name " +
copiedNameWithNamespace + " to copy.");
} else {
strExpressionsInfos[newNameWithNamespace] = copiedExpression->second;

View File

@@ -467,12 +467,6 @@ 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.
*/

View File

@@ -11,6 +11,7 @@
#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"
@@ -117,20 +118,27 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
instruction.GetParameters(),
metadata.GetParameters(),
[&](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
const gd::String& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const gd::String& type = parameterMetadata.type;
if (gd::ParameterMetadata::IsBehavior(type)) {
if (lastObjectName == objectName) {
if (parameterValue.GetPlainString() == oldBehaviorName) {
if (parameterValue == oldBehaviorName) {
instruction.SetParameter(parameterIndex,
gd::Expression(newBehaviorName));
}
}
} else {
auto node = parameterValue.GetRootNode();
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>());
if (node) {
ExpressionBehaviorRenamer renamer(GetGlobalObjectsContainer(),
GetObjectsContainer(),

View File

@@ -9,6 +9,7 @@
#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"
@@ -16,7 +17,6 @@
#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,17 +31,7 @@ namespace gd {
class GD_CORE_API ExpressionObjectsAnalyzer
: public ExpressionParser2NodeWorker {
public:
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_){};
ExpressionObjectsAnalyzer(EventsContext& context_) : context(context_){};
virtual ~ExpressionObjectsAnalyzer(){};
protected:
@@ -69,8 +59,7 @@ class GD_CORE_API ExpressionObjectsAnalyzer
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
if (gd::ParameterMetadata::IsObject(node.type)) {
context.AddObjectName(node.identifierName);
}
}
@@ -98,11 +87,6 @@ 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;
};
@@ -118,7 +102,7 @@ bool EventsContextAnalyzer::DoVisitInstruction(gd::Instruction& instruction,
instruction.GetParameters(),
instrInfo.parameters,
[this](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
const gd::String& parameterValue,
const gd::String& lastObjectName) {
AnalyzeParameter(platform,
project,
@@ -145,14 +129,16 @@ void EventsContextAnalyzer::AnalyzeParameter(
if (ParameterMetadata::IsObject(type)) {
context.AddObjectName(value);
} else if (ParameterMetadata::IsExpression("number", type)) {
auto node = parameter.GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", value);
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "number", context);
ExpressionObjectsAnalyzer analyzer(context);
node->Visit(analyzer);
} else if (ParameterMetadata::IsExpression("string", type)) {
auto node = parameter.GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", value);
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "string", context);
ExpressionObjectsAnalyzer analyzer(context);
node->Visit(analyzer);
} else if (ParameterMetadata::IsBehavior(type)) {
context.AddBehaviorName(lastObjectName, value);

View File

@@ -1,41 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsLeaderboardsLister.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
bool EventsLeaderboardsLister::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const gd::InstructionMetadata& instrInfo =
isCondition ? MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: MetadataProvider::GetActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i)
if (instrInfo.GetParameter(i).type == "leaderboardId") {
leaderboardIds.insert(instruction.GetParameter(i).GetPlainString());
}
return false;
}
EventsLeaderboardsLister::~EventsLeaderboardsLister() {}
} // namespace gd

View File

@@ -1,48 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EventsLeaderboardsLister_H
#define EventsLeaderboardsLister_H
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class Project;
class EventsList;
}
namespace gd {
/**
* \brief List the leaderboard ids in the instructions.
*
* \ingroup IDE
*/
class GD_CORE_API EventsLeaderboardsLister : public ArbitraryEventsWorker {
public:
EventsLeaderboardsLister(gd::Project& project_) : project(project_){};
virtual ~EventsLeaderboardsLister();
/**
* Return the values of all leaderboardIds found in the events.
*/
const std::set<gd::String>& GetLeaderboardIds() { return leaderboardIds; }
private:
virtual bool DoVisitInstruction(gd::Instruction& instruction,
bool isCondition);
std::set<gd::String> leaderboardIds;
gd::Project& project;
};
} // namespace gd
#endif // EventsLeaderboardsLister_H

View File

@@ -1,49 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsLeaderboardsRenamer.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
bool EventsLeaderboardsRenamer::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const gd::InstructionMetadata& instrInfo =
isCondition ? MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: MetadataProvider::GetActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i) {
const gd::ParameterMetadata parameter = instrInfo.GetParameter(i);
if (parameter.type == "leaderboardId") {
const gd::String leaderboardId =
instruction.GetParameter(i).GetPlainString();
if (leaderboardIdMap.find(leaderboardId) != leaderboardIdMap.end()) {
instruction.SetParameter(i, leaderboardIdMap[leaderboardId]);
}
}
}
return false;
}
EventsLeaderboardsRenamer::~EventsLeaderboardsRenamer() {}
} // namespace gd

View File

@@ -1,46 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EventsLeaderboardsRenamer_H
#define EventsLeaderboardsRenamer_H
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class Project;
class EventsList;
}
namespace gd {
/**
* \brief Replace the leaderboard ids in the instructions.
*
* \ingroup IDE
*/
class GD_CORE_API EventsLeaderboardsRenamer : public ArbitraryEventsWorker {
public:
EventsLeaderboardsRenamer(
gd::Project& project_,
const std::map<gd::String, gd::String>& leaderboardIdMap_)
: project(project_), leaderboardIdMap(leaderboardIdMap_){};
virtual ~EventsLeaderboardsRenamer();
private:
virtual bool DoVisitInstruction(gd::Instruction& instruction,
bool isCondition);
std::map<gd::String, gd::String> leaderboardIdMap;
gd::Project& project;
};
} // namespace gd
#endif // EventsLeaderboardsRenamer_H

View File

@@ -11,15 +11,15 @@
#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"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/InstructionSentenceFormatter.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/InstructionSentenceFormatter.h"
using namespace std;
@@ -34,30 +34,18 @@ const gd::String EventsRefactorer::searchIgnoredCharacters = ";:,#()";
*/
class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
public:
ExpressionObjectRenamer(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String &rootType_,
const gd::String& objectName_,
ExpressionObjectRenamer(const gd::String& objectName_,
const gd::String& objectNewName_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
rootType(rootType_),
hasDoneRenaming(false),
: hasDoneRenaming(false),
objectName(objectName_),
objectNewName(objectNewName_){};
virtual ~ExpressionObjectRenamer(){};
static bool Rename(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String &rootType,
gd::ExpressionNode& node,
static bool Rename(gd::ExpressionNode& node,
const gd::String& objectName,
const gd::String& objectNewName) {
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
ExpressionObjectRenamer renamer(platform, globalObjectsContainer, objectsContainer, rootType, objectName, objectNewName);
if (ExpressionValidator::HasNoErrors(node)) {
ExpressionObjectRenamer renamer(objectName, objectNewName);
node.Visit(renamer);
return renamer.HasDoneRenaming();
@@ -93,8 +81,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (gd::ParameterMetadata::IsObject(type) &&
if (gd::ParameterMetadata::IsObject(node.type) &&
node.identifierName == objectName) {
hasDoneRenaming = true;
node.identifierName = objectNewName;
@@ -121,11 +108,6 @@ 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;
};
/**
@@ -136,27 +118,14 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
*/
class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
public:
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_){};
ExpressionObjectFinder(const gd::String& objectName_)
: hasObject(false), objectName(objectName_){};
virtual ~ExpressionObjectFinder(){};
static bool CheckIfHasObject(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String &rootType,
gd::ExpressionNode& node,
static bool CheckIfHasObject(gd::ExpressionNode& node,
const gd::String& objectName) {
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
ExpressionObjectFinder finder(platform, globalObjectsContainer, objectsContainer, rootType, objectName);
if (ExpressionValidator::HasNoErrors(node)) {
ExpressionObjectFinder finder(objectName);
node.Visit(finder);
return finder.HasFoundObject();
@@ -192,8 +161,7 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (gd::ParameterMetadata::IsObject(type) &&
if (gd::ParameterMetadata::IsObject(node.type) &&
node.identifierName == objectName) {
hasObject = true;
}
@@ -216,11 +184,6 @@ 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,
@@ -242,9 +205,11 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", actions[aId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
actions[aId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -252,9 +217,11 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"string", actions[aId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
actions[aId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -296,9 +263,11 @@ bool EventsRefactorer::RenameObjectInConditions(
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", conditions[cId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
conditions[cId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -306,9 +275,11 @@ bool EventsRefactorer::RenameObjectInConditions(
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"string", conditions[cId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
conditions[cId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -345,18 +316,20 @@ bool EventsRefactorer::RenameObjectInEventParameters(
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression("number",
parameterMetadata.GetType())) {
auto node = expression.GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", expression.GetPlainString());
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression("string",
parameterMetadata.GetType())) {
auto node = expression.GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", expression.GetPlainString());
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
@@ -432,9 +405,11 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", actions[aId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "number", *node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
deleteMe = true;
break;
}
@@ -442,9 +417,11 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"string", actions[aId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "string", *node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
deleteMe = true;
break;
}
@@ -492,9 +469,11 @@ bool EventsRefactorer::RemoveObjectInConditions(
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", conditions[cId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "number", *node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
deleteMe = true;
break;
}
@@ -502,9 +481,11 @@ bool EventsRefactorer::RemoveObjectInConditions(
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"string", conditions[cId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "string", *node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
deleteMe = true;
break;
}
@@ -554,19 +535,16 @@ void EventsRefactorer::RemoveObjectInEvents(const gd::Platform& platform,
}
}
std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::EventsList& events,
gd::String toReplace,
gd::String newString,
bool matchCase,
bool inConditions,
bool inActions,
bool inEventStrings) {
vector<EventsSearchResult> modifiedEvents;
void EventsRefactorer::ReplaceStringInEvents(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::EventsList& events,
gd::String toReplace,
gd::String newString,
bool matchCase,
bool inConditions,
bool inActions,
bool inEventStrings) {
for (std::size_t i = 0; i < events.size(); ++i) {
bool eventModified = false;
if (inConditions) {
vector<gd::InstructionsList*> conditionsVectors =
events[i].GetAllConditionsVectors();
@@ -578,13 +556,6 @@ std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
toReplace,
newString,
matchCase);
if (conditionsModified && !eventModified) {
modifiedEvents.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
i));
eventModified = true;
}
}
}
@@ -598,45 +569,25 @@ std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
toReplace,
newString,
matchCase);
if (actionsModified && !eventModified) {
modifiedEvents.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
i));
eventModified = true;
}
}
}
if (inEventStrings) {
bool eventStringModified = ReplaceStringInEventSearchableStrings(
project, layout, events[i], toReplace, newString, matchCase);
if (eventStringModified && !eventModified) {
modifiedEvents.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
i));
eventModified = true;
}
}
if (events[i].CanHaveSubEvents()) {
std::vector<EventsSearchResult> modifiedSubEvent =
ReplaceStringInEvents(project,
layout,
events[i].GetSubEvents(),
toReplace,
newString,
matchCase,
inConditions,
inActions,
inEventStrings);
std::copy(modifiedSubEvent.begin(),
modifiedSubEvent.end(),
std::back_inserter(modifiedEvents));
}
if (events[i].CanHaveSubEvents())
ReplaceStringInEvents(project,
layout,
events[i].GetSubEvents(),
toReplace,
newString,
matchCase,
inConditions,
inActions,
inEventStrings);
}
return modifiedEvents;
}
gd::String ReplaceAllOccurencesCaseUnsensitive(gd::String context,
@@ -767,16 +718,14 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
bool inEventSentences) {
vector<EventsSearchResult> results;
const gd::String& ignored_characters =
EventsRefactorer::searchIgnoredCharacters;
const gd::String& ignored_characters = EventsRefactorer::searchIgnoredCharacters;
search.replace_if(
search.begin(),
search.end(),
[ignored_characters](const char& c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search.replace_if(search.begin(),
search.end(),
[ignored_characters](const char &c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search = search.LeftTrim().RightTrim();
search.RemoveConsecutiveOccurrences(search.begin(), search.end(), ' ');
@@ -788,11 +737,8 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
events[i].GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
if (!eventAddedInResults &&
SearchStringInConditions(platform,
*conditionsVectors[j],
search,
matchCase,
inEventSentences)) {
SearchStringInConditions(
platform, *conditionsVectors[j], search, matchCase, inEventSentences)) {
results.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
@@ -805,11 +751,9 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
vector<gd::InstructionsList*> actionsVectors =
events[i].GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
if (!eventAddedInResults && SearchStringInActions(platform,
*actionsVectors[j],
search,
matchCase,
inEventSentences)) {
if (!eventAddedInResults &&
SearchStringInActions(
platform, *actionsVectors[j], search, matchCase, inEventSentences)) {
results.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
@@ -846,11 +790,12 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
return results;
}
bool EventsRefactorer::SearchStringInActions(const gd::Platform& platform,
gd::InstructionsList& actions,
gd::String search,
bool matchCase,
bool inSentences) {
bool EventsRefactorer::SearchStringInActions(
const gd::Platform& platform,
gd::InstructionsList& actions,
gd::String search,
bool matchCase,
bool inSentences) {
for (std::size_t aId = 0; aId < actions.size(); ++aId) {
for (std::size_t pNb = 0; pNb < actions[aId].GetParameters().size();
++pNb) {
@@ -881,30 +826,27 @@ bool EventsRefactorer::SearchStringInActions(const gd::Platform& platform,
return false;
}
bool EventsRefactorer::SearchStringInFormattedText(const gd::Platform& platform,
gd::Instruction& instruction,
gd::String search,
bool matchCase,
bool isCondition) {
bool EventsRefactorer::SearchStringInFormattedText(
const gd::Platform& platform,
gd::Instruction& instruction,
gd::String search,
bool matchCase,
bool isCondition) {
const auto& metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
gd::String completeSentence =
gd::InstructionSentenceFormatter::Get()->GetFullText(instruction,
metadata);
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
gd::String completeSentence = gd::InstructionSentenceFormatter::Get()->GetFullText(instruction, metadata);
const gd::String& ignored_characters =
EventsRefactorer::searchIgnoredCharacters;
const gd::String& ignored_characters = EventsRefactorer::searchIgnoredCharacters;
completeSentence.replace_if(
completeSentence.begin(),
completeSentence.end(),
[ignored_characters](const char& c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
completeSentence.replace_if(completeSentence.begin(),
completeSentence.end(),
[ignored_characters](const char &c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
completeSentence.RemoveConsecutiveOccurrences(
completeSentence.begin(), completeSentence.end(), ' ');

View File

@@ -7,7 +7,6 @@
#define GDCORE_EVENTSREFACTORER_H
#include <memory>
#include <vector>
#include "GDCore/Events/Instruction.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/String.h"
@@ -19,7 +18,7 @@ class ExternalEvents;
class BaseEvent;
class Instruction;
typedef std::shared_ptr<gd::BaseEvent> BaseEventSPtr;
} // namespace gd
}
namespace gd {
@@ -44,11 +43,10 @@ class GD_CORE_API EventsSearchResult {
bool IsEventsListValid() const { return eventsList != nullptr; }
/**
* \brief Get the events list containing the event pointed by the
* EventsSearchResult. \warning Only call this when IsEventsListValid returns
* true.
* \brief Get the events list containing the event pointed by the EventsSearchResult.
* \warning Only call this when IsEventsListValid returns true.
*/
const gd::EventsList& GetEventsList() const { return *eventsList; }
const gd::EventsList & GetEventsList() const { return *eventsList; }
std::size_t GetPositionInList() const { return positionInList; }
@@ -58,7 +56,7 @@ class GD_CORE_API EventsSearchResult {
* \brief Get the event pointed by the EventsSearchResult.
* \warning Only call this when IsEventValid returns true.
*/
const gd::BaseEvent& GetEvent() const { return *event.lock(); }
const gd::BaseEvent & GetEvent() const { return *event.lock(); }
};
/**
@@ -100,31 +98,27 @@ class GD_CORE_API EventsRefactorer {
* \return A vector containing EventsSearchResult objects filled with events
* containing the string
*/
static std::vector<EventsSearchResult> SearchInEvents(
const gd::Platform& platform,
gd::EventsList& events,
gd::String search,
bool matchCase,
bool inConditions,
bool inActions,
bool inEventStrings,
bool inEventSentences);
static std::vector<EventsSearchResult> SearchInEvents(const gd::Platform& platform,
gd::EventsList& events,
gd::String search,
bool matchCase,
bool inConditions,
bool inActions,
bool inEventStrings,
bool inEventSentences);
/**
* Replace all occurrences of a gd::String in events
*
* \return A vector of all modified events.
*/
static std::vector<EventsSearchResult> ReplaceStringInEvents(
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::EventsList& events,
gd::String toReplace,
gd::String newString,
bool matchCase,
bool inConditions,
bool inActions,
bool inEventString);
static void ReplaceStringInEvents(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::EventsList& events,
gd::String toReplace,
gd::String newString,
bool matchCase,
bool inConditions,
bool inActions,
bool inEventString);
virtual ~EventsRefactorer(){};
@@ -154,21 +148,20 @@ class GD_CORE_API EventsRefactorer {
gd::InstructionsList& instructions,
gd::String oldName,
gd::String newName);
/**
/**
* Replace all occurrences of an object name by another name in an expression
* with the specified metadata
* ( include : objects or objects in math/text expressions ).
*
* \return true if something was modified.
*/
static bool RenameObjectInEventParameters(
const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::Expression& expression,
gd::ParameterMetadata parameterMetadata,
gd::String oldName,
gd::String newName);
static bool RenameObjectInEventParameters(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::Expression& expression,
gd::ParameterMetadata parameterMetadata,
gd::String oldName,
gd::String newName);
/**
* Remove all conditions of the list using an object

View File

@@ -7,6 +7,7 @@
#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"
@@ -31,16 +32,10 @@ namespace gd {
class GD_CORE_API ExpressionParameterSearcher
: public ExpressionParser2NodeWorker {
public:
ExpressionParameterSearcher(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
std::set<gd::String>& results_,
ExpressionParameterSearcher(std::set<gd::String>& results_,
const gd::String& parameterType_,
const gd::String& objectName_ = "")
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
results(results_),
: results(results_),
parameterType(parameterType_),
objectName(objectName_){};
virtual ~ExpressionParameterSearcher(){};
@@ -73,22 +68,10 @@ 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 < metadata.parameters.size();
i < node.expressionMetadata.parameters.size();
++i) {
auto& parameterMetadata = metadata.parameters[i];
auto& parameterMetadata = node.expressionMetadata.parameters[i];
if (considerFunction && parameterMetadata.GetType() == parameterType) {
// Store the value of the parameter
results.insert(
@@ -101,10 +84,6 @@ 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.
@@ -186,18 +165,24 @@ std::set<gd::String> EventsVariablesFinder::FindArgumentsInInstructions(
}
// Search in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = instructions[aId].GetParameter(pNb).GetRootNode();
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", instructions[aId].GetParameter(pNb).GetPlainString());
ExpressionParameterSearcher searcher(
platform,
project,
layout,
results,
parameterType,
objectName);
results, parameterType, objectName);
node->Visit(searcher);
}
// Search in gd::String expressions
else if (ParameterMetadata::IsExpression(
"string", 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);
}
// Remember the value of the last "object" parameter.

View File

@@ -15,8 +15,6 @@
#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;
@@ -292,11 +290,7 @@ class GD_CORE_API ExpressionCompletionFinder
* and returns completions for it.
*/
static std::vector<ExpressionCompletionDescription>
GetCompletionDescriptionsFor(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String &rootType,
gd::ExpressionNode& node,
GetCompletionDescriptionsFor(gd::ExpressionNode& node,
size_t searchedPosition) {
gd::ExpressionNodeLocationFinder finder(searchedPosition);
node.Visit(finder);
@@ -309,7 +303,6 @@ 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();
@@ -327,21 +320,19 @@ 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(
type, "", searchedPosition + 1, searchedPosition + 1));
node.type, "", searchedPosition + 1, searchedPosition + 1));
completions.push_back(ExpressionCompletionDescription::ForExpression(
type, "", searchedPosition + 1, searchedPosition + 1));
node.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(
type, "", searchedPosition + 1, searchedPosition + 1));
node.type, "", searchedPosition + 1, searchedPosition + 1));
completions.push_back(ExpressionCompletionDescription::ForExpression(
type, "", searchedPosition + 1, searchedPosition + 1));
node.type, "", searchedPosition + 1, searchedPosition + 1));
}
void OnVisitNumberNode(NumberNode& node) override {
// No completions
@@ -353,7 +344,6 @@ 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) {
@@ -369,16 +359,15 @@ 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 <
metadata.parameters.size()) {
if (!metadata.parameters[metadataParameterIndex]
functionCall->expressionMetadata.parameters.size()) {
if (!functionCall->expressionMetadata.parameters[metadataParameterIndex]
.IsCodeOnly()) {
if (visibleParameterIndex == parameterIndex) {
parameterMetadata = &metadata.parameters[metadataParameterIndex];
parameterMetadata = &functionCall->expressionMetadata
.parameters[metadataParameterIndex];
}
visibleParameterIndex++;
}
@@ -409,21 +398,12 @@ 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(
type,
node.type,
node.name,
node.location.GetStartPosition(),
node.location.GetEndPosition(),
objectName));
node.objectName));
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
// No completions
@@ -433,69 +413,35 @@ class GD_CORE_API ExpressionCompletionFinder
// No completions
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
if (gd::ParameterMetadata::IsObject(node.type)) {
// Only show completions of objects if an object is required
completions.push_back(ExpressionCompletionDescription::ForObject(
type,
node.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,
} else {
// Show completions for expressions and objects otherwise.
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition(),
objectName));
} else {
// 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));
}
node.location.GetEndPosition()));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
}
}
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(
type,
node.type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
@@ -509,7 +455,7 @@ class GD_CORE_API ExpressionCompletionFinder
} else if (IsCaretOn(node.behaviorNameNamespaceSeparatorLocation) ||
IsCaretOn(node.behaviorFunctionNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForExpression(
type,
node.type,
node.behaviorFunctionName,
node.behaviorFunctionNameLocation.GetStartPosition(),
node.behaviorFunctionNameLocation.GetEndPosition(),
@@ -520,7 +466,7 @@ class GD_CORE_API ExpressionCompletionFinder
// Object function or behavior name
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
type,
node.type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
@@ -532,7 +478,7 @@ class GD_CORE_API ExpressionCompletionFinder
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
node.objectName));
completions.push_back(ExpressionCompletionDescription::ForExpression(
type,
node.type,
node.objectFunctionOrBehaviorName,
node.objectFunctionOrBehaviorNameLocation.GetStartPosition(),
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
@@ -541,7 +487,6 @@ 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);
@@ -549,7 +494,7 @@ class GD_CORE_API ExpressionCompletionFinder
// Behavior function
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
type,
node.type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
@@ -562,7 +507,7 @@ class GD_CORE_API ExpressionCompletionFinder
node.objectName));
} else {
completions.push_back(ExpressionCompletionDescription::ForExpression(
type,
node.type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition(),
@@ -574,7 +519,7 @@ class GD_CORE_API ExpressionCompletionFinder
// Object function
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
type,
node.type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
@@ -593,7 +538,7 @@ class GD_CORE_API ExpressionCompletionFinder
}
completions.push_back(ExpressionCompletionDescription::ForExpression(
type,
node.type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition(),
@@ -603,7 +548,7 @@ class GD_CORE_API ExpressionCompletionFinder
} else {
// Free function
completions.push_back(ExpressionCompletionDescription::ForExpression(
type,
node.type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition())
@@ -611,14 +556,13 @@ 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(
type,
node.type,
node.text,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
completions.push_back(ExpressionCompletionDescription::ForExpression(
type,
node.type,
node.text,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
@@ -634,27 +578,14 @@ class GD_CORE_API ExpressionCompletionFinder
(inclusive && searchedPosition <= location.GetEndPosition())));
}
ExpressionCompletionFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String &rootType_,
size_t searchedPosition_,
ExpressionCompletionFinder(size_t searchedPosition_,
gd::ExpressionNode* maybeParentNodeAtLocation_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
rootType(rootType_),
searchedPosition(searchedPosition_),
: 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

View File

@@ -1,123 +0,0 @@
/*
* 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

View File

@@ -1,35 +0,0 @@
/*
* 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

View File

@@ -1,198 +0,0 @@
/*
* 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

View File

@@ -1,300 +0,0 @@
/*
* 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

View File

@@ -10,10 +10,6 @@
#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;
@@ -32,27 +28,15 @@ namespace gd {
*/
class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
public:
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) {};
ExpressionValidator(){};
virtual ~ExpressionValidator(){};
/**
* \brief Helper function to check if a given node does not contain
* any error.
*/
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);
static bool HasNoErrors(gd::ExpressionNode& node) {
gd::ExpressionValidator validator;
node.Visit(validator);
return validator.GetErrors().empty();
}
@@ -72,247 +56,52 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
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;
ReportAnyError(node);
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);
}
childType = Type::Variable;
CheckType(parentType, childType, node.location);
if (node.child) node.child->Visit(*this);
}
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);
parentType = currentParentType;
if (node.child) {
node.child->Visit(*this);
}
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 {
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.");
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
}
RaiseTypeError(message, node.location);
childType = Type::Empty;
}
void OnVisitEmptyNode(EmptyNode& node) override { ReportAnyError(node); }
private:
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, Object, Empty};
Type ValidateFunction(const gd::FunctionCallNode& function);
void ReportAnyError(const ExpressionNode& node) {
void ReportAnyError(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

View File

@@ -1,172 +0,0 @@
/*
* 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

View File

@@ -11,6 +11,7 @@
#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"
@@ -145,9 +146,17 @@ bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
pNb < instruction.GetParametersCount();
++pNb) {
const gd::String& type = metadata.parameters[pNb].type;
const gd::Expression& expression = instruction.GetParameter(pNb);
const gd::String& expression =
instruction.GetParameter(pNb).GetPlainString();
auto node = expression.GetRootNode();
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>());
if (node) {
ExpressionParameterMover mover(GetGlobalObjectsContainer(),
GetObjectsContainer(),

View File

@@ -11,6 +11,7 @@
#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"
@@ -155,9 +156,18 @@ bool ExpressionsRenamer::DoVisitInstruction(gd::Instruction& instruction,
for (std::size_t pNb = 0; pNb < metadata.parameters.size() &&
pNb < instruction.GetParametersCount();
++pNb) {
const gd::Expression& expression = instruction.GetParameter(pNb);
const gd::String& type = metadata.parameters[pNb].type;
const gd::String& expression =
instruction.GetParameter(pNb).GetPlainString();
auto node = expression.GetRootNode();
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>());
if (node) {
ExpressionFunctionRenamer renamer(GetGlobalObjectsContainer(),
GetObjectsContainer(),

View File

@@ -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,12 +54,13 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
metadata.GetMetadata().GetParameter(i).GetType();
i++;
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);
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);
} else if (gd::ParameterMetadata::IsExpression("variable", parameterType))
usedExtensions.insert("BuiltinVariables");
}
@@ -112,8 +113,7 @@ void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
// Add extensions bound to Objects/Behaviors/Functions
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(project.GetCurrentPlatform(), GetGlobalObjectsContainer(), GetObjectsContainer(), rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
if (gd::ParameterMetadata::IsObject(node.type)) {
usedExtensions.insert(gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName)
.GetExtension()

View File

@@ -31,7 +31,6 @@ 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

View File

@@ -18,7 +18,7 @@
namespace gd {
void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
const gd::Project& project,
gd::Project& project,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer) {
@@ -36,7 +36,7 @@ void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
}
void EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
const gd::Project& project,
gd::Project& project,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,

View File

@@ -32,7 +32,7 @@ class GD_CORE_API EventsFunctionTools {
* generation for example.
*/
static void FreeEventsFunctionToObjectsContainer(
const gd::Project& project,
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(
const gd::Project& project,
gd::Project& project,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,

View File

@@ -126,10 +126,9 @@ class ResourceWorkerInEventsWorker : public ArbitraryEventsWorker {
instruction.GetParameters(),
metadata.GetParameters(),
[this, &instruction](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterExpression,
const gd::String& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const String& parameterValue = parameterExpression.GetPlainString();
if (parameterMetadata.GetType() ==
"police") { // Should be renamed fontResource
gd::String updatedParameterValue = parameterValue;

View File

@@ -54,8 +54,11 @@ Layout::Layout()
oglFOV(90.0f),
oglZNear(1.0f),
oglZFar(500.0f),
disableInputWhenNotFocused(true),
disableInputWhenNotFocused(true)
#if defined(GD_IDE_ONLY)
,
profiler(NULL)
#endif
{
gd::Layer layer;
layer.SetCameraCount(1);
@@ -329,6 +332,7 @@ void Layout::UnserializeFrom(gd::Project& project,
disableInputWhenNotFocused =
element.GetBoolAttribute("disableInputWhenNotFocused");
#if defined(GD_IDE_ONLY)
editorSettings.UnserializeFrom(
element.GetChild("uiSettings", 0, "UISettings"));
@@ -336,6 +340,7 @@ void Layout::UnserializeFrom(gd::Project& project,
element.GetChild("objectsGroups", 0, "GroupesObjets"));
gd::EventsListSerialization::UnserializeEventsFrom(
project, GetEvents(), element.GetChild("events", 0, "Events"));
#endif
UnserializeObjectsFrom(project, element.GetChild("objects", 0, "Objets"));
initialInstances.UnserializeFrom(
@@ -405,11 +410,13 @@ void Layout::Init(const Layout& other) {
std::unique_ptr<gd::BehaviorContent>(it.second->Clone());
}
#if defined(GD_IDE_ONLY)
events = other.events;
editorSettings = other.editorSettings;
objectGroups = other.objectGroups;
profiler = other.profiler;
#endif
}
std::vector<gd::String> GetHiddenLayers(const Layout& layout) {
@@ -423,6 +430,7 @@ std::vector<gd::String> GetHiddenLayers(const Layout& layout) {
return hiddenLayers;
}
#if defined(GD_IDE_ONLY)
gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::String name,
@@ -436,7 +444,7 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
type = project.GetObject(name).GetType();
// Search in groups
else if (searchInGroups) {
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
@@ -497,16 +505,18 @@ 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) {
const auto &object = layout.GetObject(i);
if (object.HasBehaviorNamed(name)) {
return object.GetBehavior(name).GetTypeName();
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();
}
}
for (std::size_t i = 0; i < project.GetObjectsCount(); ++i) {
const auto &object = project.GetObject(i);
if (object.HasBehaviorNamed(name)) {
return object.GetBehavior(name).GetTypeName();
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();
}
}
@@ -602,5 +612,6 @@ GetBehaviorsOfObject(const gd::ObjectsContainer& project,
return behaviors;
}
#endif
} // namespace gd

View File

@@ -12,7 +12,9 @@
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
#if defined(GD_IDE_ONLY)
#include "GDCore/Project/PropertyDescriptor.h"
#endif
namespace gd {
@@ -22,7 +24,6 @@ Object::Object(const gd::String& name_) : name(name_) {}
void Object::Init(const gd::Object& object) {
name = object.name;
assetStoreId = object.assetStoreId;
type = object.type;
objectVariables = object.objectVariables;
tags = object.tags;
@@ -79,12 +80,13 @@ gd::BehaviorContent& Object::AddBehavior(
return *behaviors[behaviorName];
}
#if defined(GD_IDE_ONLY)
std::map<gd::String, gd::PropertyDescriptor> Object::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
gd::BehaviorContent* Object::AddNewBehavior(const gd::Project& project,
gd::BehaviorContent* Object::AddNewBehavior(gd::Project& project,
const gd::String& type,
const gd::String& name) {
const gd::BehaviorMetadata& behaviorMetadata =
@@ -107,11 +109,11 @@ Object::GetInitialInstanceProperties(const gd::InitialInstance& instance,
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
#endif
void Object::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
type = element.GetStringAttribute("type");
assetStoreId = element.GetStringAttribute("assetStoreId");
name = element.GetStringAttribute("name", name, "nom");
tags = element.GetStringAttribute("tags");
@@ -182,9 +184,9 @@ void Object::UnserializeFrom(gd::Project& project,
DoUnserializeFrom(project, element);
}
#if defined(GD_IDE_ONLY)
void Object::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("assetStoreId", GetAssetStoreId());
element.SetAttribute("type", GetType());
element.SetAttribute("tags", GetTags());
objectVariables.SerializeTo(element.AddChild("variables"));
@@ -207,5 +209,6 @@ void Object::SerializeTo(SerializerElement& element) const {
DoSerializeTo(element);
}
#endif
} // namespace gd

View File

@@ -83,14 +83,6 @@ class GD_CORE_API Object {
*/
const gd::String& GetName() const { return name; };
/** \brief Change the asset store id of the object.
*/
void SetAssetStoreId(const gd::String& assetStoreId_) { assetStoreId = assetStoreId_; };
/** \brief Return the asset store id of the object.
*/
const gd::String& GetAssetStoreId() const { return assetStoreId; };
/** \brief Change the type of the object.
*/
void SetType(const gd::String& type_) { type = type_; }
@@ -108,6 +100,7 @@ class GD_CORE_API Object {
const gd::String& GetTags() const { return tags; }
///@}
#if defined(GD_IDE_ONLY)
/** \name Resources management
* Members functions related to managing resources used by the object
*/
@@ -193,6 +186,7 @@ class GD_CORE_API Object {
return false;
};
///@}
#endif
/** \name Behaviors management
* Members functions related to behaviors management.
@@ -238,6 +232,7 @@ class GD_CORE_API Object {
*/
gd::BehaviorContent& AddBehavior(const gd::BehaviorContent& behavior);
#if defined(GD_IDE_ONLY)
/**
* \brief Add the behavior of the specified \a type with the specified \a
* name.
@@ -247,9 +242,10 @@ class GD_CORE_API Object {
* \return A pointer to the newly added behavior content. NULL if the creation
* failed.
*/
gd::BehaviorContent* AddNewBehavior(const gd::Project& project,
gd::BehaviorContent* AddNewBehavior(gd::Project& project,
const gd::String& type,
const gd::String& name);
#endif
/**
* \brief Get a read-only access to the map containing the behaviors with
@@ -300,11 +296,13 @@ class GD_CORE_API Object {
* Members functions related to serialization of the object
*/
///@{
#if defined(GD_IDE_ONLY)
/**
* \brief Serialize the object.
* \see DoSerializeTo
*/
void SerializeTo(SerializerElement& element) const;
#endif
/**
* \brief Unserialize the object.
@@ -315,7 +313,6 @@ class GD_CORE_API Object {
protected:
gd::String name; ///< The full name of the object
gd::String assetStoreId; ///< The ID of the asset if the object comes from the store.
gd::String type; ///< Which type is the object. ( To test if we can do
///< something reserved to some objects with it )
std::map<gd::String, std::unique_ptr<gd::BehaviorContent>>
@@ -334,10 +331,12 @@ class GD_CORE_API Object {
virtual void DoUnserializeFrom(gd::Project& project,
const SerializerElement& element){};
#if defined(GD_IDE_ONLY)
/**
* \brief Derived objects can redefine this method to save custom attributes.
*/
virtual void DoSerializeTo(SerializerElement& element) const {};
#endif
/**
* Initialize object using another object. Used by copy-ctor and assign-op.

View File

@@ -76,7 +76,7 @@ std::size_t ObjectsContainer::GetObjectsCount() const {
return initialObjects.size();
}
#if defined(GD_IDE_ONLY)
gd::Object& ObjectsContainer::InsertNewObject(const gd::Project& project,
gd::Object& ObjectsContainer::InsertNewObject(gd::Project& project,
const gd::String& objectType,
const gd::String& name,
std::size_t position) {

View File

@@ -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(const gd::Project& project,
gd::Object& InsertNewObject(gd::Project& project,
const gd::String& objectType,
const gd::String& name,
std::size_t position);

View File

@@ -51,7 +51,6 @@ Project::Project()
: name(_("Project")),
version("1.0.0"),
packageName("com.example.gamename"),
templateSlug(""),
orientation("landscape"),
folderProject(false),
windowWidth(800),
@@ -517,7 +516,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
SetProjectUuid(propElement.GetStringAttribute("projectUuid", ""));
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
SetPackageName(propElement.GetStringAttribute("packageName"));
SetTemplateSlug(propElement.GetStringAttribute("templateSlug"));
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
SetLastCompilationDirectory(propElement
@@ -749,7 +747,6 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.SetAttribute("projectUuid", projectUuid);
propElement.SetAttribute("folderProject", folderProject);
propElement.SetAttribute("packageName", packageName);
propElement.SetAttribute("templateSlug", templateSlug);
propElement.SetAttribute("orientation", orientation);
platformSpecificAssets.SerializeTo(
propElement.AddChild("platformSpecificAssets"));
@@ -977,7 +974,6 @@ void Project::Init(const gd::Project& game) {
isPlayableWithGamepad = game.isPlayableWithGamepad;
isPlayableWithMobile = game.isPlayableWithMobile;
packageName = game.packageName;
templateSlug = game.templateSlug;
orientation = game.orientation;
folderProject = game.folderProject;
latestCompilationDirectory = game.latestCompilationDirectory;

View File

@@ -162,18 +162,6 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
const gd::String& GetPackageName() const { return packageName; }
/**
* \brief Change the slug of the template from which the project is created.
*/
void SetTemplateSlug(const gd::String& templateSlug_) {
templateSlug = templateSlug_;
};
/**
* \brief Get the slug of the template from which the project is created.
*/
const gd::String& GetTemplateSlug() const { return templateSlug; }
/**
* \brief Change the project orientation (in particular when exported with
* Cordova). This has no effect on desktop and web browsers. \param
@@ -995,8 +983,6 @@ class GD_CORE_API Project : public ObjectsContainer {
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
bool isPlayableWithMobile; ///< The project is playable on a mobile.
gd::String packageName; ///< Game package name
gd::String templateSlug; ///< The slug of the template from which the game is
///< created.
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
bool

View File

@@ -184,17 +184,11 @@ std::map<gd::String, gd::PropertyDescriptor> AudioResource::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[_("Preload as sound")]
.SetDescription(_("Loads the fully decoded file into cache, so it can be played right away as Sound with no further delays."))
.SetValue(preloadAsSound ? "true" : "false")
.SetType("Boolean");
properties[_("Preload as music")]
.SetDescription(_("Prepares the file for immediate streaming as Music (does not wait for complete download)."))
.SetValue(preloadAsMusic ? "true" : "false")
.SetType("Boolean");
properties[_("Preload in cache")]
.SetDescription(_("Loads the complete file into cache, but does not decode it into memory until requested."))
.SetValue(preloadInCache ? "true" : "false")
.SetType("Boolean");
return properties;
}
@@ -205,8 +199,6 @@ bool AudioResource::UpdateProperty(const gd::String& name,
preloadAsSound = value == "1";
else if (name == _("Preload as music"))
preloadAsMusic = value == "1";
else if (name == _("Preload in cache"))
preloadInCache = value == "1";
return true;
}
@@ -577,7 +569,6 @@ void AudioResource::UnserializeFrom(const SerializerElement& element) {
SetFile(element.GetStringAttribute("file"));
SetPreloadAsMusic(element.GetBoolAttribute("preloadAsMusic"));
SetPreloadAsSound(element.GetBoolAttribute("preloadAsSound"));
SetPreloadInCache(element.GetBoolAttribute("preloadInCache"));
}
void AudioResource::SerializeTo(SerializerElement& element) const {
@@ -585,7 +576,6 @@ void AudioResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("file", GetFile());
element.SetAttribute("preloadAsMusic", PreloadAsMusic());
element.SetAttribute("preloadAsSound", PreloadAsSound());
element.SetAttribute("preloadInCache", PreloadInCache());
}
void FontResource::SetFile(const gd::String& newFile) {

View File

@@ -60,7 +60,7 @@ class GD_CORE_API Resource {
* \see gd::Resource::GetFile
* \see gd::Resource::SetFile
*/
virtual bool UseFile() const { return false; }
virtual bool UseFile() { 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() const override { return true; }
virtual bool UseFile() override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
@@ -223,7 +223,7 @@ class GD_CORE_API ImageResource : public Resource {
*/
class GD_CORE_API AudioResource : public Resource {
public:
AudioResource() : Resource(), preloadAsMusic(false), preloadAsSound(false), preloadInCache(false) {
AudioResource() : Resource(), preloadAsMusic(false), preloadAsSound(false) {
SetKind("audio");
};
virtual ~AudioResource(){};
@@ -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() const override { return true; }
virtual bool UseFile() override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
@@ -263,21 +263,10 @@ class GD_CORE_API AudioResource : public Resource {
*/
void SetPreloadAsSound(bool enable = true) { preloadAsSound = enable; }
/**
* \brief Return true if the audio resource should be preloaded in cache (without decoding into memory).
*/
bool PreloadInCache() const { return preloadInCache; }
/**
* \brief Set if the audio resource should be preloaded in cache (without decoding into memory).
*/
void SetPreloadInCache(bool enable = true) { preloadInCache = enable; }
private:
gd::String file;
bool preloadAsSound;
bool preloadAsMusic;
bool preloadInCache;
};
/**
@@ -297,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() const override { return true; }
virtual bool UseFile() override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
@@ -323,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() const override { return true; }
virtual bool UseFile() override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
@@ -349,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() const override { return true; }
virtual bool UseFile() override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
@@ -390,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() const override { return true; }
virtual bool UseFile() override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;

View File

@@ -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;
}

View File

@@ -31,8 +31,8 @@ Extensions do have the same distinction between the "**IDE**" part and the "**Ru
In GDevelop, developers can associate and manipulate variables in their games. To represent them, we have two things:
- The **editor** `gd::Variable` that is part of the structure of the game, so living in [GDCore in Variable.h](https://docs.gdevelop.io/GDCore%20Documentation/classgd_1_1_variable.html). This is what is shown in the editor, saved on disk in the project file.
- The **game engine** variable, called `gdjs.Variable` in GDJS. [Documentation is in the GDJS **game engine**](https://docs.gdevelop.io/GDJS%20Runtime%20Documentation/classes/gdjs.Variable.html). This JavaScript class is what is used during a game.
- The **editor** `gd::Variable` that is part of the structure of the game, so living in [GDCore in Variable.h](https://docs.gdevelop-app.com/GDCore%20Documentation/classgd_1_1_variable.html). This is what is shown in the editor, saved on disk in the project file.
- The **game engine** variable, called `gdjs.Variable` in GDJS. [Documentation is in the GDJS **game engine**](https://docs.gdevelop-app.com/GDJS%20Runtime%20Documentation/Variable.html). This JavaScript class is what is used during a game.
The editor `gd::Variable` **knows nothing** about the game engine class `gdjs.Variable`. And the `gdjs.Variable` class in the game engine knows almost nothing from `gd::Variable` (apart from how it's written in JSON, to be able to load a game default variables).
@@ -56,10 +56,10 @@ The game engine is in GDJS/Runtime and is all written in TypeScript.
## What about events?
An "**event**" is by default something that [is mostly empty](https://docs.gdevelop.io/GDCore%20Documentation/classgd_1_1_base_event.html). In a more traditional programming language, an event can be seen as a scope or block (example: `{ some code here }` in a C style language like JavaScript, Java or C++).
An "**event**" is by default something that [is mostly empty](https://docs.gdevelop-app.com/GDCore%20Documentation/classgd_1_1_base_event.html). In a more traditional programming language, an event can be seen as a scope or block (example: `{ some code here }` in a C style language like JavaScript, Java or C++).
[Default events are defined](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Events/Builtin) in GDevelop Core.
In particular, there is StandardEvent, which has conditions and actions. Conditions and actions are both a list of [`gd::Instruction`](https://docs.gdevelop.io/GDCore%20Documentation/classgd_1_1_instruction.html). A `gd::Instruction` is either a condition or an action (it depends only on the context where they are used).
In particular, there is StandardEvent, which has conditions and actions. Conditions and actions are both a list of [`gd::Instruction`](https://docs.gdevelop-app.com/GDCore%20Documentation/classgd_1_1_instruction.html). A `gd::Instruction` is either a condition or an action (it depends only on the context where they are used).
A `gd::Instruction` is "just" a type (the name of the action or condition), and some parameters. You can think of it as a function in a programming language (`add(2, 3)` is calling a function called "add" with parameters "2" and "3"). Conditions have the special thing that they are functions returning true or false (and potentially being used to do a filter on the objects being picked for next conditions and actions).

View File

@@ -6,7 +6,7 @@ GDevelop Core is a portable C++ library, compiled to be used in JavaScript in th
## 1) Getting started 🤓
First, take a look at the _Readme.md_ at the root of the repository and the [developer documentation](https://docs.gdevelop.io/).
First, take a look at the _Readme.md_ at the root of the repository and the [developer documentation](https://docs.gdevelop-app.com/).
## 2) How to contribute 😎

View File

@@ -197,17 +197,6 @@ 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", "");

View File

@@ -32,7 +32,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
group.AddObject("MyOtherSpriteObject");
group.AddObject("MyFakeObjectWithUnsupportedCapability");
gd::ExpressionParser2 parser;
gd::ExpressionParser2 parser(platform, project, layout1);
unsigned int maxDepth = 0;
gd::EventsCodeGenerationContext context(&maxDepth);
@@ -40,10 +40,8 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("Valid text generation") {
{
auto node = parser.ParseExpression("\"hello world\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
auto node = parser.ParseExpression("string", "\"hello world\"");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -51,10 +49,8 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello world\"");
}
{
auto node = parser.ParseExpression("\"hello\" + \"world\" ");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
auto node = parser.ParseExpression("string", "\"hello\" + \"world\" ");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -63,10 +59,8 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
}
{
auto node = parser.ParseExpression(
"\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
"string", "\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\"");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -78,10 +72,8 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("Valid number generation") {
{
auto node = parser.ParseExpression("12.45");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
auto node = parser.ParseExpression("number", "12.45");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -93,10 +85,8 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("Invalid operators generation") {
// TODO: Should any error return directly 0 or ""?
{
auto node = parser.ParseExpression("12.45 +");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
auto node = parser.ParseExpression("number", "12.45 +");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -104,10 +94,8 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() == "12.45 + 0");
}
{
auto node = parser.ParseExpression("12.45 * *");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
auto node = parser.ParseExpression("number", "12.45 * *");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -118,20 +106,16 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("Valid unary operator generation") {
{
auto node = parser.ParseExpression("-12.45");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
auto node = parser.ParseExpression("number", "-12.45");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "-(12.45)");
}
{
auto node = parser.ParseExpression("12.5 + -2. / (.3)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
auto node = parser.ParseExpression("number", "12.5 + -2. / (.3)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
@@ -140,24 +124,20 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
}
SECTION("Valid function calls") {
SECTION("without parameter") {
{
auto node =
parser.ParseExpression(" 1 / MyExtension::GetNumber()");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
parser.ParseExpression("number", " 1 / MyExtension::GetNumber()");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "1 / getNumber()");
}
SECTION("number and string parameters") {
{
auto node = parser.ParseExpression(
"MyExtension::GetNumberWith2Params(12, \"hello world\")");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
"number", "MyExtension::GetNumberWith2Params(12, \"hello world\")");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -165,13 +145,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getNumberWith2Params(12, \"hello world\")");
}
SECTION("nested function call") {
{
auto node =
parser.ParseExpression("MyExtension::GetNumberWith2Params("
parser.ParseExpression("number",
"MyExtension::GetNumberWith2Params("
"MyExtension::GetNumber(), \"hello world\")");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -179,12 +158,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getNumberWith2Params(getNumber(), \"hello world\")");
}
SECTION("object function") {
{
auto node =
parser.ParseExpression("MySpriteObject.GetObjectNumber()");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
parser.ParseExpression("number", "MySpriteObject.GetObjectNumber()");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -192,12 +169,11 @@ 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("string",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -207,11 +183,22 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
}
}
SECTION("Valid function calls with optional arguments") {
SECTION("with optional parameter set") {
auto node = parser.ParseExpression("MyExtension::MouseX(\"layer1\",2+2)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
{
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,
context);
REQUIRE(node);
@@ -220,39 +207,13 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"getMouseX(\"\", \"layer1\", 2 + 2)");
// (first argument is the currentScene)
}
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,
}
SECTION(
"Valid function calls (deprecated way of specifying optional "
"arguments)") {
{
auto node = parser.ParseExpression("number", "MyExtension::MouseX(,)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -262,17 +223,15 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
// (first argument is the currentScene)
}
}
SECTION("Invalid function calls") {
SECTION("unknown identifier in parameters") {
{
auto node =
parser.ParseExpression("MySpriteObject.GetObjectStringWith3Param("
parser.ParseExpression("string",
"MySpriteObject.GetObjectStringWith3Param("
"MySpriteObject.GetObjectNumber() / 2.3, "
"MySpriteObject.GetObjectStringWith1Param("
"MyExtension::GetNumber()), test)");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -284,38 +243,33 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"/* Error during generation, unrecognized identifier type: "
"unknown with value test */ \"test\") ?? \"\"");
}
SECTION("missing parameter") {
{
auto node = parser.ParseExpression(
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
{
auto node = parser.ParseExpression(
"number",
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
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");
}
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getNumberWith2Params(getNumber(), /* Error during generation, "
"parameter not existing in the nodes */ \"\")");
}
SECTION("unknown function") {
auto node = parser.ParseExpression("MyExtension::Idontexist()");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
{
// 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,
context);
REQUIRE(node);
@@ -324,12 +278,11 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"/* Error during generation, function not found: "
"MyExtension::Idontexist */ 0");
}
SECTION("too much parameters") {
auto node = parser.ParseExpression("MyExtension::GetNumberWith2Params(1, "
{
auto node = parser.ParseExpression("number",
"MyExtension::GetNumberWith2Params(1, "
"\"2\", MyExtension::GetNumber())");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -338,14 +291,13 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"getNumberWith2Params(1, \"2\")");
}
}
SECTION("function calls (capabilities)") {
SECTION("supported capability") {
SECTION("Invalid function calls (capabilities)") {
{
// Capability is supported, so the expression is valid.
auto node = parser.ParseExpression(
"string",
"MySpriteObject.GetSomethingRequiringEffectCapability(123)");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -354,29 +306,26 @@ 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("MyFakeObjectWithUnsupportedCapability."
parser.ParseExpression("string",
"MyFakeObjectWithUnsupportedCapability."
"GetSomethingRequiringEffectCapability(123)");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(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(
"AllObjects.GetSomethingRequiringEffectCapability(123)");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
"string", "AllObjects.GetSomethingRequiringEffectCapability(123)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -387,87 +336,51 @@ 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") {
SECTION("empty variable") {
{
// Test an empty expression
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "") == "fakeBadVariable");
}
SECTION("only an unary operator") {
{
// Test a unary operator
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "objectvar", "-") ==
"fakeBadVariable");
}
SECTION("only a binary operator") {
{
// Test an operator
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "globalvar", "/") ==
"fakeBadVariable");
}
}
SECTION("Invalid variables, using operators") {
SECTION("unary operation") {
{
// Test a unary operator
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "objectvar", "-(var1)") ==
"fakeBadVariable");
}
SECTION("binary operation") {
{
// Test an operator
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "globalvar", "var1+var2") ==
"fakeBadVariable");
}
SECTION("multiple operation") {
{
// Test multiple operators
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(
"MyExtension::GetVariableAsNumber(myVariable)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
"number", "MyExtension::GetVariableAsNumber(myVariable)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -475,12 +388,11 @@ 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("number",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -488,76 +400,13 @@ 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("number",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -566,13 +415,12 @@ 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("number",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -581,14 +429,13 @@ 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("number",
"",
codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
@@ -615,10 +462,8 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
}
SECTION("Mixed test (1)") {
{
auto node = parser.ParseExpression("-+-MyExtension::MouseX(,)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
auto node = parser.ParseExpression("number", "-+-MyExtension::MouseX(,)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);

View File

@@ -19,17 +19,16 @@ 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;
gd::ExpressionParser2 parser(platform, project, layout1);
auto getCompletionsFor = [&](const gd::String& type,
const gd::String& expression,
size_t location) {
auto node = parser.ParseExpression(expression);
auto node = parser.ParseExpression(type, expression);
REQUIRE(node != nullptr);
return gd::ExpressionCompletionFinder::GetCompletionDescriptionsFor(
platform, project, layout1, type, *node, location);
*node, location);
};
const std::vector<gd::ExpressionCompletionDescription>
@@ -67,24 +66,6 @@ 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)};
@@ -162,17 +143,6 @@ 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(

View File

@@ -14,9 +14,10 @@
template <class TNode>
bool CheckNodeAtLocationIs(gd::ExpressionParser2& parser,
const gd::String& type,
const gd::String& expression,
size_t searchPosition) {
auto node = parser.ParseExpression(expression);
auto node = parser.ParseExpression(type, expression);
REQUIRE(node != nullptr);
return dynamic_cast<TNode*>(
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(
@@ -25,9 +26,10 @@ 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(expression);
auto node = parser.ParseExpression(type, expression);
REQUIRE(node != nullptr);
return dynamic_cast<TNode*>(
gd::ExpressionNodeLocationFinder::GetParentNodeAtPosition(
@@ -35,18 +37,20 @@ 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(expression);
auto node = parser.ParseExpression(type, 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(expression);
auto node = parser.ParseExpression(type, expression);
REQUIRE(node != nullptr);
return gd::ExpressionNodeLocationFinder::GetParentNodeAtPosition(
*node, searchPosition) == nullptr;
@@ -59,21 +63,21 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
auto& layout1 = project.InsertNewLayout("Layout1", 0);
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
gd::ExpressionParser2 parser;
gd::ExpressionParser2 parser(platform, project, layout1);
SECTION("Empty expressions") {
SECTION("Test 1") {
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
parser, "", 0) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "", 1) ==
parser, "string", "", 0) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "string", "", 1) ==
true);
}
SECTION("Test 2") {
REQUIRE(CheckNoNodeAtLocation(parser, " ", 0) ==
REQUIRE(CheckNoNodeAtLocation(parser, "string", " ", 0) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
parser, " ", 1) == true);
REQUIRE(CheckNoNodeAtLocation(parser, " ", 2) ==
parser, "string", " ", 1) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "string", " ", 2) ==
true);
}
}
@@ -81,255 +85,270 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
SECTION("Valid text") {
SECTION("Test 1") {
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "\"Hello world\"", 0) == true);
parser, "string", "\"Hello world\"", 0) == true);
}
SECTION("Test 2") {
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "\"Hello world\"", 1) == true);
parser, "string", "\"Hello world\"", 1) == true);
}
SECTION("Test 3") {
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "\"Hello world\"", 12) == true);
parser, "string", "\"Hello world\"", 12) == true);
}
SECTION("Test 4") {
REQUIRE(CheckNoNodeAtLocation(parser, "\"Hello world\"", 13) ==
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"Hello world\"", 13) ==
true);
}
SECTION("Test 5") {
REQUIRE(CheckNoNodeAtLocation(parser, "\"Hello world\"", 99) ==
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"Hello world\"", 99) ==
true);
}
}
SECTION("Valid text operators") {
SECTION("Test 1") {
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "\"Hello \" + \"World\"", 1) == true);
parser, "string", "\"Hello \" + \"World\"", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "\"Hello \" + \"World\"", 8) == true);
parser, "string", "\"Hello \" + \"World\"", 8) == true);
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "\"Hello \" + \"World\"", 15) == true);
parser, "string", "\"Hello \" + \"World\"", 15) == true);
}
}
SECTION("Invalid texts") {
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "\"", 0) ==
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "string", "\"", 0) ==
true);
REQUIRE(CheckNoNodeAtLocation(parser, "\"", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "\"a", 1) ==
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "string", "\"a", 1) ==
true);
}
SECTION("Invalid parenthesis") {
REQUIRE(CheckNodeAtLocationIs<gd::SubExpressionNode>(
parser, "((\"hello\"", 1) == true);
parser, "string", "((\"hello\"", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "((\"hello\"", 2) == true);
parser, "string", "((\"hello\"", 2) == true);
}
SECTION("Invalid text operators") {
SECTION("Test 1") {
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "\"Hello \" - \"World\"", 0) == true);
parser, "string", "\"Hello \" - \"World\"", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "\"Hello \" - \"World\"", 1) == true);
parser, "string", "\"Hello \" - \"World\"", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "\"Hello \" / \"World\"", 8) == true);
parser, "string", "\"Hello \" / \"World\"", 8) == true);
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
parser, "\"Hello \" * \"World\"", 15) == true);
parser, "string", "\"Hello \" * \"World\"", 15) == true);
}
}
SECTION("Valid unary operators") {
SECTION("Test 1") {
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
parser, "-123", 0) == true);
parser, "number", "-123", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
parser, "+123", 0) == true);
parser, "number", "+123", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "-123", 1) == true);
parser, "number", "-123", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "-123", 2) == true);
parser, "number", "-123", 2) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "-123", 3) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "-123", 4) == true);
parser, "number", "-123", 3) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "number", "-123", 4) == true);
}
SECTION("Test 2") {
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
parser, "-+-123", 0) == true);
parser, "number", "-+-123", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
parser, "-+-123", 1) == true);
parser, "number", "-+-123", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
parser, "-+-123", 2) == true);
parser, "number", "-+-123", 2) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "-+-123", 3) == true);
parser, "number", "-+-123", 3) == true);
}
}
SECTION("Invalid number operators") {
SECTION("Test 1") {
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "12 ! 34", 0) == true);
parser, "number", "12 ! 34", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "12 ! 34", 1) == true);
parser, "number", "12 ! 34", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "12 ! 34", 2) == true);
parser, "number", "12 ! 34", 2) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "12 ! 34", 3) == true);
parser, "number", "12 ! 34", 3) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "12 ! 34", 4) == true);
parser, "number", "12 ! 34", 4) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "12 ! 34", 5) == true);
parser, "number", "12 ! 34", 5) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "12 ! 34", 6) == true);
parser, "number", "12 ! 34", 6) == true);
}
SECTION("Test 2") {
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "1 / /2", 0) == true);
parser, "number", "1 / /2", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "1 / /2", 1) == true);
parser, "number", "1 / /2", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "1 / /2", 2) == true);
parser, "number", "1 / /2", 2) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "1 / /2", 3) == true);
parser, "number", "1 / /2", 3) == true);
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
parser, "1 / /2", 4) == true);
parser, "number", "1 / /2", 4) == true);
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
parser, "1 / /2", 5) == true);
parser, "number", "1 / /2", 5) == true);
}
}
SECTION("Numbers and texts mismatchs") {
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);
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);
}
SECTION("Numbers and texts mismatchs (parent node)") {
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);
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);
}
SECTION("Valid objects") {
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "HelloWorld1", 0) == true);
parser, "object", "HelloWorld1", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "HelloWorld1", 1) == true);
parser, "object", "HelloWorld1", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "HelloWorld1", 10) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "HelloWorld1", 11) == true);
parser, "object", "HelloWorld1", 10) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "object", "HelloWorld1", 11) == true);
}
SECTION("Valid objects (parent node)") {
REQUIRE(CheckNoParentNodeAtLocation(
parser, "HelloWorld1", 0) == true);
parser, "object", "HelloWorld1", 0) == true);
}
SECTION("Invalid objects") {
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "a+b", 0) == true);
parser, "object", "a+b", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "a+b", 1) == true);
parser, "object", "a+b", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "a+b", 2) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "a+b", 3) == true);
parser, "object", "a+b", 2) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "object", "a+b", 3) == true);
}
SECTION("Valid function calls") {
SECTION("Test 1") {
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "12 + MyExtension::GetNumber()", 0) ==
parser, "number", "12 + MyExtension::GetNumber()", 0) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "12 + MyExtension::GetNumber()", 1) ==
parser, "number", "12 + MyExtension::GetNumber()", 1) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "12 + MyExtension::GetNumber()", 2) ==
parser, "number", "12 + MyExtension::GetNumber()", 2) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "12 + MyExtension::GetNumber()", 3) ==
parser, "number", "12 + MyExtension::GetNumber()", 3) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
parser, "12 + MyExtension::GetNumber()", 4) ==
parser, "number", "12 + MyExtension::GetNumber()", 4) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "12 + MyExtension::GetNumber()", 5) ==
parser, "number", "12 + MyExtension::GetNumber()", 5) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "12 + MyExtension::GetNumber()", 27) ==
parser, "number", "12 + MyExtension::GetNumber()", 27) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "12 + MyExtension::GetNumber()", 28) ==
parser, "number", "12 + MyExtension::GetNumber()", 28) ==
true);
REQUIRE(CheckNoNodeAtLocation(
parser, "12 + MyExtension::GetNumber()", 29) ==
parser, "number", "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, "12 + MyExtension::GetNumber()", 0) ==
parser, "number", "12 + MyExtension::GetNumber()", 0) ==
true);
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(
parser, "12 + MyExtension::GetNumber()", 6) ==
parser, "number", "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);
}
@@ -337,67 +356,66 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
SECTION("Invalid function calls") {
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(12)", 0) == true);
parser, "number", "Idontexist(12)", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(12)", 1) == true);
parser, "number", "Idontexist(12)", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(12)", 2) == true);
parser, "number", "Idontexist(12)", 2) == true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(12)", 10) == true);
parser, "number", "Idontexist(12)", 10) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "Idontexist(12)", 11) == true);
parser, "number", "Idontexist(12)", 11) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "Idontexist(12)", 12) == true);
parser, "number", "Idontexist(12)", 12) == true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(12)", 13) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "Idontexist(12)", 14) ==
parser, "number", "Idontexist(12)", 13) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "number", "Idontexist(12)", 14) ==
true);
}
SECTION("Invalid function calls (parent node)") {
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "Idontexist(12)", 12) == true);
parser, "number", "Idontexist(12)", 12) == true);
REQUIRE(CheckParentNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(12)", 12) == true);
parser, "number", "Idontexist(12)", 12) == true);
}
SECTION("Unterminated function calls") {
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(", 0) == true);
parser, "number", "Idontexist(", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(", 1) == true);
parser, "number", "Idontexist(", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
parser, "Idontexist(", 10) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "Idontexist(", 11) == true);
parser, "number", "Idontexist(", 10) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "number", "Idontexist(", 11) == true);
}
SECTION("Valid variables") {
SECTION("Test 1") {
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "myVariable", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "myVariable", 9) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "myVariable", 10) ==
REQUIRE(CheckNodeAtLocationIs<gd::VariableNode>(
parser, "scenevar", "myVariable", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::VariableNode>(
parser, "scenevar", "myVariable", 9) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "scenevar", "myVariable", 10) ==
true);
}
SECTION("Test 2") {
auto node = parser.ParseExpression("Var1.Child1");
auto node = parser.ParseExpression("scenevar", "Var1.Child1");
REQUIRE(node != nullptr);
auto var1Node =
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 0);
REQUIRE(dynamic_cast<gd::IdentifierNode*>(var1Node) != nullptr);
REQUIRE(dynamic_cast<gd::IdentifierNode&>(*var1Node).identifierName == "Var1");
REQUIRE(dynamic_cast<gd::VariableNode*>(var1Node) != nullptr);
REQUIRE(dynamic_cast<gd::VariableNode&>(*var1Node).name == "Var1");
// It's actually the same node.
auto child1Node =
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 4);
REQUIRE(dynamic_cast<gd::IdentifierNode*>(child1Node) != nullptr);
REQUIRE(dynamic_cast<gd::IdentifierNode&>(*child1Node).childIdentifierName ==
REQUIRE(dynamic_cast<gd::VariableAccessorNode*>(child1Node) != nullptr);
REQUIRE(dynamic_cast<gd::VariableAccessorNode&>(*child1Node).name ==
"Child1");
}
SECTION("Test 3") {
auto node = parser.ParseExpression(
"myVariable[ \"My named children\" ].grandChild");
"scenevar", "myVariable[ \"My named children\" ].grandChild");
REQUIRE(node != nullptr);
auto myVariableNode =

File diff suppressed because it is too large Load Diff

View File

@@ -21,15 +21,13 @@ TEST_CASE("ExpressionParser2 - Benchmarks", "[common][events]") {
auto &layout1 = project.InsertNewLayout("Layout1", 0);
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
gd::ExpressionParser2 parser;
gd::ExpressionParser2 parser(platform, project, layout1);
auto parseExpression = [&parser, &project, &platform, &layout1](const gd::String &expression) {
auto parseExpressionWithType = [&parser, &project, &platform, &layout1,
auto parseExpression = [&parser](const gd::String &expression) {
auto parseExpressionWithType = [&parser,
&expression](const gd::String &type) {
auto node = parser.ParseExpression(expression);
auto node = parser.ParseExpression(type, expression);
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, project, layout1, type);
node->Visit(validator);
};
parseExpressionWithType("number");

View File

@@ -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;
gd::ExpressionParser2 parser(platform, project, layout1);
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(line.c_str());
auto node1 = parser.ParseExpression("string", line.c_str());
REQUIRE(node1 != nullptr);
auto node2 = parser.ParseExpression(line.c_str());
auto node2 = parser.ParseExpression("number", line.c_str());
REQUIRE(node2 != nullptr);
count++;

View File

@@ -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;
gd::ExpressionParser2 parser(platform, project, layout1);
auto testPrinter = [&parser](const gd::String &type,
const gd::String &expression,
const gd::String &expectedOutput = "") {
auto node = parser.ParseExpression(expression);
auto node = parser.ParseExpression(type, expression);
REQUIRE(node != nullptr);
gd::ExpressionParser2NodePrinter printer;
node->Visit(printer);

2
ExtLibs/Readme.md Normal file
View File

@@ -0,0 +1,2 @@
Uncompress here the external libraries needed: SFML.
Refer to [the documentation](https://docs.gdevelop-app.com/GDCore%20Documentation/setup_dev_env.html) for more information and to get the download links of the libraries.

View File

@@ -19,7 +19,8 @@ namespace gdjs {
constructor(runtimeScene, behaviorData, owner) {
super(runtimeScene, behaviorData, owner);
this._relativeToOriginalWindowSize = !!behaviorData.relativeToOriginalWindowSize;
this._relativeToOriginalWindowSize =
!!behaviorData.relativeToOriginalWindowSize;
this._leftEdgeAnchor = behaviorData.leftEdgeAnchor;
this._rightEdgeAnchor = behaviorData.rightEdgeAnchor;
this._topEdgeAnchor = behaviorData.topEdgeAnchor;

View File

@@ -1,5 +1,5 @@
// @ts-check
describe('gdjs.AnchorRuntimeBehavior', function () {
describe.only('gdjs.AnchorRuntimeBehavior', function () {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },

View File

@@ -95,10 +95,11 @@ namespace gdjs {
}
updateFontFamily(): void {
this._pixiObject.textStyles.default.fontFamily = this._object._runtimeScene
.getGame()
.getFontManager()
.getFontFamily(this._object._fontFamily);
this._pixiObject.textStyles.default.fontFamily =
this._object._runtimeScene
.getGame()
.getFontManager()
.getFontFamily(this._object._fontFamily);
this._pixiObject.dirty = true;
}

View File

@@ -169,5 +169,6 @@ namespace gdjs {
return this._pixiObject.textHeight * this.getScale();
}
}
export const BitmapTextRuntimeObjectRenderer = BitmapTextRuntimeObjectPixiRenderer;
export const BitmapTextRuntimeObjectRenderer =
BitmapTextRuntimeObjectPixiRenderer;
}

View File

@@ -34,6 +34,7 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
std::make_shared<DestroyOutsideBehavior>(),
std::shared_ptr<gd::BehaviorsSharedData>());
#if defined(GD_IDE_ONLY)
aut.AddCondition("ExtraBorder",
_("Additional border"),
_("Compare the additional border that the object must cross "
@@ -46,7 +47,8 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "DestroyOutside")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetExtraBorder");
.SetFunctionName("GetExtraBorder")
.SetIncludeFile("DestroyOutsideBehavior/DestroyOutsideRuntimeBehavior.h");
aut.AddAction("ExtraBorder",
_("Additional border"),
@@ -61,5 +63,7 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("SetExtraBorder")
.SetGetter("GetExtraBorder");
.SetGetter("GetExtraBorder")
.SetIncludeFile("DestroyOutsideBehavior/DestroyOutsideRuntimeBehavior.h");
#endif
}

View File

@@ -414,9 +414,8 @@ namespace gdjs {
return;
}
if (this.dialogueData.select) {
this.selectedOption = gdjs.dialogueTree._normalizedOptionIndex(
optionIndex
);
this.selectedOption =
gdjs.dialogueTree._normalizedOptionIndex(optionIndex);
this.selectedOptionUpdated = true;
}
};

View File

@@ -52,9 +52,8 @@ namespace gdjs {
const inputManager = runtimeScene.getGame().getInputManager();
//Try mouse
const mouseDraggableManager = DraggableManager.getMouseManager(
runtimeScene
);
const mouseDraggableManager =
DraggableManager.getMouseManager(runtimeScene);
if (
inputManager.isMouseButtonPressed(0) &&
!mouseDraggableManager.isDragging(this)
@@ -107,9 +106,8 @@ namespace gdjs {
}
doStepPostEvents(runtimeScene) {
const mouseDraggableManager = DraggableManager.getMouseManager(
runtimeScene
);
const mouseDraggableManager =
DraggableManager.getMouseManager(runtimeScene);
mouseDraggableManager.leftPressedLastFrame = runtimeScene
.getGame()
.getInputManager()
@@ -174,9 +172,8 @@ namespace gdjs {
if (!runtimeScene.touchDraggableManagers[touchId]) {
//Create the shared manager if necessary.
// @ts-ignore
runtimeScene.touchDraggableManagers[
touchId
] = new TouchDraggableManager(runtimeScene, touchId);
runtimeScene.touchDraggableManagers[touchId] =
new TouchDraggableManager(runtimeScene, touchId);
}
// @ts-ignore
return runtimeScene.touchDraggableManagers[touchId];

View File

@@ -6,7 +6,8 @@ namespace gdjs {
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
const adjustmentFilter =
filter as unknown as PIXI.filters.AdjustmentFilter;
if (parameterName === 'gamma') {
adjustmentFilter.gamma = value;
} else if (parameterName === 'saturation') {

View File

@@ -6,7 +6,8 @@ namespace gdjs {
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
const advancedBloomFilter =
filter as unknown as PIXI.filters.AdvancedBloomFilter;
if (parameterName === 'threshold') {
advancedBloomFilter.threshold = value;
} else if (parameterName === 'bloomScale') {

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
const asciiFilter = filter as unknown as PIXI.filters.AsciiFilter;
if (parameterName === 'size') {
asciiFilter.size = value;
}

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter;
if (parameterName === 'rotation') {
bevelFilter.rotation = value;
} else if (parameterName === 'thickness') {
@@ -21,16 +21,14 @@ namespace gdjs {
}
},
updateStringParameter: function (filter, parameterName, value) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
bevelFilter.lightColor =
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
}
if (parameterName === 'shadowColor') {
bevelFilter.shadowColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
bevelFilter.shadowColor =
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
}
},
updateBooleanParameter: function (filter, parameterName, value) {},

View File

@@ -9,7 +9,7 @@ namespace gdjs {
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const colorMatrix = (filter as unknown) as PIXI.filters.ColorMatrixFilter;
const colorMatrix = filter as unknown as PIXI.filters.ColorMatrixFilter;
if (parameterName !== 'opacity') {
return;
}

View File

@@ -8,7 +8,7 @@ namespace gdjs {
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const blendingModeFilter = (filter as unknown) as PIXI.filters.AlphaFilter;
const blendingModeFilter = filter as unknown as PIXI.filters.AlphaFilter;
if (parameterName === 'alpha') {
blendingModeFilter.alpha = value;
} else if (parameterName === 'blendmode') {

View File

@@ -9,7 +9,8 @@ namespace gdjs {
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const brightnessFilter = (filter as unknown) as PIXI.filters.ColorMatrixFilter;
const brightnessFilter =
filter as unknown as PIXI.filters.ColorMatrixFilter;
if (parameterName !== 'brightness') {
return;
}

View File

@@ -6,7 +6,8 @@ namespace gdjs {
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
const bulgePinchFilter =
filter as unknown as PIXI.filters.BulgePinchFilter;
if (parameterName === 'centerX') {
bulgePinchFilter.center[0] = value;
} else if (parameterName === 'centerY') {

View File

@@ -19,7 +19,7 @@ namespace gdjs {
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
const colorMapFilter = filter as unknown as PIXI.filters.ColorMapFilter;
if (parameterName === 'mix') {
colorMapFilter.mix = gdjs.PixiFiltersTools.clampValue(
value / 100,
@@ -30,7 +30,7 @@ namespace gdjs {
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
const colorMapFilter = filter as unknown as PIXI.filters.ColorMapFilter;
if (parameterName === 'nearest') {
colorMapFilter.nearest = value;
}

Some files were not shown because too many files have changed in this diff Show More