Compare commits

..

49 Commits

Author SHA1 Message Date
Fannie Yan
06adf9070d Revert changes on cpp file 2022-06-10 12:00:10 +02:00
Fannie Yan
0747129f4f Display all expressions types with preference activated 2022-06-10 11:46:34 +02:00
Fannie Yan
62be7a07db Revert c++ changes 2022-06-09 16:59:46 +02:00
Fannie Yan
0414c58fbf Show all expressions if preference is toggled 2022-06-09 15:27:15 +02:00
Fannie Yan
5c0c48dd05 Add preference toggle tu use all expression types 2022-06-09 15:26:15 +02:00
Fannie Yan
c8a8ef6cbc Add optional parameter to ignore current node type 2022-06-09 15:13:44 +02:00
Sebastian Krzyszkowiak
b0ac0b1254 Fix music files staying muted after loading after upgrading to Howler.js 2.2.3 (#4011)
* This is caused by a bug in recent versions of Howler.js. Since there's no fix in Howler yet, this change reverts the problematic commit (0323af9b84) on top of v2.2.3 release.
* See https://github.com/goldfire/howler.js/issues/1603 for more details.

Only show in developer changelog
2022-06-09 10:19:42 +02:00
Florian Rival
e8b34ca535 Fix camera actions/conditions/expressions broken since introducing new expressions (#4012)
Removed some logs so that it's easier to spot errors reported.

Don't show in changelog
2022-06-09 10:09:14 +02:00
Clément Pasteau
70226f45aa Rework Asset Pack add to a list of filtered assets
Do not show in changelog
2022-06-08 17:10:58 +02:00
Clément Pasteau
b1572102c7 Bump IDE to 5.0.136
Do not show in changelog
2022-06-08 15:10:49 +02:00
Sebastian Krzyszkowiak
63da874469 Preload sounds sequentially to avoid overloading the browser
- Trying to preload all sounds at once can overwhelm the browser
in case of projects with many big sound files. Load the files
sequentially, the same way images are being preloaded.
2022-06-08 15:02:54 +02:00
Sebastian Krzyszkowiak
0963f2b8f6 Fix sounds being looped correctly on Firefox
- Upgrading Howler.js to v2.2.3 fixes sounds not being looped under recent Firefox versions (see https://github.com/goldfire/howler.js/issues/1442)
2022-06-08 15:00:01 +02:00
github-actions[bot]
5aa4fd5739 Update translations [skip ci] (#3924) 2022-06-08 14:57:29 +02:00
Oxey405
11c0248df5 Added an option to remove all unused resources 2022-06-08 14:53:11 +02:00
Fannie Yan
5bdc9769df Add camera borders expressions and conditions (#3999)
* Add CameraBorderLeft, CameraBorderRight, CameraBorderTop and CameraBorderBottom expressions and conditions
* Add CameraCenterX and CameraCenterY expressions and hide CameraX and CameraY expressions
2022-06-08 10:01:45 +02:00
Clément Pasteau
998ea7aa40 improve small things for Asset store and Events drag and drop (#3993)
* Hide Cancel action while installing

* Reset Pack Install disabled status on homepage

* Remove pointer from SelectField

* Create Chip component to harmonize cursor behavior to default

* Fix InlineCheckbox random cursors

* Improve drop indicator style

* Fix chip delete icon hover cursor

* Change drop indicator for multiple themes to be more visible
2022-06-07 10:32:42 +02:00
Fannie Yan
124003b4ac Save template slug in project (#3988)
only show in developer changelog
2022-06-07 10:26:30 +02:00
Florian Rival
96435530cd Set search bars to be rounded in the modern theme (#3994)
* Search bar have rounding and margins in most dialogs
* In lists, they stay "integrated" (no rounding, no margins)
* Also fix an icon that was too big

Don't show in changelog
2022-06-06 14:42:34 +02:00
D8H
8013ff0d7d Show the tag name as the asset page title (#3992)
Don't show in changelog
2022-06-06 10:43:59 +02:00
D8H
763aa0aa25 Show expression errors directly in the event sheet (#3823) 2022-06-03 23:02:51 +02:00
D8H
46dfb57859 Allow to go back to previous asset pages (#3986) 2022-06-03 19:16:35 +02:00
Fannie Yan
186eb4fcf0 Fix undo and redo in events sheet (#3981)
* Scroll to last change
* Select all events where a change occured
2022-06-03 16:28:27 +02:00
Florian Rival
c78c5def05 Add support for displaying external (optionally, paid) asset packs in the Asset Store (#3987)
* If you're an author of a paid asset pack, feel free to reach out to the GDevelop team on Discord or on the forum to get your pack listed!
2022-06-03 16:20:08 +02:00
Clément Pasteau
d694458fae Update all dialogs to have a way to close, except the ones with significant change (#3985) 2022-06-03 15:20:21 +02:00
D8H
74d6e51a2d Give asset suggestions after selecting an asset in the store 2022-06-03 15:12:31 +02:00
AlexandreS
ca5c3a27b0 Fix leaderboard reset action that used to timeout 2022-06-03 11:05:27 +02:00
Clément Pasteau
9ccdd2a0fa Give the possibility to add an asset pack as a whole
* New Dialog indicating how many assets are already in the project
* Option to add only the remaining assets
* Improved design/UX overall
2022-06-02 18:28:37 +02:00
D8H
9ec7fc71a8 The asset store remembers the opened page (#3982) 2022-06-02 12:41:53 +02:00
D8H
0f0a4034b9 Fix the hook dependency of the search. (#3980)
Don't show in changelog
2022-06-01 22:43:03 +02:00
D8H
6f831fe471 Cleanup filters context (#3979)
Don't show in changelogs.
2022-06-01 19:21:27 +02:00
D8H
0403882a18 Keep assets from a pack in order (#3978) 2022-06-01 18:30:21 +02:00
Florian Rival
318b1a62c9 Display the modern Dark theme by default for new users 2022-06-01 17:04:07 +02:00
Aurélien Vivet
41c9069289 Disable the autocomplete for Text Input objects (#3895) 2022-06-01 16:56:19 +02:00
Florian Rival
e98c96b5a4 Add a new "Modern" dark theme (#3974)
* This theme brings some changes to make the interface more readable to new users (outlined buttons) and a more modern approach (vibrant color, rounded buttons). It's still based on Material Design and similar to other themes, but is a first step toward an improved interface.
2022-06-01 14:36:07 +02:00
AlexandreS
893013e102 Fix loss of data when editing variables from the instance properties panel 2022-06-01 11:58:32 +02:00
D8H
dc41ec862c Sort asset search results and make the color filter not excluding. (#3965) 2022-06-01 10:39:10 +02:00
Florian Rival
5495257732 Reduce size of events in JSON by not writing empty events/sub-instructions arrays (#3975)
Only show in developer changelog
2022-05-31 17:24:01 +02:00
D8H
8c0a3221de Update the mouse buttons status when the cursor comes back inside the game view (#3970) 2022-05-30 13:35:55 +02:00
Florian Rival
70db787161 Automatically removing smoothing on pixel art assets added from the Asset Store 2022-05-27 15:06:10 +02:00
Florian Rival
cfd5a4b99e Fix asset store details page Link size and hrefs
Don't show in changelog
2022-05-27 13:11:02 +02:00
Clément Pasteau
fbb7f16d29 Rework the Asset Store details page to show a preview of the animation(s) of the object (#3930) 2022-05-27 12:58:42 +02:00
Fannie Yan
2c5374b87a Rework events Drag&Drop in events sheet (#3936) 2022-05-27 10:41:43 +02:00
Florian Rival
20075411bf Add links to extension creators public profile pages in the extension pages on the wiki (#3963) 2022-05-25 17:34:47 +02:00
Fannie Yan
a07efcd94e Clear authors and publishers when opening project after creation (#3964) 2022-05-25 17:06:28 +02:00
D8H
fcef430784 Add filters on animation, viewpoint, dimension, object type, color and license to the assets store (#3915) 2022-05-25 16:34:39 +02:00
AlexandreS
b9d27184b2 Enable backdrop click on new behavior dialog
Do not show in changelog
2022-05-25 14:58:29 +02:00
LuniMoon
1ae0ddbf66 Optimise copy of the new feature issue template (#3961)
Modified copy to send the user to the optimised channel for feature request and user feedback (Forum).

Don't show in changelog
2022-05-25 14:26:03 +02:00
Fannie Yan
1ec60f9d55 Add context in search placeholder (#3958)
* Specify search scope in search placeholder
2022-05-25 14:09:21 +02:00
Clément Pasteau
ecef111981 Update yarn editor folder hash (#3959)
Do not show in changelog
2022-05-25 11:23:52 +02:00
412 changed files with 11709 additions and 4577 deletions

View File

@@ -10,11 +10,11 @@ assignees: ''
BEFORE opening a new feature request, please make sure that 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
- 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
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 :)
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 :)
## Description

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -658,7 +658,7 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
}
gd::String EventsCodeGenerator::GenerateParameterCodes(
const gd::String& parameter,
const gd::Expression& parameter,
const gd::ParameterMetadata& metadata,
gd::EventsCodeGenerationContext& context,
const gd::String& lastObjectName,
@@ -668,19 +668,20 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
if (ParameterMetadata::IsExpression("number", metadata.type)) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "number", parameter);
*this, context, "number", parameter, lastObjectName);
} else if (ParameterMetadata::IsExpression("string", metadata.type)) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "string", parameter);
*this, context, "string", parameter, lastObjectName);
} else if (ParameterMetadata::IsExpression("variable", metadata.type)) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, metadata.type, parameter, lastObjectName);
} else if (ParameterMetadata::IsObject(metadata.type)) {
// It would be possible to run a gd::ExpressionCodeGenerator if later
// objects can have nested objects, or function returning objects.
argOutput = GenerateObject(parameter, metadata.type, context);
argOutput = GenerateObject(parameter.GetPlainString(), metadata.type, context);
} else if (metadata.type == "relationalOperator") {
argOutput += parameter == "=" ? "==" : parameter;
auto parameterString = parameter.GetPlainString();
argOutput += parameterString == "=" ? "==" : parameterString;
if (argOutput != "==" && argOutput != "<" && argOutput != ">" &&
argOutput != "<=" && argOutput != ">=" && argOutput != "!=") {
cout << "Warning: Bad relational operator: Set to == by default." << endl;
@@ -689,7 +690,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput = "\"" + argOutput + "\"";
} else if (metadata.type == "operator") {
argOutput += parameter;
argOutput += parameter.GetPlainString();
if (argOutput != "=" && argOutput != "+" && argOutput != "-" &&
argOutput != "/" && argOutput != "*") {
cout << "Warning: Bad operator: Set to = by default." << endl;
@@ -698,9 +699,9 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput = "\"" + argOutput + "\"";
} else if (ParameterMetadata::IsBehavior(metadata.type)) {
argOutput = GenerateGetBehaviorNameCode(parameter);
argOutput = GenerateGetBehaviorNameCode(parameter.GetPlainString());
} else if (metadata.type == "key") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "audioResource" ||
metadata.type == "bitmapFontResource" ||
metadata.type == "fontResource" ||
@@ -710,15 +711,17 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
// Deprecated, old parameter names:
metadata.type == "password" || metadata.type == "musicfile" ||
metadata.type == "soundfile" || metadata.type == "police") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "mouse") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "yesorno") {
argOutput += (parameter == "yes" || parameter == "oui") ? GenerateTrue()
auto parameterString = parameter.GetPlainString();
argOutput += (parameterString == "yes" || parameterString == "oui") ? GenerateTrue()
: GenerateFalse();
} else if (metadata.type == "trueorfalse") {
auto parameterString = parameter.GetPlainString();
// This is duplicated in AdvancedExtension.cpp for GDJS
argOutput += (parameter == "True" || parameter == "Vrai") ? GenerateTrue()
argOutput += (parameterString == "True" || parameterString == "Vrai") ? GenerateTrue()
: GenerateFalse();
}
// Code only parameter type
@@ -738,7 +741,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
if (!metadata.type.empty())
cout << "Warning: Unknown type of parameter \"" << metadata.type
<< "\"." << std::endl;
argOutput += "\"" + ConvertToString(parameter) + "\"";
argOutput += "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
}
}
@@ -758,7 +761,7 @@ vector<gd::String> EventsCodeGenerator::GenerateParametersCodes(
parametersInfo,
[this, &context, &supplementaryParametersTypes, &arguments](
const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
const gd::Expression& parameterValue,
const gd::String& lastObjectName) {
gd::String argOutput =
GenerateParameterCodes(parameterValue,
@@ -1243,7 +1246,7 @@ gd::String EventsCodeGenerator::GenerateArgumentsList(
return argumentsStr;
}
EventsCodeGenerator::EventsCodeGenerator(gd::Project& project_,
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
const gd::Layout& layout,
const gd::Platform& platform_)
: platform(platform_),
@@ -1260,7 +1263,7 @@ EventsCodeGenerator::EventsCodeGenerator(gd::Project& project_,
EventsCodeGenerator::EventsCodeGenerator(
const gd::Platform& platform_,
gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& objectsAndGroups_)
: platform(platform_),
globalObjectsAndGroups(globalObjectsAndGroups_),

View File

@@ -48,7 +48,7 @@ class GD_CORE_API EventsCodeGenerator {
* \brief Construct a code generator for the specified
* platform/project/layout.
*/
EventsCodeGenerator(gd::Project& project_,
EventsCodeGenerator(const gd::Project& project_,
const gd::Layout& layout,
const gd::Platform& platform_);
@@ -57,7 +57,7 @@ class GD_CORE_API EventsCodeGenerator {
* objects/groups and platform
*/
EventsCodeGenerator(const gd::Platform& platform,
gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& objectsAndGroups_);
virtual ~EventsCodeGenerator(){};
@@ -327,7 +327,7 @@ class GD_CORE_API EventsCodeGenerator {
/**
* \brief Get the global objects/groups used for code generation.
*/
gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
const gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
return globalObjectsAndGroups;
}
@@ -348,7 +348,7 @@ class GD_CORE_API EventsCodeGenerator {
* \brief Get the project the code is being generated for.
* \warning This is only valid if HasProjectAndLayout() is true.
*/
gd::Project& GetProject() const { return *project; }
const gd::Project& GetProject() const { return *project; }
/**
* \brief Get the layout the code is being generated for.
@@ -517,7 +517,7 @@ class GD_CORE_API EventsCodeGenerator {
* \endcode
*/
virtual gd::String GenerateParameterCodes(
const gd::String& parameter,
const gd::Expression& parameter,
const gd::ParameterMetadata& metadata,
gd::EventsCodeGenerationContext& context,
const gd::String& lastObjectName,
@@ -770,12 +770,12 @@ class GD_CORE_API EventsCodeGenerator {
const gd::Platform& platform; ///< The platform being used.
gd::ObjectsContainer& globalObjectsAndGroups;
const gd::ObjectsContainer& globalObjectsAndGroups;
const gd::ObjectsContainer& objectsAndGroups;
bool hasProjectAndLayout; ///< true only if project and layout are valid
///< references. If false, they should not be used.
gd::Project* project; ///< The project being used.
const gd::Project* project; ///< The project being used.
const gd::Layout* scene; ///< The scene being generated.
bool errorOccurred; ///< Must be set to true if an error occured.

View File

@@ -25,36 +25,38 @@
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
namespace gd {
gd::String ExpressionCodeGenerator::GenerateExpressionCode(
EventsCodeGenerator& codeGenerator,
EventsCodeGenerationContext& context,
const gd::String& type,
const gd::String& expression,
const gd::String& objectName) {
gd::ExpressionParser2 parser(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups());
ExpressionCodeGenerator generator(codeGenerator, context);
const gd::String& rootType,
const gd::Expression& expression,
const gd::String& rootObjectName) {
ExpressionCodeGenerator generator(rootType, rootObjectName, codeGenerator, context);
auto node = parser.ParseExpression(type, expression, objectName);
auto node = expression.GetRootNode();
if (!node) {
std::cout << "Error: error while parsing: \"" << expression << "\" ("
<< type << ")" << std::endl;
std::cout << "Error: error while parsing: \"" << expression.GetPlainString()
<< "\" (" << rootType << ")" << std::endl;
return generator.GenerateDefaultValue(type);
return generator.GenerateDefaultValue(rootType);
}
gd::ExpressionValidator validator;
gd::ExpressionValidator validator(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType);
node->Visit(validator);
if (!validator.GetErrors().empty()) {
std::cout << "Error: \"" << validator.GetErrors()[0]->GetMessage()
<< "\" in: \"" << expression << "\" (" << type << ")"
<< std::endl;
<< "\" in: \"" << expression.GetPlainString() << "\" ("
<< rootType << ")" << std::endl;
return generator.GenerateDefaultValue(type);
return generator.GenerateDefaultValue(rootType);
}
node->Visit(generator);
@@ -97,15 +99,24 @@ void ExpressionCodeGenerator::OnVisitTextNode(TextNode& node) {
void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
// This "translation" from the type to an enum could be avoided
// if all types were moved to an enum.
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType,
node);
EventsCodeGenerator::VariableScope scope =
node.type == "globalvar"
type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: ((node.type == "scenevar")
: ((type == "scenevar")
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootObjectName,
node);
output += codeGenerator.GenerateGetVariable(
node.name, scope, context, node.objectName);
node.name, scope, context, objectName);
if (node.child) node.child->Visit(*this);
}
@@ -117,7 +128,7 @@ void ExpressionCodeGenerator::OnVisitVariableAccessorNode(
void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
ExpressionCodeGenerator generator(codeGenerator, context);
ExpressionCodeGenerator generator("string", "", codeGenerator, context);
node.expression->Visit(generator);
output +=
codeGenerator.GenerateVariableBracketAccessor(generator.GetOutput());
@@ -125,39 +136,79 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
}
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
if (gd::ParameterMetadata::IsObject(node.type)) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType,
node);
if (gd::ParameterMetadata::IsObject(type)) {
output +=
codeGenerator.GenerateObject(node.identifierName, node.type, context);
} else {
codeGenerator.GenerateObject(node.identifierName, type, context);
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
EventsCodeGenerator::VariableScope scope =
type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: ((type == "scenevar")
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootObjectName,
node);
output += codeGenerator.GenerateGetVariable(
node.identifierName, scope, context, objectName);
if (!node.childIdentifierName.empty()) {
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}
} else if (node.childIdentifierName.empty()) {
output += "/* Error during generation, unrecognized identifier type: " +
codeGenerator.ConvertToString(node.type) + " with value " +
codeGenerator.ConvertToString(type) + " with value " +
codeGenerator.ConvertToString(node.identifierName) + " */ " +
codeGenerator.ConvertToStringExplicit(node.identifierName);
}
else {
// This is for function names that are put in IdentifierNode
// because the type is needed to tell them appart from variables.
output += GenerateDefaultValue(type);
}
}
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
if (gd::MetadataProvider::IsBadExpressionMetadata(node.expressionMetadata)) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType,
node);
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
node);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
output += "/* Error during generation, function not found: " +
codeGenerator.ConvertToString(node.functionName) + " */ " +
GenerateDefaultValue(node.type);
GenerateDefaultValue(type);
return;
}
if (!node.objectName.empty()) {
if (!node.behaviorName.empty()) {
output += GenerateBehaviorFunctionCode(node.type,
output += GenerateBehaviorFunctionCode(type,
node.objectName,
node.behaviorName,
node.parameters,
node.expressionMetadata);
metadata);
} else {
output += GenerateObjectFunctionCode(
node.type, node.objectName, node.parameters, node.expressionMetadata);
type, node.objectName, node.parameters, metadata);
}
} else {
output +=
GenerateFreeFunctionCode(node.parameters, node.expressionMetadata);
GenerateFreeFunctionCode(node.parameters, metadata);
}
}
@@ -305,18 +356,21 @@ gd::String ExpressionCodeGenerator::GenerateParametersCodes(
auto& parameterMetadata = expressionMetadata.parameters[i];
if (!parameterMetadata.IsCodeOnly()) {
ExpressionCodeGenerator generator(codeGenerator, context);
if (nonCodeOnlyParameterIndex < parameters.size()) {
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootObjectName,
*parameters[nonCodeOnlyParameterIndex].get());
ExpressionCodeGenerator generator(parameterMetadata.GetType(), objectName, codeGenerator, context);
parameters[nonCodeOnlyParameterIndex]->Visit(generator);
parametersCode += generator.GetOutput();
} else if (parameterMetadata.IsOptional()) {
ExpressionCodeGenerator generator(parameterMetadata.GetType(), "", codeGenerator, context);
// Optional parameters default value were not parsed at the time of the
// expression parsing. Parse them now.
ExpressionParser2 parser(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups());
auto node = parser.ParseExpression(parameterMetadata.GetType(),
parameterMetadata.GetDefaultValue());
ExpressionParser2 parser;
auto node = parser.ParseExpression(parameterMetadata.GetDefaultValue());
node->Visit(generator);
parametersCode += generator.GetOutput();
@@ -374,12 +428,22 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
}
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
output += GenerateDefaultValue(node.type);
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType,
node);
output += GenerateDefaultValue(type);
}
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(
ObjectFunctionNameNode& node) {
output += GenerateDefaultValue(node.type);
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootType,
node);
output += GenerateDefaultValue(type);
}
} // namespace gd

View File

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

View File

@@ -0,0 +1,41 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Events/Expression.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/String.h"
namespace gd {
Expression::Expression() : node(nullptr) {};
Expression::Expression(gd::String plainString_)
: node(nullptr), plainString(plainString_) {};
Expression::Expression(const char* plainString_)
: node(nullptr), plainString(plainString_) {};
Expression::Expression(const Expression& copy)
: node(nullptr), plainString{copy.plainString} {};
Expression& Expression::operator=(const Expression& expression) {
plainString = expression.plainString;
node = nullptr;
return *this;
};
Expression::~Expression(){};
ExpressionNode* Expression::GetRootNode() const {
if (!node) {
gd::ExpressionParser2 parser = ExpressionParser2();
node = std::move(parser.ParseExpression(plainString));
}
return node.get();
}
} // namespace gd

View File

@@ -6,7 +6,15 @@
#ifndef GDCORE_EXPRESSION_H
#define GDCORE_EXPRESSION_H
#include "GDCore/String.h"
#include <memory>
namespace gd {
class ExpressionParser2;
class ObjectsContainer;
struct ExpressionNode;
} // namespace gd
namespace gd {
@@ -24,32 +32,49 @@ class GD_CORE_API Expression {
/**
* \brief Construct an empty expression
*/
Expression(){};
Expression();
/**
* \brief Construct an expression from a string
*/
Expression(gd::String plainString_) : plainString(plainString_){};
Expression(gd::String plainString_);
/**
* \brief Construct an expression from a const char *
*/
Expression(const char* plainString_) : plainString(plainString_){};
Expression(const char* plainString_);
/**
* \brief Copy construct an expression.
*/
Expression(const Expression& copy);
/**
* \brief Expression affectation overriding.
*/
Expression& operator=(const Expression& expression);
/**
* \brief Get the plain string representing the expression
*/
inline const gd::String& GetPlainString() const { return plainString; };
/**
* @brief Get the expression node.
* @return std::unique_ptr<gd::ExpressionNode>
*/
gd::ExpressionNode* GetRootNode() const;
/**
* \brief Mimics std::string::c_str
*/
inline const char* c_str() const { return plainString.c_str(); };
virtual ~Expression(){};
virtual ~Expression();
private:
gd::String plainString; ///< The expression string
mutable std::unique_ptr<gd::ExpressionNode> node;
};
} // namespace gd

View File

@@ -26,132 +26,16 @@ namespace gd {
gd::String ExpressionParser2::NAMESPACE_SEPARATOR = "::";
ExpressionParser2::ExpressionParser2(
const gd::Platform& platform_,
const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_)
ExpressionParser2::ExpressionParser2()
: expression(""),
currentPosition(0),
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_) {}
namespace {
/**
* Return the minimum number of parameters, starting from a given parameter
* (by convention, 1 for object functions and 2 for behavior functions).
*/
size_t GetMinimumParametersNumber(
const std::vector<gd::ParameterMetadata>& parameters,
size_t initialParameterIndex) {
size_t nb = 0;
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
if (!parameters[i].optional && !parameters[i].codeOnly) nb++;
}
return nb;
}
/**
* Return the maximum number of parameters, starting from a given parameter
* (by convention, 1 for object functions and 2 for behavior functions).
*/
size_t GetMaximumParametersNumber(
const std::vector<gd::ParameterMetadata>& parameters,
size_t initialParameterIndex) {
size_t nb = 0;
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
if (!parameters[i].codeOnly) nb++;
}
return nb;
}
} // namespace
std::unique_ptr<ExpressionParserDiagnostic> ExpressionParser2::ValidateFunction(
const gd::String& type,
const gd::FunctionCallNode& function,
size_t functionStartPosition) {
if (gd::MetadataProvider::IsBadExpressionMetadata(
function.expressionMetadata)) {
return gd::make_unique<ExpressionParserError>(
"invalid_function_name",
_("Cannot find an expression with this name: ") +
function.functionName + "\n" +
_("Double check that you've not made any typo in the name."),
functionStartPosition,
GetCurrentPosition());
}
// Validate the type of the function
const gd::String& returnType = function.expressionMetadata.GetReturnType();
if (returnType == "number") {
if (type == "string")
return RaiseTypeError(
_("You tried to use an expression that returns a number, but a "
"string is expected. Use `ToString` if you need to convert a "
"number to a string."),
functionStartPosition);
else if (type != "number" && type != "number|string")
return RaiseTypeError(_("You tried to use an expression that returns a "
"number, but another type is expected:") +
" " + type,
functionStartPosition);
} else if (returnType == "string") {
if (type == "number")
return RaiseTypeError(
_("You tried to use an expression that returns a string, but a "
"number is expected. Use `ToNumber` if you need to convert a "
"string to a number."),
functionStartPosition);
else if (type != "string" && type != "number|string")
return RaiseTypeError(_("You tried to use an expression that returns a "
"string, but another type is expected:") +
" " + type,
functionStartPosition);
} else {
if (type != returnType)
return RaiseTypeError(
_("You tried to use an expression with the wrong return type:") + " " +
returnType,
functionStartPosition);
}
// Validate parameters count
size_t minParametersCount = GetMinimumParametersNumber(
function.expressionMetadata.parameters,
WrittenParametersFirstIndex(function.objectName, function.behaviorName));
size_t maxParametersCount = GetMaximumParametersNumber(
function.expressionMetadata.parameters,
WrittenParametersFirstIndex(function.objectName, function.behaviorName));
if (function.parameters.size() < minParametersCount ||
function.parameters.size() > maxParametersCount) {
gd::String expectedCountMessage =
minParametersCount == maxParametersCount
? _("The number of parameters must be exactly ") +
gd::String::From(minParametersCount)
: _("The number of parameters must be: ") +
gd::String::From(minParametersCount) + "-" +
gd::String::From(maxParametersCount);
if (function.parameters.size() < minParametersCount) {
return gd::make_unique<ExpressionParserError>(
"too_few_parameters",
"You have not entered enough parameters for the expression. " +
expectedCountMessage,
functionStartPosition,
GetCurrentPosition());
}
}
return gd::make_unique<ExpressionParserDiagnostic>();
}
currentPosition(0) {}
std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
size_t textStartPosition = GetCurrentPosition();
SkipAllWhitespaces();
if (!CheckIfChar(IsQuote)) {
auto text = gd::make_unique<TextNode>("");
// It can't happen.
text->diagnostic =
RaiseSyntaxError(_("A text must start with a double quote (\")."));
text->location =

View File

@@ -40,9 +40,7 @@ namespace gd {
*/
class GD_CORE_API ExpressionParser2 {
public:
ExpressionParser2(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_);
ExpressionParser2();
virtual ~ExpressionParser2(){};
/**
@@ -58,13 +56,11 @@ class GD_CORE_API ExpressionParser2 {
* \return The node representing the expression as a parsed tree.
*/
std::unique_ptr<ExpressionNode> ParseExpression(
const gd::String &type,
const gd::String &expression_,
const gd::String &objectName = "") {
const gd::String &expression_) {
expression = expression_;
currentPosition = 0;
return Start(type, objectName);
return Start();
}
/**
@@ -88,18 +84,16 @@ class GD_CORE_API ExpressionParser2 {
* Each method is a part of the grammar.
*/
///@{
std::unique_ptr<ExpressionNode> Start(const gd::String &type,
const gd::String &objectName = "") {
std::unique_ptr<ExpressionNode> Start() {
size_t expressionStartPosition = GetCurrentPosition();
auto expression = Expression(type, objectName);
const gd::String &inferredType = expression->type;
auto expression = Expression();
// Check for extra characters at the end of the expression
if (!IsEndReached()) {
auto op = gd::make_unique<OperatorNode>(inferredType, ' ');
auto op = gd::make_unique<OperatorNode>(' ');
op->leftHandSide = std::move(expression);
op->rightHandSide = ReadUntilEnd("unknown");
op->rightHandSide = ReadUntilEnd();
op->rightHandSide->parent = op.get();
op->rightHandSide->diagnostic = RaiseSyntaxError(
_("The expression has extra character at the end that should be "
@@ -113,61 +107,49 @@ class GD_CORE_API ExpressionParser2 {
return expression;
}
std::unique_ptr<ExpressionNode> Expression(
const gd::String &type, const gd::String &objectName = "") {
std::unique_ptr<ExpressionNode> Expression() {
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> leftHandSide = Term(type, objectName);
const gd::String &inferredType = leftHandSide->type;
std::unique_ptr<ExpressionNode> leftHandSide = Term();
SkipAllWhitespaces();
if (IsEndReached()) return leftHandSide;
if (CheckIfChar(IsExpressionEndingChar)) return leftHandSide;
if (CheckIfChar(IsExpressionOperator)) {
auto op = gd::make_unique<OperatorNode>(inferredType, GetCurrentChar());
auto op = gd::make_unique<OperatorNode>(GetCurrentChar());
op->leftHandSide = std::move(leftHandSide);
op->diagnostic = ValidateOperator(inferredType, GetCurrentChar());
op->leftHandSide->parent = op.get();
op->diagnostic = ValidateOperator(GetCurrentChar());
SkipChar();
op->rightHandSide = Expression(inferredType, objectName);
op->rightHandSide = Expression();
op->rightHandSide->parent = op.get();
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
return std::move(op);
}
if (inferredType == "string") {
leftHandSide->diagnostic = RaiseSyntaxError(
"You must add the operator + between texts or expressions. For "
"example: \"Your name: \" + VariableString(PlayerName).");
} else if (inferredType == "number") {
leftHandSide->diagnostic = RaiseSyntaxError(
"No operator found. Did you forget to enter an operator (like +, -, "
"* or /) between numbers or expressions?");
} else {
leftHandSide->diagnostic = RaiseSyntaxError(
"More than one term was found. Verify that your expression is "
"properly written.");
}
leftHandSide->diagnostic = RaiseSyntaxError(
"More than one term was found. Verify that your expression is "
"properly written.");
auto op = gd::make_unique<OperatorNode>(inferredType, ' ');
auto op = gd::make_unique<OperatorNode>(' ');
op->leftHandSide = std::move(leftHandSide);
op->rightHandSide = Expression(inferredType, objectName);
op->leftHandSide->parent = op.get();
op->rightHandSide = Expression();
op->rightHandSide->parent = op.get();
op->location =
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
return std::move(op);
}
std::unique_ptr<ExpressionNode> Term(const gd::String &type,
const gd::String &objectName) {
std::unique_ptr<ExpressionNode> Term() {
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> factor = Factor(type, objectName);
const gd::String &inferredType = factor->type;
std::unique_ptr<ExpressionNode> factor = Factor();
SkipAllWhitespaces();
@@ -175,11 +157,13 @@ class GD_CORE_API ExpressionParser2 {
// to guarantee the proper operator precedence. (Expression could also
// be reworked to use a while loop).
while (CheckIfChar(IsTermOperator)) {
auto op = gd::make_unique<OperatorNode>(inferredType, GetCurrentChar());
auto op = gd::make_unique<OperatorNode>(GetCurrentChar());
op->leftHandSide = std::move(factor);
op->diagnostic = ValidateOperator(inferredType, GetCurrentChar());
op->leftHandSide->parent = op.get();
op->diagnostic = ValidateOperator(GetCurrentChar());
SkipChar();
op->rightHandSide = Factor(inferredType, objectName);
op->rightHandSide = Factor();
op->rightHandSide->parent = op.get();
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
SkipAllWhitespaces();
@@ -190,54 +174,35 @@ class GD_CORE_API ExpressionParser2 {
return factor;
};
std::unique_ptr<ExpressionNode> Factor(const gd::String &type,
const gd::String &objectName) {
std::unique_ptr<ExpressionNode> Factor() {
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
if (CheckIfChar(IsQuote)) {
std::unique_ptr<ExpressionNode> factor = ReadText();
if (type == "number")
factor->diagnostic =
RaiseTypeError(_("You entered a text, but a number was expected."),
expressionStartPosition);
else if (type != "string" && type != "number|string")
factor->diagnostic = RaiseTypeError(
_("You entered a text, but this type was expected:") + type,
expressionStartPosition);
return factor;
} else if (CheckIfChar(IsUnaryOperator)) {
auto unaryOperatorCharacter = GetCurrentChar();
SkipChar();
auto operatorOperand = Factor(type, objectName);
const gd::String &inferredType = operatorOperand->type;
auto operatorOperand = Factor();
auto unaryOperator = gd::make_unique<UnaryOperatorNode>(
inferredType, unaryOperatorCharacter);
unaryOperatorCharacter);
unaryOperator->diagnostic = ValidateUnaryOperator(
inferredType, unaryOperatorCharacter, expressionStartPosition);
unaryOperatorCharacter, expressionStartPosition);
unaryOperator->factor = std::move(operatorOperand);
unaryOperator->factor->parent = unaryOperator.get();
unaryOperator->location = ExpressionParserLocation(
expressionStartPosition, GetCurrentPosition());
return std::move(unaryOperator);
} else if (CheckIfChar(IsNumberFirstChar)) {
std::unique_ptr<ExpressionNode> factor = ReadNumber();
if (type == "string")
factor->diagnostic = RaiseTypeError(
_("You entered a number, but a text was expected (in quotes)."),
expressionStartPosition);
else if (type != "number" && type != "number|string")
factor->diagnostic = RaiseTypeError(
_("You entered a number, but this type was expected:") + type,
expressionStartPosition);
return factor;
} else if (CheckIfChar(IsOpeningParenthesis)) {
SkipChar();
std::unique_ptr<ExpressionNode> factor = SubExpression(type, objectName);
std::unique_ptr<ExpressionNode> factor = SubExpression();
if (!CheckIfChar(IsClosingParenthesis)) {
factor->diagnostic =
@@ -247,29 +212,20 @@ class GD_CORE_API ExpressionParser2 {
SkipIfChar(IsClosingParenthesis);
return factor;
} else if (IsIdentifierAllowedChar()) {
// This is a place where the grammar differs according to the
// type being expected.
if (gd::ParameterMetadata::IsExpression("variable", type)) {
return Variable(type, objectName);
} else {
return Identifier(type);
}
return Identifier();
}
std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace(type);
factor->diagnostic = RaiseEmptyError(type, expressionStartPosition);
std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace();
return factor;
}
std::unique_ptr<SubExpressionNode> SubExpression(
const gd::String &type, const gd::String &objectName) {
std::unique_ptr<SubExpressionNode> SubExpression() {
size_t expressionStartPosition = GetCurrentPosition();
auto expression = Expression(type, objectName);
const gd::String &inferredType = expression->type;
auto expression = Expression();
auto subExpression =
gd::make_unique<SubExpressionNode>(inferredType, std::move(expression));
gd::make_unique<SubExpressionNode>(std::move(expression));
subExpression->location =
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
@@ -277,7 +233,7 @@ class GD_CORE_API ExpressionParser2 {
};
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
Identifier(const gd::String &type) {
Identifier() {
auto identifierAndLocation = ReadIdentifierName();
gd::String name = identifierAndLocation.name;
auto nameLocation = identifierAndLocation.location;
@@ -304,47 +260,28 @@ class GD_CORE_API ExpressionParser2 {
if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
return FreeFunction(type, name, nameLocation, openingParenthesisLocation);
return FreeFunction(name, nameLocation, openingParenthesisLocation);
} else if (CheckIfChar(IsDot)) {
ExpressionParserLocation dotLocation = SkipChar();
SkipAllWhitespaces();
return ObjectFunctionOrBehaviorFunction(
type, name, nameLocation, dotLocation);
} else {
auto identifier = gd::make_unique<IdentifierNode>(name, type);
if (type == "string") {
identifier->diagnostic =
RaiseTypeError(_("You must wrap your text inside double quotes "
"(example: \"Hello world\")."),
nameLocation.GetStartPosition());
} else if (type == "number") {
identifier->diagnostic = RaiseTypeError(
_("You must enter a number."), nameLocation.GetStartPosition());
} else if (type == "number|string") {
identifier->diagnostic = RaiseTypeError(
_("You must enter a number or a text, wrapped inside double quotes "
"(example: \"Hello world\")."),
nameLocation.GetStartPosition());
} else if (!gd::ParameterMetadata::IsObject(type)) {
identifier->diagnostic = RaiseTypeError(
_("You've entered a name, but this type was expected:") + type,
nameLocation.GetStartPosition());
}
name, nameLocation, dotLocation);
} else if (CheckIfChar(IsOpeningSquareBracket)) {
return Variable(name, nameLocation);
}
else {
auto identifier = gd::make_unique<IdentifierNode>(name);
identifier->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
identifier->identifierNameLocation = identifier->location;
return std::move(identifier);
}
}
std::unique_ptr<VariableNode> Variable(const gd::String &type,
const gd::String &objectName) {
auto identifierAndLocation = ReadIdentifierName();
const gd::String &name = identifierAndLocation.name;
const auto &nameLocation = identifierAndLocation.location;
auto variable = gd::make_unique<VariableNode>(type, name, objectName);
std::unique_ptr<VariableNode> Variable(const gd::String &name, gd::ExpressionParserLocation nameLocation) {
auto variable = gd::make_unique<VariableNode>(name);
variable->child = VariableAccessorOrVariableBracketAccessor();
variable->child->parent = variable.get();
variable->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
@@ -359,8 +296,8 @@ class GD_CORE_API ExpressionParser2 {
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket)) {
SkipChar();
auto child = gd::make_unique<VariableBracketAccessorNode>(
Expression("number|string"));
auto child = gd::make_unique<VariableBracketAccessorNode>(Expression());
child->expression->parent = child.get();
if (!CheckIfChar(IsClosingSquareBracket)) {
child->diagnostic =
@@ -369,6 +306,7 @@ class GD_CORE_API ExpressionParser2 {
}
SkipIfChar(IsClosingSquareBracket);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
child->location =
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
@@ -381,6 +319,7 @@ class GD_CORE_API ExpressionParser2 {
auto child =
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
child->nameLocation = identifierAndLocation.location;
child->dotLocation = dotLocation;
child->location =
@@ -389,40 +328,21 @@ class GD_CORE_API ExpressionParser2 {
return std::move(child);
}
return std::move(
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>());
return std::move(gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>());
}
std::unique_ptr<FunctionCallNode> FreeFunction(
const gd::String &type,
const gd::String &functionFullName,
const ExpressionParserLocation &identifierLocation,
const ExpressionParserLocation &openingParenthesisLocation) {
// TODO: error if trying to use function for type != "number" && != "string"
// + Test for it
const gd::ExpressionMetadata &metadata =
MetadataProvider::GetAnyExpressionMetadata(platform, functionFullName);
// In case we can't find a valid expression, ensure the node has the type
// that is requested by the parent, so we avoid putting "unknown" (which
// would be also correct, but less precise and would prevent completions to
// be shown to the user)
const gd::String returnType =
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
? type
: metadata.GetReturnType();
auto parametersNode = Parameters(metadata.parameters);
auto function =
gd::make_unique<FunctionCallNode>(returnType,
std::move(parametersNode.parameters),
metadata,
functionFullName);
gd::make_unique<FunctionCallNode>(functionFullName);
auto parametersNode = Parameters(function.get());
function->parameters = std::move(parametersNode.parameters);
function->diagnostic = std::move(parametersNode.diagnostic);
if (!function->diagnostic) // TODO: reverse the order of diagnostic?
function->diagnostic = ValidateFunction(
type, *function, identifierLocation.GetStartPosition());
function->location = ExpressionParserLocation(
identifierLocation.GetStartPosition(), GetCurrentPosition());
@@ -434,16 +354,15 @@ class GD_CORE_API ExpressionParser2 {
return std::move(function);
}
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode>
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
ObjectFunctionOrBehaviorFunction(
const gd::String &type,
const gd::String &objectName,
const ExpressionParserLocation &objectNameLocation,
const ExpressionParserLocation &objectNameDotLocation) {
auto identifierAndLocation = ReadIdentifierName();
const gd::String &objectFunctionOrBehaviorName = identifierAndLocation.name;
const auto &objectFunctionOrBehaviorNameLocation =
identifierAndLocation.location;
const gd::String &parentIdentifier,
const ExpressionParserLocation &parentIdentifierLocation,
const ExpressionParserLocation &parentIdentifierDotLocation) {
auto childIdentifierAndLocation = ReadIdentifierName();
const gd::String &childIdentifierName = childIdentifierAndLocation.name;
const auto &childIdentifierNameLocation =
childIdentifierAndLocation.location;
SkipAllWhitespaces();
@@ -451,87 +370,68 @@ class GD_CORE_API ExpressionParser2 {
ExpressionParserLocation namespaceSeparatorLocation =
SkipNamespaceSeparator();
SkipAllWhitespaces();
return BehaviorFunction(type,
objectName,
objectFunctionOrBehaviorName,
objectNameLocation,
objectNameDotLocation,
objectFunctionOrBehaviorNameLocation,
return BehaviorFunction(parentIdentifier,
childIdentifierName,
parentIdentifierLocation,
parentIdentifierDotLocation,
childIdentifierNameLocation,
namespaceSeparatorLocation);
} else if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
gd::String objectType =
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName);
const gd::ExpressionMetadata &metadata =
MetadataProvider::GetObjectAnyExpressionMetadata(
platform, objectType, objectFunctionOrBehaviorName);
// In case we can't find a valid expression, ensure the node has the type
// that is requested by the parent, so we avoid putting "unknown" (which
// would be also correct, but less precise and would prevent completions
// to be shown to the user)
const gd::String returnType =
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
? type
: metadata.GetReturnType();
auto parametersNode = Parameters(metadata.parameters, objectName);
auto function = gd::make_unique<FunctionCallNode>(
returnType,
objectName,
std::move(parametersNode.parameters),
metadata,
objectFunctionOrBehaviorName);
parentIdentifier,
childIdentifierName);
auto parametersNode = Parameters(function.get(), parentIdentifier);
function->parameters = std::move(parametersNode.parameters),
function->diagnostic = std::move(parametersNode.diagnostic);
if (!function->diagnostic) // TODO: reverse the order of diagnostic?
function->diagnostic = ValidateFunction(
type, *function, objectNameLocation.GetStartPosition());
// If the function needs a capability on the object that may not be covered
// by all objects, check it now.
if (!metadata.GetRequiredBaseObjectCapability().empty()) {
const gd::ObjectMetadata &objectMetadata =
MetadataProvider::GetObjectMetadata(platform, objectType);
if (objectMetadata.IsUnsupportedBaseObjectCapability(
metadata.GetRequiredBaseObjectCapability())) {
function->diagnostic = RaiseTypeError(
_("This expression exists, but it can't be used on this object."),
objectNameLocation.GetStartPosition());
}
}
function->location = ExpressionParserLocation(
objectNameLocation.GetStartPosition(), GetCurrentPosition());
function->objectNameLocation = objectNameLocation;
function->objectNameDotLocation = objectNameDotLocation;
function->functionNameLocation = objectFunctionOrBehaviorNameLocation;
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
function->objectNameLocation = parentIdentifierLocation;
function->objectNameDotLocation = parentIdentifierDotLocation;
function->functionNameLocation = childIdentifierNameLocation;
function->openingParenthesisLocation = openingParenthesisLocation;
function->closingParenthesisLocation =
parametersNode.closingParenthesisLocation;
return std::move(function);
} else if (CheckIfChar(IsDot) || CheckIfChar(IsOpeningSquareBracket)) {
auto variable = gd::make_unique<VariableNode>(parentIdentifier);
auto child =
gd::make_unique<VariableAccessorNode>(childIdentifierName);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
child->nameLocation = childIdentifierNameLocation;
child->dotLocation = parentIdentifierDotLocation;
child->location = ExpressionParserLocation(
parentIdentifierDotLocation.GetStartPosition(), GetCurrentPosition());
variable->child = std::move(child);
variable->child->parent = variable.get();
variable->location = ExpressionParserLocation(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
variable->nameLocation = parentIdentifierLocation;
return std::move(variable);
}
auto node = gd::make_unique<ObjectFunctionNameNode>(
type, objectName, objectFunctionOrBehaviorName);
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis (for an object expression), or double colon "
"(::) was expected (for a behavior expression)."));
auto node = gd::make_unique<IdentifierNode>(
parentIdentifier, childIdentifierName);
if (!CheckIfChar(IsParameterSeparator) && !CheckIfChar(IsClosingParenthesis) && !IsEndReached()) {
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis (for an object expression), a double colon "
"(:: for a behavior expression), a dot or an opening bracket (for "
"a child variable) where expected."));
}
node->location = ExpressionParserLocation(
objectNameLocation.GetStartPosition(), GetCurrentPosition());
node->objectNameLocation = objectNameLocation;
node->objectNameDotLocation = objectNameDotLocation;
node->objectFunctionOrBehaviorNameLocation =
objectFunctionOrBehaviorNameLocation;
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
node->identifierNameLocation = parentIdentifierLocation;
node->identifierNameDotLocation = parentIdentifierDotLocation;
node->childIdentifierNameLocation = childIdentifierNameLocation;
return std::move(node);
}
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode> BehaviorFunction(
const gd::String &type,
const gd::String &objectName,
const gd::String &behaviorName,
const ExpressionParserLocation &objectNameLocation,
@@ -547,35 +447,14 @@ class GD_CORE_API ExpressionParser2 {
if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
gd::String behaviorType = GetTypeOfBehavior(
globalObjectsContainer, objectsContainer, behaviorName);
const gd::ExpressionMetadata &metadata =
MetadataProvider::GetBehaviorAnyExpressionMetadata(
platform, behaviorType, functionName);
// In case we can't find a valid expression, ensure the node has the type
// that is requested by the parent, so we avoid putting "unknown" (which
// would be also correct, but less precise and would prevent completions
// to be shown to the user)
const gd::String returnType =
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
? type
: metadata.GetReturnType();
auto parametersNode =
Parameters(metadata.parameters, objectName, behaviorName);
auto function = gd::make_unique<FunctionCallNode>(
returnType,
objectName,
behaviorName,
std::move(parametersNode.parameters),
metadata,
functionName);
auto parametersNode =
Parameters(function.get(), objectName, behaviorName);
function->parameters = std::move(parametersNode.parameters);
function->diagnostic = std::move(parametersNode.diagnostic);
if (!function->diagnostic) // TODO: reverse the order of diagnostic?
function->diagnostic = ValidateFunction(
type, *function, objectNameLocation.GetStartPosition());
function->location = ExpressionParserLocation(
objectNameLocation.GetStartPosition(), GetCurrentPosition());
@@ -591,7 +470,7 @@ class GD_CORE_API ExpressionParser2 {
return std::move(function);
} else {
auto node = gd::make_unique<ObjectFunctionNameNode>(
type, objectName, behaviorName, functionName);
objectName, behaviorName, functionName);
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis was expected here to call a function."));
@@ -615,7 +494,7 @@ class GD_CORE_API ExpressionParser2 {
};
ParametersNode Parameters(
std::vector<gd::ParameterMetadata> parameterMetadata,
FunctionCallNode *functionCallNode,
const gd::String &objectName = "",
const gd::String &behaviorName = "") {
std::vector<std::unique_ptr<ExpressionNode>> parameters;
@@ -626,77 +505,25 @@ class GD_CORE_API ExpressionParser2 {
size_t parameterIndex =
WrittenParametersFirstIndex(objectName, behaviorName);
bool previousCharacterIsParameterSeparator = false;
while (!IsEndReached()) {
SkipAllWhitespaces();
if (CheckIfChar(IsClosingParenthesis)) {
if (CheckIfChar(IsClosingParenthesis) && !previousCharacterIsParameterSeparator) {
auto closingParenthesisLocation = SkipChar();
return ParametersNode{
std::move(parameters), nullptr, closingParenthesisLocation};
} else {
if (parameterIndex < parameterMetadata.size()) {
const gd::String &type = parameterMetadata[parameterIndex].GetType();
if (parameterMetadata[parameterIndex].IsCodeOnly()) {
// Do nothing, code only parameters are not written in expressions.
} else if (gd::ParameterMetadata::IsExpression("number", type)) {
parameters.push_back(Expression("number"));
} else if (gd::ParameterMetadata::IsExpression("string", type)) {
parameters.push_back(Expression("string"));
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
parameters.push_back(Expression(
type, lastObjectName.empty() ? objectName : lastObjectName));
} else if (gd::ParameterMetadata::IsObject(type)) {
size_t parameterStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> objectExpression = Expression(type);
// Memorize the last object name. By convention, parameters that
// require an object (mainly, "objectvar" and "behavior") should be
// placed after the object in the list of parameters (if possible,
// just after). Search "lastObjectName" in the codebase for other
// place where this convention is enforced.
if (auto identifierNode =
dynamic_cast<IdentifierNode *>(objectExpression.get())) {
lastObjectName = identifierNode->identifierName;
} else {
objectExpression->diagnostic =
gd::make_unique<ExpressionParserError>(
"malformed_object_parameter",
_("An object name was expected but something else was "
"written. Enter just the name of the object for this "
"parameter."),
parameterStartPosition,
GetCurrentPosition());
}
parameters.push_back(std::move(objectExpression));
} else {
size_t parameterStartPosition = GetCurrentPosition();
parameters.push_back(Expression("unknown"));
parameters.back()->diagnostic =
gd::make_unique<ExpressionParserError>(
"unknown_parameter_type",
_("This function is improperly set up. Reach out to the "
"extension developer or a GDevelop maintainer to fix "
"this issue"),
parameterStartPosition,
GetCurrentPosition());
}
} else {
size_t parameterStartPosition = GetCurrentPosition();
parameters.push_back(Expression("unknown"));
parameters.back()
->diagnostic = gd::make_unique<ExpressionParserError>(
"extra_parameter",
_("This parameter was not expected by this expression. Remove it "
"or verify that you've entered the proper expression name."),
parameterStartPosition,
GetCurrentPosition());
}
SkipAllWhitespaces();
SkipIfChar(IsParameterSeparator);
parameterIndex++;
}
bool isEmptyParameter = CheckIfChar(IsParameterSeparator)
|| (CheckIfChar(IsClosingParenthesis) && previousCharacterIsParameterSeparator);
auto parameter = isEmptyParameter ? gd::make_unique<EmptyNode>() : Expression();
parameter->parent = functionCallNode;
parameters.push_back(std::move(parameter));
SkipAllWhitespaces();
previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
SkipIfChar(IsParameterSeparator);
parameterIndex++;
}
ExpressionParserLocation invalidClosingParenthesisLocation;
@@ -708,92 +535,32 @@ class GD_CORE_API ExpressionParser2 {
}
///@}
/** \name Validators
* Return a diagnostic if any error is found
*/
///@{
std::unique_ptr<ExpressionParserDiagnostic> ValidateFunction(
const gd::String &type,
const gd::FunctionCallNode &function,
size_t functionStartPosition);
std::unique_ptr<ExpressionParserDiagnostic> ValidateOperator(
const gd::String &type, gd::String::value_type operatorChar) {
if (type == "number") {
if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
operatorChar == '*') {
return gd::make_unique<ExpressionParserDiagnostic>();
}
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an operator that is not supported. Operator should be "
"either +, -, / or *."),
GetCurrentPosition());
} else if (type == "string") {
if (operatorChar == '+') {
return gd::make_unique<ExpressionParserDiagnostic>();
}
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an operator that is not supported. Only + can be used "
"to concatenate texts."),
GetCurrentPosition());
} else if (gd::ParameterMetadata::IsObject(type)) {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("Operators (+, -, /, *) can't be used with an object name. Remove "
"the operator."),
GetCurrentPosition());
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("Operators (+, -, /, *) can't be used in variable names. Remove "
"the operator from the variable name."),
GetCurrentPosition());
gd::String::value_type operatorChar) {
if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
operatorChar == '*') {
return gd::make_unique<ExpressionParserDiagnostic>();
}
return gd::make_unique<ExpressionParserDiagnostic>();
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an operator that is not supported. Operator should be "
"either +, -, / or *."),
GetCurrentPosition());
}
std::unique_ptr<ExpressionParserDiagnostic> ValidateUnaryOperator(
const gd::String &type,
gd::String::value_type operatorChar,
size_t position) {
if (type == "number") {
if (operatorChar == '+' || operatorChar == '-') {
return gd::make_unique<ExpressionParserDiagnostic>();
}
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an \"unary\" operator that is not supported. Operator "
"should be "
"either + or -."),
position);
} else if (type == "string") {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an operator that is not supported. Only + can be used "
"to concatenate texts, and must be placed between two texts (or "
"expressions)."),
position);
} else if (gd::ParameterMetadata::IsObject(type)) {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("Operators (+, -) can't be used with an object name. Remove the "
"operator."),
position);
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("Operators (+, -) can't be used in variable names. Remove "
"the operator from the variable name."),
position);
if (operatorChar == '+' || operatorChar == '-') {
return gd::make_unique<ExpressionParserDiagnostic>();
}
return gd::make_unique<ExpressionParserDiagnostic>();
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an \"unary\" operator that is not supported. Operator "
"should be "
"either + or -."),
position);
}
///@}
@@ -981,7 +748,7 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<NumberNode> ReadNumber();
std::unique_ptr<EmptyNode> ReadUntilWhitespace(gd::String type) {
std::unique_ptr<EmptyNode> ReadUntilWhitespace() {
size_t startPosition = GetCurrentPosition();
gd::String text;
while (currentPosition < expression.size() &&
@@ -990,13 +757,13 @@ class GD_CORE_API ExpressionParser2 {
currentPosition++;
}
auto node = gd::make_unique<EmptyNode>(type, text);
auto node = gd::make_unique<EmptyNode>(text);
node->location =
ExpressionParserLocation(startPosition, GetCurrentPosition());
return node;
}
std::unique_ptr<EmptyNode> ReadUntilEnd(gd::String type) {
std::unique_ptr<EmptyNode> ReadUntilEnd() {
size_t startPosition = GetCurrentPosition();
gd::String text;
while (currentPosition < expression.size()) {
@@ -1004,7 +771,7 @@ class GD_CORE_API ExpressionParser2 {
currentPosition++;
}
auto node = gd::make_unique<EmptyNode>(type, text);
auto node = gd::make_unique<EmptyNode>(text);
node->location =
ExpressionParserLocation(startPosition, GetCurrentPosition());
return node;
@@ -1037,34 +804,11 @@ class GD_CORE_API ExpressionParser2 {
return std::move(gd::make_unique<ExpressionParserError>(
"type_error", message, beginningPosition, GetCurrentPosition()));
}
std::unique_ptr<ExpressionParserError> RaiseEmptyError(
const gd::String &type, size_t beginningPosition) {
gd::String message;
if (type == "number") {
message = _("You must enter a number or a valid expression call.");
} else if (type == "string") {
message = _(
"You must enter a text (between quotes) or a valid expression call.");
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
message = _("You must enter a variable name.");
} else if (gd::ParameterMetadata::IsObject(type)) {
message = _("You must enter a valid object name.");
} else {
message = _("You must enter a valid expression.");
}
return std::move(RaiseTypeError(message, beginningPosition));
}
///@}
gd::String expression;
std::size_t currentPosition;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
static gd::String NAMESPACE_SEPARATOR;
};

View File

@@ -17,6 +17,7 @@ class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
struct FunctionCallNode;
} // namespace gd
namespace gd {
@@ -57,6 +58,10 @@ struct GD_CORE_API ExpressionParserDiagnostic {
* \brief An error that can be attached to a gd::ExpressionNode.
*/
struct GD_CORE_API ExpressionParserError : public ExpressionParserDiagnostic {
ExpressionParserError(const gd::String &type_,
const gd::String &message_,
const ExpressionParserLocation &location_)
: type(type_), message(message_), location(location_){};
ExpressionParserError(const gd::String &type_,
const gd::String &message_,
size_t position_)
@@ -86,7 +91,7 @@ struct GD_CORE_API ExpressionParserError : public ExpressionParserDiagnostic {
* an expression inherits from.
*/
struct GD_CORE_API ExpressionNode {
ExpressionNode(const gd::String &type_) : type(type_){};
ExpressionNode() : parent(nullptr) {};
virtual ~ExpressionNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker){};
@@ -97,17 +102,12 @@ struct GD_CORE_API ExpressionNode {
/// function can store the position of the
/// object name, the dot, the function
/// name, etc...
gd::String type; // Actual type of the node.
// "string", "number", type supported by
// gd::ParameterMetadata::IsObject, types supported by
// gd::ParameterMetadata::IsExpression or "unknown".
ExpressionNode *parent;
};
struct GD_CORE_API SubExpressionNode : public ExpressionNode {
SubExpressionNode(const gd::String &type_,
std::unique_ptr<ExpressionNode> expression_)
: ExpressionNode(type_), expression(std::move(expression_)){};
SubExpressionNode(std::unique_ptr<ExpressionNode> expression_)
: ExpressionNode(), expression(std::move(expression_)){};
virtual ~SubExpressionNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitSubExpressionNode(*this);
@@ -120,8 +120,8 @@ struct GD_CORE_API SubExpressionNode : public ExpressionNode {
* \brief An operator node. For example: "lhs + rhs".
*/
struct GD_CORE_API OperatorNode : public ExpressionNode {
OperatorNode(const gd::String &type_, gd::String::value_type op_)
: ExpressionNode(type_), op(op_){};
OperatorNode(gd::String::value_type op_)
: ExpressionNode(), op(op_){};
virtual ~OperatorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitOperatorNode(*this);
@@ -136,8 +136,8 @@ struct GD_CORE_API OperatorNode : public ExpressionNode {
* \brief A unary operator node. For example: "-2".
*/
struct GD_CORE_API UnaryOperatorNode : public ExpressionNode {
UnaryOperatorNode(const gd::String &type_, gd::String::value_type op_)
: ExpressionNode(type_), op(op_){};
UnaryOperatorNode(gd::String::value_type op_)
: ExpressionNode(), op(op_){};
virtual ~UnaryOperatorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitUnaryOperatorNode(*this);
@@ -153,7 +153,7 @@ struct GD_CORE_API UnaryOperatorNode : public ExpressionNode {
*/
struct GD_CORE_API NumberNode : public ExpressionNode {
NumberNode(const gd::String &number_)
: ExpressionNode("number"), number(number_){};
: ExpressionNode(), number(number_){};
virtual ~NumberNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitNumberNode(*this);
@@ -168,7 +168,7 @@ struct GD_CORE_API NumberNode : public ExpressionNode {
* Its `type` is always "string".
*/
struct GD_CORE_API TextNode : public ExpressionNode {
TextNode(const gd::String &text_) : ExpressionNode("string"), text(text_){};
TextNode(const gd::String &text_) : ExpressionNode(), text(text_){};
virtual ~TextNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitTextNode(*this);
@@ -177,32 +177,88 @@ struct GD_CORE_API TextNode : public ExpressionNode {
gd::String text;
};
struct GD_CORE_API IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode
: public ExpressionNode {
IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode()
: ExpressionNode(){};
};
/**
* \brief An identifier node, usually representing an object or a variable
* with an optional function name or child variable name respectively.
*
* The name of a function to call on an object or the behavior,
* for example: "MyObject.Function" or "MyObject.Physics".
*
* A variable, potentially with accessor to its child,
* for example: MyVariable or MyVariable.MyChild
*/
struct GD_CORE_API IdentifierNode
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
IdentifierNode(
const gd::String &identifierName_)
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(),
identifierName(identifierName_),
childIdentifierName(""){};
IdentifierNode(
const gd::String &identifierName_,
const gd::String &childIdentifierName_)
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(),
identifierName(identifierName_),
childIdentifierName(childIdentifierName_){};
virtual ~IdentifierNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitIdentifierNode(*this);
};
gd::String identifierName; ///< The object or variable name.
gd::String childIdentifierName; ///< The object function or variable child name.
ExpressionParserLocation
identifierNameLocation; ///< Location of the object or variable name.
ExpressionParserLocation
identifierNameDotLocation; ///< Location of the "." after the object or variable name.
ExpressionParserLocation childIdentifierNameLocation; ///< Location of object
/// function, behavior or
/// child variable name.
};
struct GD_CORE_API FunctionCallOrObjectFunctionNameOrEmptyNode
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
FunctionCallOrObjectFunctionNameOrEmptyNode()
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(){};
virtual ~FunctionCallOrObjectFunctionNameOrEmptyNode(){};
void Visit(ExpressionParser2NodeWorker &worker) override{};
};
struct GD_CORE_API VariableAccessorOrVariableBracketAccessorNode : public ExpressionNode {
VariableAccessorOrVariableBracketAccessorNode() : ExpressionNode(""){};
VariableAccessorOrVariableBracketAccessorNode() : ExpressionNode(){};
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode> child;
};
/**
* \brief A variable, potentially with accessor to its children.
*
* Example: MyVariable or MyVariable.MyChildren
* \brief A variable with bracket accessor or at least 2 "dot" accessors.
*
* Example: MyVariable[MyChildren] or MyVariable.MyChildren.MyGranChildren.
*
* Other cases like "MyVariable" or "MyVariable.MyChildren" are IdentifierNode
* to allow handling ambiguities.
*
* \see gd::IdentifierNode
* \see gd::VariableAccessorNode
* \see gd::VariableBracketAccessorNode
*/
struct GD_CORE_API VariableNode : public ExpressionNode {
VariableNode(const gd::String &type_,
const gd::String &name_,
const gd::String &objectName_)
: ExpressionNode(type_), name(name_), objectName(objectName_){};
struct GD_CORE_API VariableNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
VariableNode(const gd::String &name_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(), name(name_){};
virtual ~VariableNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitVariableNode(*this);
};
gd::String name;
gd::String objectName;
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
child; // Can be nullptr if no accessor
@@ -216,7 +272,8 @@ struct GD_CORE_API VariableNode : public ExpressionNode {
*/
struct GD_CORE_API VariableAccessorNode
: public VariableAccessorOrVariableBracketAccessorNode {
VariableAccessorNode(const gd::String &name_) : name(name_){};
VariableAccessorNode(const gd::String &name_)
: VariableAccessorOrVariableBracketAccessorNode(), name(name_){};
virtual ~VariableAccessorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitVariableAccessorNode(*this);
@@ -234,7 +291,7 @@ struct GD_CORE_API VariableAccessorNode
struct GD_CORE_API VariableBracketAccessorNode
: public VariableAccessorOrVariableBracketAccessorNode {
VariableBracketAccessorNode(std::unique_ptr<ExpressionNode> expression_)
: expression(std::move(expression_)){};
: VariableAccessorOrVariableBracketAccessorNode(), expression(std::move(expression_)){};
virtual ~VariableBracketAccessorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitVariableBracketAccessorNode(*this);
@@ -243,55 +300,26 @@ struct GD_CORE_API VariableBracketAccessorNode
std::unique_ptr<ExpressionNode> expression;
};
struct GD_CORE_API IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode
: public ExpressionNode {
IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(
const gd::String &type)
: ExpressionNode(type){};
};
/**
* \brief An identifier node, usually representing an object or a function name.
*/
struct GD_CORE_API IdentifierNode
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
IdentifierNode(const gd::String &identifierName_, const gd::String &type_)
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(type_),
identifierName(identifierName_){};
virtual ~IdentifierNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitIdentifierNode(*this);
};
gd::String identifierName;
};
struct GD_CORE_API FunctionCallOrObjectFunctionNameOrEmptyNode
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
FunctionCallOrObjectFunctionNameOrEmptyNode(const gd::String &type)
: IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode(type){};
virtual ~FunctionCallOrObjectFunctionNameOrEmptyNode(){};
void Visit(ExpressionParser2NodeWorker &worker) override{};
};
/**
* \brief The name of a function to call on an object or the behavior
* For example: "MyObject.Function" or "MyObject.Physics" or
* "MyObject.Physics::LinearVelocity".
* For example: "MyObject.Physics::LinearVelocity".
*
* Other cases like "MyObject.Function" or "MyObject.Physics" are IdentifierNode
* to allow handling ambiguities.
*
* \see gd::IdentifierNode
*/
struct GD_CORE_API ObjectFunctionNameNode
: public FunctionCallOrObjectFunctionNameOrEmptyNode {
ObjectFunctionNameNode(const gd::String &type_,
const gd::String &objectName_,
ObjectFunctionNameNode(const gd::String &objectName_,
const gd::String &objectFunctionOrBehaviorName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
objectName(objectName_),
objectFunctionOrBehaviorName(objectFunctionOrBehaviorName_) {}
ObjectFunctionNameNode(const gd::String &type_,
const gd::String &objectName_,
ObjectFunctionNameNode(const gd::String &objectName_,
const gd::String &behaviorName_,
const gd::String &behaviorFunctionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
objectName(objectName_),
objectFunctionOrBehaviorName(behaviorName_),
behaviorFunctionName(behaviorFunctionName_) {}
@@ -334,39 +362,24 @@ struct GD_CORE_API ObjectFunctionNameNode
*/
struct GD_CORE_API FunctionCallNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
/** \brief Construct a free function call node. */
FunctionCallNode(const gd::String &type_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
FunctionCallNode(const gd::String &functionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
functionName(functionName_){};
/** \brief Construct an object function call node. */
FunctionCallNode(const gd::String &type_,
const gd::String &objectName_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
FunctionCallNode(const gd::String &objectName_,
const gd::String &functionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
objectName(objectName_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
functionName(functionName_){};
/** \brief Construct a behavior function call node. */
FunctionCallNode(const gd::String &type_,
const gd::String &objectName_,
FunctionCallNode(const gd::String &objectName_,
const gd::String &behaviorName_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_),
: FunctionCallOrObjectFunctionNameOrEmptyNode(),
objectName(objectName_),
behaviorName(behaviorName_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
functionName(functionName_){};
virtual ~FunctionCallNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
@@ -376,7 +389,6 @@ struct GD_CORE_API FunctionCallNode : public FunctionCallOrObjectFunctionNameOrE
gd::String objectName;
gd::String behaviorName;
std::vector<std::unique_ptr<ExpressionNode>> parameters;
const ExpressionMetadata &expressionMetadata;
gd::String functionName;
ExpressionParserLocation
@@ -401,8 +413,8 @@ struct GD_CORE_API FunctionCallNode : public FunctionCallOrObjectFunctionNameOrE
* encountered and any other node could not make sense.
*/
struct GD_CORE_API EmptyNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
EmptyNode(const gd::String &type_, const gd::String &text_ = "")
: FunctionCallOrObjectFunctionNameOrEmptyNode(type_), text(text_){};
EmptyNode(const gd::String &text_ = "")
: FunctionCallOrObjectFunctionNameOrEmptyNode(), text(text_){};
virtual ~EmptyNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitEmptyNode(*this);

View File

@@ -91,6 +91,9 @@ class GD_CORE_API ExpressionParser2NodePrinter
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
output += node.identifierName;
if (!node.childIdentifierName.empty()) {
output += "." + node.childIdentifierName;
}
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.behaviorFunctionName.empty()) {

View File

@@ -4,6 +4,7 @@
* 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"
@@ -232,7 +233,8 @@ 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());
@@ -340,9 +342,10 @@ 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())
.SetAttribute("inverted", list[k].IsInverted());
instruction.AddChild("type").SetAttribute("value", list[k].GetType());
if (list[k].IsInverted())
instruction.GetChild("type").SetAttribute("inverted", true);
// Parameters
SerializerElement& parameters = instruction.AddChild("parameters");
@@ -352,9 +355,10 @@ void gd::EventsListSerialization::SerializeInstructionsTo(
.SetValue(list[k].GetParameter(l).GetPlainString());
// Sub instructions
SerializerElement& subInstructions =
instruction.AddChild("subInstructions");
SerializeInstructionsTo(list[k].GetSubInstructions(), subInstructions);
if (!list[k].GetSubInstructions().empty()) {
SerializeInstructionsTo(list[k].GetSubInstructions(),
instruction.AddChild("subInstructions"));
}
}
}

View File

@@ -29,7 +29,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension
.AddExpressionAndConditionAndAction(
"number",
"CameraX",
"CameraCenterX",
_("Camera center X position"),
_("the X position of the center of a camera"),
_("the X position of camera _PARAM4_ (layer: _PARAM3_)"),
@@ -43,15 +43,25 @@ 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",
"CameraY",
"CameraCenterY",
_("Camera center Y position"),
_("the Y position of the center of a camera"),
_("the Y position of camera _PARAM4_ (layer: _PARAM3_)"),
@@ -65,10 +75,20 @@ 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(
@@ -80,9 +100,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"",
"res/conditions/camera24.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"))
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"))
.AddParameter("expression", _("Camera number"), "", true)
.UseStandardParameters("number")
.MarkAsAdvanced();
@@ -96,9 +116,77 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"",
"res/conditions/camera24.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"))
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"))
.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)
.UseStandardParameters("number")
.MarkAsAdvanced();

View File

@@ -4,8 +4,10 @@
* 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"
@@ -29,6 +31,15 @@ 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,6 +72,13 @@ 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

@@ -13,7 +13,9 @@
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/String.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
using namespace std;
@@ -30,12 +32,9 @@ ExtensionAndMetadata<BehaviorMetadata>
MetadataProvider::GetExtensionAndBehaviorMetadata(const gd::Platform& platform,
gd::String behaviorType) {
for (auto& extension : platform.GetAllPlatformExtensions()) {
auto behaviorTypes = extension->GetBehaviorsTypes();
for (std::size_t j = 0; j < behaviorTypes.size(); ++j) {
if (behaviorTypes[j] == behaviorType)
return ExtensionAndMetadata<BehaviorMetadata>(
*extension, extension->GetBehaviorMetadata(behaviorType));
}
if (extension->HasBehavior(behaviorType))
return ExtensionAndMetadata<BehaviorMetadata>(
*extension, extension->GetBehaviorMetadata(behaviorType));
}
return ExtensionAndMetadata<BehaviorMetadata>(badExtension, badBehaviorMetadata);
@@ -202,8 +201,7 @@ MetadataProvider::GetExtensionAndBehaviorExpressionMetadata(
const gd::Platform& platform, gd::String autoType, gd::String exprType) {
auto& extensions = platform.GetAllPlatformExtensions();
for (auto& extension : extensions) {
const auto& autos = extension->GetBehaviorsTypes();
if (find(autos.begin(), autos.end(), autoType) != autos.end()) {
if (extension->HasBehavior(autoType)) {
const auto& allAutoExpressions =
extension->GetAllExpressionsForBehavior(autoType);
if (allAutoExpressions.find(exprType) != allAutoExpressions.end())
@@ -292,8 +290,7 @@ MetadataProvider::GetExtensionAndBehaviorStrExpressionMetadata(
const gd::Platform& platform, gd::String autoType, gd::String exprType) {
auto& extensions = platform.GetAllPlatformExtensions();
for (auto& extension : extensions) {
const auto& autos = extension->GetBehaviorsTypes();
if (find(autos.begin(), autos.end(), autoType) != autos.end()) {
if (extension->HasBehavior(autoType)) {
const auto& allBehaviorStrExpressions =
extension->GetAllStrExpressionsForBehavior(autoType);
if (allBehaviorStrExpressions.find(exprType) !=
@@ -350,28 +347,30 @@ const gd::ExpressionMetadata& MetadataProvider::GetAnyExpressionMetadata(
const gd::Platform& platform, gd::String exprType) {
const auto& numberExpressionMetadata =
GetExpressionMetadata(platform, exprType);
if (&numberExpressionMetadata != &badExpressionMetadata) {
return numberExpressionMetadata;
}
const auto& stringExpressionMetadata =
GetStrExpressionMetadata(platform, exprType);
return &numberExpressionMetadata != &badExpressionMetadata
? numberExpressionMetadata
: &stringExpressionMetadata != &badExpressionMetadata
? stringExpressionMetadata
: badExpressionMetadata;
if (&stringExpressionMetadata != &badExpressionMetadata) {
return stringExpressionMetadata;
}
return badExpressionMetadata;
}
const gd::ExpressionMetadata& MetadataProvider::GetObjectAnyExpressionMetadata(
const gd::Platform& platform, gd::String objectType, gd::String exprType) {
const auto& numberExpressionMetadata =
GetObjectExpressionMetadata(platform, objectType, exprType);
if (&numberExpressionMetadata != &badExpressionMetadata) {
return numberExpressionMetadata;
}
const auto& stringExpressionMetadata =
GetObjectStrExpressionMetadata(platform, objectType, exprType);
return &numberExpressionMetadata != &badExpressionMetadata
? numberExpressionMetadata
: &stringExpressionMetadata != &badExpressionMetadata
? stringExpressionMetadata
: badExpressionMetadata;
if (&stringExpressionMetadata != &badExpressionMetadata) {
return stringExpressionMetadata;
}
return badExpressionMetadata;
}
const gd::ExpressionMetadata&
@@ -380,14 +379,98 @@ MetadataProvider::GetBehaviorAnyExpressionMetadata(const gd::Platform& platform,
gd::String exprType) {
const auto& numberExpressionMetadata =
GetBehaviorExpressionMetadata(platform, autoType, exprType);
if (&numberExpressionMetadata != &badExpressionMetadata) {
return numberExpressionMetadata;
}
const auto& stringExpressionMetadata =
GetBehaviorStrExpressionMetadata(platform, autoType, exprType);
if (&stringExpressionMetadata != &badExpressionMetadata) {
return stringExpressionMetadata;
}
return badExpressionMetadata;
}
return &numberExpressionMetadata != &badExpressionMetadata
? numberExpressionMetadata
: &stringExpressionMetadata != &badExpressionMetadata
? stringExpressionMetadata
: badExpressionMetadata;
const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
FunctionCallNode& node) {
if (!node.behaviorName.empty()) {
gd::String behaviorType =
GetTypeOfBehavior(globalObjectsContainer, objectsContainer, node.behaviorName);
return MetadataProvider::GetBehaviorAnyExpressionMetadata(
platform, behaviorType, node.functionName);
}
else if (!node.objectName.empty()) {
gd::String objectType =
GetTypeOfObject(globalObjectsContainer, objectsContainer, node.objectName);
return MetadataProvider::GetObjectAnyExpressionMetadata(
platform, objectType, node.functionName);
}
return MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
}
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
FunctionCallNode& functionCall,
ExpressionNode& parameter) {
int parameterIndex = -1;
for (int i = 0; i < functionCall.parameters.size(); i++) {
if (functionCall.parameters.at(i).get() == &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;
}
MetadataProvider::~MetadataProvider() {}

View File

@@ -15,6 +15,8 @@ class ExpressionMetadata;
class ExpressionMetadata;
class Platform;
class PlatformExtension;
struct FunctionCallNode;
struct ExpressionNode;
} // namespace gd
namespace gd {
@@ -234,6 +236,26 @@ class GD_CORE_API MetadataProvider {
static const gd::ExpressionMetadata& GetObjectAnyExpressionMetadata(
const gd::Platform& platform, gd::String objectType, gd::String exprType);
static const gd::ExpressionMetadata& GetFunctionCallMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
FunctionCallNode& node);
static const gd::ParameterMetadata* GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
FunctionCallNode& functionCall,
ExpressionNode& parameter);
static const gd::ParameterMetadata* GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
FunctionCallNode& functionCall,
int parameterIndex);
/**
* Get information about an expression from its type.
* Works for behavior expressions.

View File

@@ -35,4 +35,18 @@ void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
name = element.GetStringAttribute("name");
}
// TODO factorize in a file with an enum and helpers?
const gd::String ParameterMetadata::numberType = "number";
const gd::String ParameterMetadata::stringType = "string";
const gd::String &ParameterMetadata::GetExpressionValueType(const gd::String &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,6 +207,15 @@ class GD_CORE_API ParameterMetadata {
return false;
}
/**
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
*/
static const gd::String &GetExpressionValueType(const gd::String &parameterType);
static const gd::String numberType;
static const gd::String stringType;
/** \name Serialization
*/
///@{

View File

@@ -14,7 +14,7 @@
namespace gd {
void ParameterMetadataTools::ParametersToObjectsContainer(
gd::Project& project,
const gd::Project& project,
const std::vector<gd::ParameterMetadata>& parameters,
gd::ObjectsContainer& outputObjectsContainer) {
outputObjectsContainer.GetObjects().clear();
@@ -59,13 +59,13 @@ void ParameterMetadataTools::IterateOverParameters(
const std::vector<gd::Expression>& parameters,
const std::vector<gd::ParameterMetadata>& parametersMetadata,
std::function<void(const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
const gd::Expression& parameterValue,
const gd::String& lastObjectName)> fn) {
IterateOverParametersWithIndex(
parameters,
parametersMetadata,
[&fn](const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
fn(parameterMetadata, parameterValue, lastObjectName);
@@ -76,17 +76,17 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
const std::vector<gd::Expression>& parameters,
const std::vector<gd::ParameterMetadata>& parametersMetadata,
std::function<void(const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName)> fn) {
gd::String lastObjectName = "";
for (std::size_t pNb = 0; pNb < parametersMetadata.size(); ++pNb) {
const gd::ParameterMetadata& parameterMetadata = parametersMetadata[pNb];
const gd::String& parameterValue =
const gd::Expression& parameterValue =
pNb < parameters.size() ? parameters[pNb].GetPlainString() : "";
const gd::String& parameterValueOrDefault =
parameterValue.empty() && parameterMetadata.optional
? parameterMetadata.GetDefaultValue()
const gd::Expression& parameterValueOrDefault =
parameterValue.GetPlainString().empty() && parameterMetadata.optional
? Expression(parameterMetadata.GetDefaultValue())
: parameterValue;
fn(parameterMetadata, parameterValueOrDefault, pNb, lastObjectName);
@@ -97,7 +97,7 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
// Search "lastObjectName" in the codebase for other place where this
// convention is enforced.
if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType()))
lastObjectName = parameterValueOrDefault;
lastObjectName = parameterValueOrDefault.GetPlainString();
}
}

View File

@@ -19,7 +19,7 @@ namespace gd {
class GD_CORE_API ParameterMetadataTools {
public:
static void ParametersToObjectsContainer(
gd::Project& project,
const gd::Project& project,
const std::vector<gd::ParameterMetadata>& parameters,
gd::ObjectsContainer& outputObjectsContainer);
@@ -32,7 +32,7 @@ class GD_CORE_API ParameterMetadataTools {
const std::vector<gd::Expression>& parameters,
const std::vector<gd::ParameterMetadata>& parametersMetadata,
std::function<void(const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
const gd::Expression& parameterValue,
const gd::String& lastObjectName)> fn);
/**
@@ -44,7 +44,7 @@ class GD_CORE_API ParameterMetadataTools {
const std::vector<gd::Expression>& parameters,
const std::vector<gd::ParameterMetadata>& parametersMetadata,
std::function<void(const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName)> fn);

View File

@@ -346,6 +346,11 @@ gd::BehaviorMetadata& PlatformExtension::GetBehaviorMetadata(
return badBehaviorMetadata;
}
bool PlatformExtension::HasBehavior(
const gd::String& behaviorType) const {
return behaviorsInfo.find(behaviorType) != behaviorsInfo.end();
}
gd::EffectMetadata& PlatformExtension::GetEffectMetadata(
const gd::String& effectName) {
if (effectsMetadata.find(effectName) != effectsMetadata.end())
@@ -375,7 +380,7 @@ gd::InstructionMetadata& PlatformExtension::AddDuplicatedAction(
auto copiedAction = actionsInfos.find(copiedNameWithNamespace);
if (copiedAction == actionsInfos.end()) {
gd::LogWarning("Could not find an action with name " +
gd::LogError("Could not find an action with name " +
copiedNameWithNamespace + " to copy.");
} else {
actionsInfos[newNameWithNamespace] = copiedAction->second;
@@ -395,7 +400,7 @@ gd::InstructionMetadata& PlatformExtension::AddDuplicatedCondition(
auto copiedCondition = conditionsInfos.find(copiedNameWithNamespace);
if (copiedCondition == conditionsInfos.end()) {
gd::LogWarning("Could not find a condition with name " +
gd::LogError("Could not find a condition with name " +
copiedNameWithNamespace + " to copy.");
} else {
conditionsInfos[newNameWithNamespace] = copiedCondition->second;
@@ -412,7 +417,7 @@ gd::ExpressionMetadata& PlatformExtension::AddDuplicatedExpression(
auto copiedExpression = expressionsInfos.find(copiedNameWithNamespace);
if (copiedExpression == expressionsInfos.end()) {
gd::LogWarning("Could not find an expression with name " +
gd::LogError("Could not find an expression with name " +
copiedNameWithNamespace + " to copy.");
} else {
expressionsInfos[newNameWithNamespace] = copiedExpression->second;
@@ -429,7 +434,7 @@ gd::ExpressionMetadata& PlatformExtension::AddDuplicatedStrExpression(
auto copiedExpression = strExpressionsInfos.find(copiedNameWithNamespace);
if (copiedExpression == strExpressionsInfos.end()) {
gd::LogWarning("Could not find a string expression with name " +
gd::LogError("Could not find a string expression with name " +
copiedNameWithNamespace + " to copy.");
} else {
strExpressionsInfos[newNameWithNamespace] = copiedExpression->second;

View File

@@ -467,6 +467,12 @@ class GD_CORE_API PlatformExtension {
*/
BehaviorMetadata& GetBehaviorMetadata(const gd::String& behaviorType);
/**
* \brief Return true if the extension contains a behavior associated to \a
* behaviorType
*/
bool HasBehavior(const gd::String& behaviorType) const;
/**
* \brief Return the metadata for the effect with the given name.
*/

View File

@@ -11,7 +11,6 @@
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
@@ -118,27 +117,20 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
instruction.GetParameters(),
metadata.GetParameters(),
[&](const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const gd::String& type = parameterMetadata.type;
if (gd::ParameterMetadata::IsBehavior(type)) {
if (lastObjectName == objectName) {
if (parameterValue == oldBehaviorName) {
if (parameterValue.GetPlainString() == oldBehaviorName) {
instruction.SetParameter(parameterIndex,
gd::Expression(newBehaviorName));
}
}
} else {
gd::ExpressionParser2 parser(
platform, GetGlobalObjectsContainer(), GetObjectsContainer());
auto node =
gd::ParameterMetadata::IsExpression("number", type)
? parser.ParseExpression("number", parameterValue)
: (gd::ParameterMetadata::IsExpression("string", type)
? parser.ParseExpression("string", parameterValue)
: std::unique_ptr<gd::ExpressionNode>());
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionBehaviorRenamer renamer(GetGlobalObjectsContainer(),
GetObjectsContainer(),

View File

@@ -9,7 +9,6 @@
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
@@ -17,6 +16,7 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
@@ -31,7 +31,17 @@ namespace gd {
class GD_CORE_API ExpressionObjectsAnalyzer
: public ExpressionParser2NodeWorker {
public:
ExpressionObjectsAnalyzer(EventsContext& context_) : context(context_){};
ExpressionObjectsAnalyzer(
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String &rootType_,
EventsContext& context_) :
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
rootType(rootType_),
context(context_){};
virtual ~ExpressionObjectsAnalyzer(){};
protected:
@@ -59,7 +69,8 @@ class GD_CORE_API ExpressionObjectsAnalyzer
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (gd::ParameterMetadata::IsObject(node.type)) {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
context.AddObjectName(node.identifierName);
}
}
@@ -87,6 +98,11 @@ class GD_CORE_API ExpressionObjectsAnalyzer
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::String rootType;
EventsContext& context;
};
@@ -102,7 +118,7 @@ bool EventsContextAnalyzer::DoVisitInstruction(gd::Instruction& instruction,
instruction.GetParameters(),
instrInfo.parameters,
[this](const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
const gd::Expression& parameterValue,
const gd::String& lastObjectName) {
AnalyzeParameter(platform,
project,
@@ -129,16 +145,14 @@ void EventsContextAnalyzer::AnalyzeParameter(
if (ParameterMetadata::IsObject(type)) {
context.AddObjectName(value);
} else if (ParameterMetadata::IsExpression("number", type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", value);
auto node = parameter.GetRootNode();
ExpressionObjectsAnalyzer analyzer(context);
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "number", context);
node->Visit(analyzer);
} else if (ParameterMetadata::IsExpression("string", type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", value);
auto node = parameter.GetRootNode();
ExpressionObjectsAnalyzer analyzer(context);
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "string", context);
node->Visit(analyzer);
} else if (ParameterMetadata::IsBehavior(type)) {
context.AddBehaviorName(lastObjectName, value);

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/Project/ObjectsContainer.h"
#include "GDCore/IDE/Events/InstructionSentenceFormatter.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
using namespace std;
@@ -34,18 +34,30 @@ const gd::String EventsRefactorer::searchIgnoredCharacters = ";:,#()";
*/
class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
public:
ExpressionObjectRenamer(const gd::String& objectName_,
ExpressionObjectRenamer(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String &rootType_,
const gd::String& objectName_,
const gd::String& objectNewName_)
: hasDoneRenaming(false),
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
rootType(rootType_),
hasDoneRenaming(false),
objectName(objectName_),
objectNewName(objectNewName_){};
virtual ~ExpressionObjectRenamer(){};
static bool Rename(gd::ExpressionNode& node,
static bool Rename(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String &rootType,
gd::ExpressionNode& node,
const gd::String& objectName,
const gd::String& objectNewName) {
if (ExpressionValidator::HasNoErrors(node)) {
ExpressionObjectRenamer renamer(objectName, objectNewName);
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
ExpressionObjectRenamer renamer(platform, globalObjectsContainer, objectsContainer, rootType, objectName, objectNewName);
node.Visit(renamer);
return renamer.HasDoneRenaming();
@@ -81,7 +93,8 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (gd::ParameterMetadata::IsObject(node.type) &&
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (gd::ParameterMetadata::IsObject(type) &&
node.identifierName == objectName) {
hasDoneRenaming = true;
node.identifierName = objectNewName;
@@ -108,6 +121,11 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
bool hasDoneRenaming;
const gd::String& objectName;
const gd::String& objectNewName;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::String rootType;
};
/**
@@ -118,14 +136,27 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
*/
class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
public:
ExpressionObjectFinder(const gd::String& objectName_)
: hasObject(false), objectName(objectName_){};
ExpressionObjectFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String &rootType_,
const gd::String& objectName_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
rootType(rootType_),
hasObject(false),
objectName(objectName_){};
virtual ~ExpressionObjectFinder(){};
static bool CheckIfHasObject(gd::ExpressionNode& node,
static bool CheckIfHasObject(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String &rootType,
gd::ExpressionNode& node,
const gd::String& objectName) {
if (ExpressionValidator::HasNoErrors(node)) {
ExpressionObjectFinder finder(objectName);
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
ExpressionObjectFinder finder(platform, globalObjectsContainer, objectsContainer, rootType, objectName);
node.Visit(finder);
return finder.HasFoundObject();
@@ -161,7 +192,8 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (gd::ParameterMetadata::IsObject(node.type) &&
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (gd::ParameterMetadata::IsObject(type) &&
node.identifierName == objectName) {
hasObject = true;
}
@@ -184,6 +216,11 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
private:
bool hasObject;
const gd::String& objectName;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::String rootType;
};
bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
@@ -205,11 +242,9 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", actions[aId].GetParameter(pNb).GetPlainString());
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
actions[aId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -217,11 +252,9 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"string", actions[aId].GetParameter(pNb).GetPlainString());
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
actions[aId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -263,11 +296,9 @@ bool EventsRefactorer::RenameObjectInConditions(
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", conditions[cId].GetParameter(pNb).GetPlainString());
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
conditions[cId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -275,11 +306,9 @@ bool EventsRefactorer::RenameObjectInConditions(
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"string", conditions[cId].GetParameter(pNb).GetPlainString());
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
conditions[cId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -316,20 +345,18 @@ bool EventsRefactorer::RenameObjectInEventParameters(
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression("number",
parameterMetadata.GetType())) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", expression.GetPlainString());
auto node = expression.GetRootNode();
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression("string",
parameterMetadata.GetType())) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", expression.GetPlainString());
auto node = expression.GetRootNode();
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
@@ -405,11 +432,9 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", actions[aId].GetParameter(pNb).GetPlainString());
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "number", *node, name)) {
deleteMe = true;
break;
}
@@ -417,11 +442,9 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"string", actions[aId].GetParameter(pNb).GetPlainString());
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "string", *node, name)) {
deleteMe = true;
break;
}
@@ -469,11 +492,9 @@ bool EventsRefactorer::RemoveObjectInConditions(
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", conditions[cId].GetParameter(pNb).GetPlainString());
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "number", *node, name)) {
deleteMe = true;
break;
}
@@ -481,11 +502,9 @@ bool EventsRefactorer::RemoveObjectInConditions(
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"string", conditions[cId].GetParameter(pNb).GetPlainString());
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(platform, project, layout, "string", *node, name)) {
deleteMe = true;
break;
}
@@ -535,16 +554,19 @@ void EventsRefactorer::RemoveObjectInEvents(const gd::Platform& platform,
}
}
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) {
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;
for (std::size_t i = 0; i < events.size(); ++i) {
bool eventModified = false;
if (inConditions) {
vector<gd::InstructionsList*> conditionsVectors =
events[i].GetAllConditionsVectors();
@@ -556,6 +578,13 @@ void EventsRefactorer::ReplaceStringInEvents(gd::ObjectsContainer& project,
toReplace,
newString,
matchCase);
if (conditionsModified && !eventModified) {
modifiedEvents.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
i));
eventModified = true;
}
}
}
@@ -569,25 +598,45 @@ void EventsRefactorer::ReplaceStringInEvents(gd::ObjectsContainer& project,
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())
ReplaceStringInEvents(project,
layout,
events[i].GetSubEvents(),
toReplace,
newString,
matchCase,
inConditions,
inActions,
inEventStrings);
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));
}
}
return modifiedEvents;
}
gd::String ReplaceAllOccurencesCaseUnsensitive(gd::String context,
@@ -718,14 +767,16 @@ 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(), ' ');
@@ -737,8 +788,11 @@ 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,
@@ -751,9 +805,11 @@ 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,
@@ -790,12 +846,11 @@ 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) {
@@ -826,27 +881,30 @@ bool EventsRefactorer::SearchStringInActions(
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,6 +7,7 @@
#define GDCORE_EVENTSREFACTORER_H
#include <memory>
#include <vector>
#include "GDCore/Events/Instruction.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/String.h"
@@ -18,7 +19,7 @@ class ExternalEvents;
class BaseEvent;
class Instruction;
typedef std::shared_ptr<gd::BaseEvent> BaseEventSPtr;
}
} // namespace gd
namespace gd {
@@ -43,10 +44,11 @@ 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; }
@@ -56,7 +58,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(); }
};
/**
@@ -98,27 +100,31 @@ 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 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);
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);
virtual ~EventsRefactorer(){};
@@ -148,20 +154,21 @@ 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,7 +7,6 @@
#include "EventsVariablesFinder.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
@@ -32,10 +31,16 @@ namespace gd {
class GD_CORE_API ExpressionParameterSearcher
: public ExpressionParser2NodeWorker {
public:
ExpressionParameterSearcher(std::set<gd::String>& results_,
ExpressionParameterSearcher(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
std::set<gd::String>& results_,
const gd::String& parameterType_,
const gd::String& objectName_ = "")
: results(results_),
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
results(results_),
parameterType(parameterType_),
objectName(objectName_){};
virtual ~ExpressionParameterSearcher(){};
@@ -68,10 +73,22 @@ class GD_CORE_API ExpressionParameterSearcher
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
bool considerFunction = objectName.empty() || node.objectName == objectName;
const gd::ExpressionMetadata &metadata = node.objectName.empty() ?
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName) :
MetadataProvider::GetObjectAnyExpressionMetadata(
platform,
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
node.functionName);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
return;
}
for (size_t i = 0; i < node.parameters.size() &&
i < node.expressionMetadata.parameters.size();
i < metadata.parameters.size();
++i) {
auto& parameterMetadata = node.expressionMetadata.parameters[i];
auto& parameterMetadata = metadata.parameters[i];
if (considerFunction && parameterMetadata.GetType() == parameterType) {
// Store the value of the parameter
results.insert(
@@ -84,6 +101,10 @@ class GD_CORE_API ExpressionParameterSearcher
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
gd::String parameterType; ///< The type of the parameters to be searched for.
@@ -165,24 +186,18 @@ std::set<gd::String> EventsVariablesFinder::FindArgumentsInInstructions(
}
// Search in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", instructions[aId].GetParameter(pNb).GetPlainString());
ExpressionParameterSearcher searcher(
results, parameterType, objectName);
node->Visit(searcher);
}
// Search in gd::String expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression(
"number", instructions[aId].GetParameter(pNb).GetPlainString());
auto node = instructions[aId].GetParameter(pNb).GetRootNode();
ExpressionParameterSearcher searcher(
results, parameterType, objectName);
platform,
project,
layout,
results,
parameterType,
objectName);
node->Visit(searcher);
}
// Remember the value of the last "object" parameter.

View File

@@ -15,6 +15,8 @@
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
namespace gd {
class Expression;
@@ -290,7 +292,11 @@ class GD_CORE_API ExpressionCompletionFinder
* and returns completions for it.
*/
static std::vector<ExpressionCompletionDescription>
GetCompletionDescriptionsFor(gd::ExpressionNode& node,
GetCompletionDescriptionsFor(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String &rootType,
gd::ExpressionNode& node,
size_t searchedPosition) {
gd::ExpressionNodeLocationFinder finder(searchedPosition);
node.Visit(finder);
@@ -303,6 +309,7 @@ class GD_CORE_API ExpressionCompletionFinder
gd::ExpressionNode* maybeParentNodeAtLocation = finder.GetParentNode();
gd::ExpressionCompletionFinder autocompletionProvider(
platform, globalObjectsContainer, objectsContainer, rootType,
searchedPosition, maybeParentNodeAtLocation);
nodeAtLocation->Visit(autocompletionProvider);
return autocompletionProvider.GetCompletionDescriptions();
@@ -320,19 +327,21 @@ class GD_CORE_API ExpressionCompletionFinder
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, "", searchedPosition + 1, searchedPosition + 1));
type, "", searchedPosition + 1, searchedPosition + 1));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, "", searchedPosition + 1, searchedPosition + 1));
type, "", searchedPosition + 1, searchedPosition + 1));
}
void OnVisitOperatorNode(OperatorNode& node) override {
// No completions.
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, "", searchedPosition + 1, searchedPosition + 1));
type, "", searchedPosition + 1, searchedPosition + 1));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, "", searchedPosition + 1, searchedPosition + 1));
type, "", searchedPosition + 1, searchedPosition + 1));
}
void OnVisitNumberNode(NumberNode& node) override {
// No completions
@@ -344,6 +353,7 @@ class GD_CORE_API ExpressionCompletionFinder
FunctionCallNode* functionCall =
dynamic_cast<FunctionCallNode*>(maybeParentNodeAtLocation);
if (functionCall != nullptr) {
int parameterIndex = -1;
for (int i = 0; i < functionCall->parameters.size(); i++) {
if (functionCall->parameters.at(i).get() == &node) {
@@ -359,15 +369,16 @@ class GD_CORE_API ExpressionCompletionFinder
size_t metadataParameterIndex =
ExpressionParser2::WrittenParametersFirstIndex(
functionCall->objectName, functionCall->behaviorName);
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
platform, globalObjectsContainer, objectsContainer, *functionCall);
const gd::ParameterMetadata* parameterMetadata = nullptr;
while (metadataParameterIndex <
functionCall->expressionMetadata.parameters.size()) {
if (!functionCall->expressionMetadata.parameters[metadataParameterIndex]
metadata.parameters.size()) {
if (!metadata.parameters[metadataParameterIndex]
.IsCodeOnly()) {
if (visibleParameterIndex == parameterIndex) {
parameterMetadata = &functionCall->expressionMetadata
.parameters[metadataParameterIndex];
parameterMetadata = &metadata.parameters[metadataParameterIndex];
}
visibleParameterIndex++;
}
@@ -398,12 +409,21 @@ class GD_CORE_API ExpressionCompletionFinder
}
}
void OnVisitVariableNode(VariableNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform,
globalObjectsContainer,
objectsContainer,
// Variable fields doesn't use expression completion,
// so the object will be found inside the expression itself.
"",
node);
completions.push_back(ExpressionCompletionDescription::ForVariable(
node.type,
type,
node.name,
node.location.GetStartPosition(),
node.location.GetEndPosition(),
node.objectName));
objectName));
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
// No completions
@@ -413,35 +433,69 @@ class GD_CORE_API ExpressionCompletionFinder
// No completions
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (gd::ParameterMetadata::IsObject(node.type)) {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
// Only show completions of objects if an object is required
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform,
globalObjectsContainer,
objectsContainer,
// Variable fields doesn't use expression completion,
// so the object will be found inside the expression itself.
"",
node);
completions.push_back(ExpressionCompletionDescription::ForVariable(
type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition(),
objectName));
} else {
// Show completions for expressions and objects otherwise.
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
// Object function or behavior name
if (IsCaretOn(node.identifierNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
type,
node.identifierName,
node.identifierNameLocation.GetStartPosition(),
node.identifierNameLocation.GetEndPosition()));
if (!node.identifierNameDotLocation.IsValid()) {
completions.push_back(ExpressionCompletionDescription::ForExpression(
type,
node.identifierName,
node.identifierNameLocation.GetStartPosition(),
node.identifierNameLocation.GetEndPosition()));
}
} else if (IsCaretOn(node.identifierNameDotLocation) ||
IsCaretOn(node.childIdentifierNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.childIdentifierName,
node.childIdentifierNameLocation.GetStartPosition(),
node.childIdentifierNameLocation.GetEndPosition(),
node.identifierName));
completions.push_back(ExpressionCompletionDescription::ForExpression(
type,
node.childIdentifierName,
node.childIdentifierNameLocation.GetStartPosition(),
node.childIdentifierNameLocation.GetEndPosition(),
node.identifierName));
}
}
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
if (!node.behaviorFunctionName.empty() ||
node.behaviorNameNamespaceSeparatorLocation.IsValid()) {
// Behavior function (or behavior function being written, with the
// function name missing)
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
@@ -455,7 +509,7 @@ class GD_CORE_API ExpressionCompletionFinder
} else if (IsCaretOn(node.behaviorNameNamespaceSeparatorLocation) ||
IsCaretOn(node.behaviorFunctionNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
type,
node.behaviorFunctionName,
node.behaviorFunctionNameLocation.GetStartPosition(),
node.behaviorFunctionNameLocation.GetEndPosition(),
@@ -466,7 +520,7 @@ class GD_CORE_API ExpressionCompletionFinder
// Object function or behavior name
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
@@ -478,7 +532,7 @@ class GD_CORE_API ExpressionCompletionFinder
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
node.objectName));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
type,
node.objectFunctionOrBehaviorName,
node.objectFunctionOrBehaviorNameLocation.GetStartPosition(),
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
@@ -487,6 +541,7 @@ class GD_CORE_API ExpressionCompletionFinder
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
bool isCaretOnParenthesis = IsCaretOn(node.openingParenthesisLocation) ||
IsCaretOn(node.closingParenthesisLocation);
@@ -494,7 +549,7 @@ class GD_CORE_API ExpressionCompletionFinder
// Behavior function
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
@@ -507,7 +562,7 @@ class GD_CORE_API ExpressionCompletionFinder
node.objectName));
} else {
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition(),
@@ -519,7 +574,7 @@ class GD_CORE_API ExpressionCompletionFinder
// Object function
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
@@ -538,7 +593,7 @@ class GD_CORE_API ExpressionCompletionFinder
}
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition(),
@@ -548,7 +603,7 @@ class GD_CORE_API ExpressionCompletionFinder
} else {
// Free function
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition())
@@ -556,13 +611,14 @@ class GD_CORE_API ExpressionCompletionFinder
}
}
void OnVisitEmptyNode(EmptyNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
type,
node.text,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
type,
node.text,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
@@ -578,14 +634,27 @@ class GD_CORE_API ExpressionCompletionFinder
(inclusive && searchedPosition <= location.GetEndPosition())));
}
ExpressionCompletionFinder(size_t searchedPosition_,
ExpressionCompletionFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String &rootType_,
size_t searchedPosition_,
gd::ExpressionNode* maybeParentNodeAtLocation_)
: searchedPosition(searchedPosition_),
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
rootType(rootType_),
searchedPosition(searchedPosition_),
maybeParentNodeAtLocation(maybeParentNodeAtLocation_){};
std::vector<ExpressionCompletionDescription> completions;
size_t searchedPosition;
gd::ExpressionNode* maybeParentNodeAtLocation;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::String rootType;
};
} // namespace gd

View File

@@ -0,0 +1,123 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONLEFTSIDETYPEFINDER_H
#define GDCORE_EXPRESSIONLEFTSIDETYPEFINDER_H
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/Tools/Localization.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief Find the type of the node at the left side of operations.
*
* \see gd::ExpressionTypeFinder
*/
class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWorker {
public:
/**
* \brief Helper function to find the type of the node at the left side of
* operations.
*/
static const gd::String GetType(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
gd::ExpressionNode& node) {
gd::ExpressionLeftSideTypeFinder typeFinder(
platform, globalObjectsContainer, objectsContainer);
node.Visit(typeFinder);
return typeFinder.GetType();
}
virtual ~ExpressionLeftSideTypeFinder(){};
protected:
ExpressionLeftSideTypeFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
type("unknown") {};
const gd::String &GetType() {
return type;
};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
node.factor->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
node.expression->Visit(*this);
}
void OnVisitNumberNode(NumberNode& node) override {
type = "number";
}
void OnVisitTextNode(TextNode& node) override {
type = "string";
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
platform, globalObjectsContainer, objectsContainer, node);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
type = "unknown";
}
else {
type = metadata.GetReturnType();
}
}
void OnVisitVariableNode(VariableNode& node) override {
type = "unknown";
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
type = "unknown";
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
type = "unknown";
}
void OnVisitEmptyNode(EmptyNode& node) override {
type = "unknown";
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
type = "unknown";
}
private:
gd::String type;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::String rootType;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONLEFTSIDETYPEFINDER_H

View File

@@ -0,0 +1,35 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
using namespace std;
namespace gd {
// TODO factorize in a file with an enum and helpers?
const gd::String ExpressionTypeFinder::unknownType = "unknown";
const gd::String ExpressionTypeFinder::numberType = "number";
const gd::String ExpressionTypeFinder::stringType = "string";
const gd::String ExpressionTypeFinder::numberOrStringType = "number|string";
} // namespace gd

View File

@@ -0,0 +1,198 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONTYPEFINDER_H
#define GDCORE_EXPRESSIONTYPEFINDER_H
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/IDE/Events/ExpressionLeftSideTypeFinder.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/Tools/Localization.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief Find the type of the expression or sub-expression that a given node
* represents.
*
* The type returned by this worker is a mix of:
* - an expected type looking up like a parameter declaration
* - an actual type looking down, but only looking at the most left branch
* (using ExpressionLeftSideTypeFinder)
*
* This logic was built with the constraint of following a parser that can't
* know the right side. Now that it is extracted, it could be enhanced if needed.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
public:
/**
* \brief Helper function to find the type of the expression or
* sub-expression that a given node represents.
*/
static const gd::String GetType(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String &rootType,
gd::ExpressionNode& node) {
gd::ExpressionTypeFinder typeFinder(
platform, globalObjectsContainer, objectsContainer, rootType);
node.Visit(typeFinder);
return typeFinder.GetType();
}
virtual ~ExpressionTypeFinder(){};
protected:
ExpressionTypeFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String &rootType_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
rootType(rootType_),
type(ExpressionTypeFinder::unknownType),
child(nullptr) {};
const gd::String &GetType() {
return gd::ParameterMetadata::GetExpressionValueType(type);
};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
VisitParent(node);
}
void OnVisitOperatorNode(OperatorNode& node) override {
VisitParent(node);
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
VisitParent(node);
}
void OnVisitNumberNode(NumberNode& node) override {
type = ExpressionTypeFinder::numberType;
}
void OnVisitTextNode(TextNode& node) override {
type = ExpressionTypeFinder::stringType;
}
void OnVisitVariableNode(VariableNode& node) override {
VisitParent(node);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
VisitParent(node);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
VisitParent(node);
}
void OnVisitEmptyNode(EmptyNode& node) override {
VisitParent(node);
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
VisitParent(node);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
if (child == nullptr) {
type = ExpressionTypeFinder::unknownType;
}
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
platform,
globalObjectsContainer,
objectsContainer,
node);
if (leftSideType == ExpressionTypeFinder::numberType
|| leftSideType == ExpressionTypeFinder::stringType) {
type = leftSideType;
}
else {
type = ExpressionTypeFinder::numberOrStringType;
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (child == nullptr) {
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
platform, globalObjectsContainer, objectsContainer, node);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
VisitParent(node);
}
else {
type = metadata.GetReturnType();
}
}
else {
const gd::ParameterMetadata* parameterMetadata =
gd::MetadataProvider::GetFunctionCallParameterMetadata(
platform,
globalObjectsContainer,
objectsContainer,
node,
*child);
if (parameterMetadata == nullptr || parameterMetadata->GetType().empty()) {
type = ExpressionTypeFinder::unknownType;
}
else {
type = parameterMetadata->GetType();
}
}
}
private:
inline void VisitParent(ExpressionNode& node) {
child = &node;
if (node.parent != nullptr) {
node.parent->Visit(*this);
}
else if (rootType == ExpressionTypeFinder::numberOrStringType) {
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
platform,
globalObjectsContainer,
objectsContainer,
node);
if (leftSideType == ExpressionTypeFinder::numberType
|| leftSideType == ExpressionTypeFinder::stringType) {
type = leftSideType;
}
else {
type = rootType;
}
}
else {
type = rootType;
}
}
static const gd::String unknownType;
static const gd::String numberType;
static const gd::String stringType;
static const gd::String numberOrStringType;
gd::String type;
ExpressionNode *child;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::String rootType;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONTYPEFINDER_H

View File

@@ -0,0 +1,300 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
using namespace std;
namespace gd {
namespace {
/**
* Return the minimum number of parameters, starting from a given parameter
* (by convention, 1 for object functions and 2 for behavior functions).
*/
size_t GetMinimumParametersNumber(
const std::vector<gd::ParameterMetadata>& parameters,
size_t initialParameterIndex) {
size_t nb = 0;
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
if (!parameters[i].optional && !parameters[i].codeOnly) nb++;
}
return nb;
}
/**
* Return the maximum number of parameters, starting from a given parameter
* (by convention, 1 for object functions and 2 for behavior functions).
*/
size_t GetMaximumParametersNumber(
const std::vector<gd::ParameterMetadata>& parameters,
size_t initialParameterIndex) {
size_t nb = 0;
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
if (!parameters[i].codeOnly) nb++;
}
return nb;
}
} // namespace
ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::FunctionCallNode& function) {
ReportAnyError(function);
gd::String objectType = function.objectName.empty() ? "" :
GetTypeOfObject(globalObjectsContainer, objectsContainer, function.objectName);
gd::String behaviorType = function.behaviorName.empty() ? "" :
GetTypeOfBehavior(globalObjectsContainer, objectsContainer, function.behaviorName);
const gd::ExpressionMetadata &metadata = function.behaviorName.empty() ?
function.objectName.empty() ?
MetadataProvider::GetAnyExpressionMetadata(platform, function.functionName) :
MetadataProvider::GetObjectAnyExpressionMetadata(
platform, objectType, function.functionName) :
MetadataProvider::GetBehaviorAnyExpressionMetadata(
platform, behaviorType, function.functionName);
if (!function.objectName.empty()) {
// If the function needs a capability on the object that may not be covered
// by all objects, check it now.
if (!metadata.GetRequiredBaseObjectCapability().empty()) {
const gd::ObjectMetadata &objectMetadata =
MetadataProvider::GetObjectMetadata(platform, objectType);
if (objectMetadata.IsUnsupportedBaseObjectCapability(
metadata.GetRequiredBaseObjectCapability())) {
RaiseTypeError(
_("This expression exists, but it can't be used on this object."),
function.objectNameLocation);
return StringToType(metadata.GetReturnType());
}
}
}
Type returnType = StringToType(metadata.GetReturnType());
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
RaiseError(
"invalid_function_name",
_("Cannot find an expression with this name: ") +
function.functionName + "\n" +
_("Double check that you've not made any typo in the name."),
function.location);
return returnType;
}
// Validate the type of the function
if (returnType == Type::Number) {
if (parentType == Type::String) {
RaiseTypeError(
_("You tried to use an expression that returns a number, but a "
"string is expected. Use `ToString` if you need to convert a "
"number to a string."),
function.location);
return returnType;
}
else if (parentType != Type::Number && parentType != Type::NumberOrString) {
RaiseTypeError(_("You tried to use an expression that returns a "
"number, but another type is expected:") +
" " + TypeToString(parentType),
function.location);
return returnType;
}
} else if (returnType == Type::String) {
if (parentType == Type::Number) {
RaiseTypeError(
_("You tried to use an expression that returns a string, but a "
"number is expected. Use `ToNumber` if you need to convert a "
"string to a number."),
function.location);
return returnType;
}
else if (parentType != Type::String && parentType != Type::NumberOrString) {
RaiseTypeError(_("You tried to use an expression that returns a "
"string, but another type is expected:") +
" " + TypeToString(parentType),
function.location);
return returnType;
}
} else {
if (parentType != returnType) {
RaiseTypeError(
_("You tried to use an expression with the wrong return type:") + " " +
TypeToString(returnType),
function.location);
return returnType;
}
}
// Validate parameters count
size_t minParametersCount = GetMinimumParametersNumber(
metadata.parameters,
ExpressionParser2::WrittenParametersFirstIndex(function.objectName, function.behaviorName));
size_t maxParametersCount = GetMaximumParametersNumber(
metadata.parameters,
ExpressionParser2::WrittenParametersFirstIndex(function.objectName, function.behaviorName));
if (function.parameters.size() < minParametersCount ||
function.parameters.size() > maxParametersCount) {
gd::String expectedCountMessage =
minParametersCount == maxParametersCount
? _("The number of parameters must be exactly ") +
gd::String::From(minParametersCount)
: _("The number of parameters must be: ") +
gd::String::From(minParametersCount) + "-" +
gd::String::From(maxParametersCount);
if (function.parameters.size() < minParametersCount) {
RaiseError(
"too_few_parameters",
_("You have not entered enough parameters for the expression.") + " " +
expectedCountMessage,
function.location);
}
else {
RaiseError(
"extra_parameter",
_("This parameter was not expected by this expression. Remove it "
"or verify that you've entered the proper expression name.") + " " +
expectedCountMessage,
ExpressionParserLocation(
function.parameters[maxParametersCount]->location.GetStartPosition(),
function.location.GetEndPosition() - 1));
}
return returnType;
}
// TODO: reverse the order of diagnostic?
size_t writtenParametersFirstIndex =
ExpressionParser2::WrittenParametersFirstIndex(
function.objectName, function.behaviorName);
int metadataIndex = writtenParametersFirstIndex;
for (int parameterIndex = 0; parameterIndex < function.parameters.size(); parameterIndex++) {
auto& parameter = function.parameters[parameterIndex];
while (metadata.GetParameters()[metadataIndex].IsCodeOnly()) {
// The sizes are already checked above.
metadataIndex++;
}
auto& parameterMetadata = metadata.GetParameters()[metadataIndex];
if (!parameterMetadata.IsOptional() || dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) {
auto currentParentType = parentType;
parentType = StringToType(parameterMetadata.GetType());
parameter->Visit(*this);
parentType = currentParentType;
const gd::String &expectedParameterType = parameterMetadata.GetType();
if (gd::ParameterMetadata::IsExpression(
ExpressionValidator::variableTypeString, expectedParameterType)) {
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr
&& dynamic_cast<VariableNode *>(parameter.get()) == nullptr) {
RaiseError(
"malformed_variable_parameter",
_("A variable name was expected but something else was "
"written. Enter just the name of the variable for this "
"parameter."),
parameter->location);
}
}
else if (gd::ParameterMetadata::IsObject(expectedParameterType)) {
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr) {
RaiseError(
"malformed_object_parameter",
_("An object name was expected but something else was "
"written. Enter just the name of the object for this "
"parameter."),
parameter->location);
}
}
// String and number are already checked in children.
else if (!gd::ParameterMetadata::IsExpression(
ExpressionValidator::numberTypeString, expectedParameterType)
&& !gd::ParameterMetadata::IsExpression(
ExpressionValidator::stringTypeString, expectedParameterType)) {
RaiseError(
"unknown_parameter_type",
_("This function is improperly set up. Reach out to the "
"extension developer or a GDevelop maintainer to fix "
"this issue"),
parameter->location);
}
}
metadataIndex++;
}
return returnType;
}
// TODO factorize in a file with an enum and helpers?
const gd::String ExpressionValidator::unknownTypeString = "unknown";
const gd::String ExpressionValidator::numberTypeString = "number";
const gd::String ExpressionValidator::stringTypeString = "string";
const gd::String ExpressionValidator::numberOrStringTypeString = "number|string";
const gd::String ExpressionValidator::variableTypeString = "variable";
const gd::String ExpressionValidator::objectTypeString = "object";
const gd::String ExpressionValidator::emptyTypeString = "empty";
const gd::String &ExpressionValidator::TypeToString(Type type) {
switch (type) {
case Type::Unknown:
return unknownTypeString;
case Type::Number:
return numberTypeString;
case Type::String:
return stringTypeString;
case Type::NumberOrString:
return numberOrStringTypeString;
case Type::Variable:
return variableTypeString;
case Type::Object:
return objectTypeString;
case Type::Empty:
return emptyTypeString;
}
return unknownTypeString;
}
ExpressionValidator::Type ExpressionValidator::StringToType(const gd::String &type) {
if (type == ExpressionValidator::numberTypeString
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::numberTypeString, type)) {
return Type::Number;
}
if (type == ExpressionValidator::stringTypeString
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::stringTypeString, type)) {
return Type::String;
}
if (type == ExpressionValidator::numberOrStringTypeString) {
return Type::NumberOrString;
}
if (type == ExpressionValidator::variableTypeString
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::variableTypeString, type)) {
return Type::Variable;
}
if (type == ExpressionValidator::objectTypeString
|| gd::ParameterMetadata::IsObject(type)) {
return Type::Object;
}
return Type::Unknown;
}
} // namespace gd

View File

@@ -10,6 +10,10 @@
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
namespace gd {
class Expression;
class ObjectsContainer;
@@ -28,15 +32,27 @@ namespace gd {
*/
class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
public:
ExpressionValidator(){};
ExpressionValidator(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String &rootType_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))),
childType(Type::Unknown) {};
virtual ~ExpressionValidator(){};
/**
* \brief Helper function to check if a given node does not contain
* any error.
*/
static bool HasNoErrors(gd::ExpressionNode& node) {
gd::ExpressionValidator validator;
static bool HasNoErrors(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String &rootType,
gd::ExpressionNode& node) {
gd::ExpressionValidator validator(platform, globalObjectsContainer, objectsContainer, rootType);
node.Visit(validator);
return validator.GetErrors().empty();
}
@@ -56,52 +72,247 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
ReportAnyError(node);
node.leftHandSide->Visit(*this);
const Type leftType = childType;
if (leftType == Type::Number) {
if (node.op == ' ') {
RaiseError("syntax_error",
"No operator found. Did you forget to enter an operator (like +, -, "
"* or /) between numbers or expressions?", node.rightHandSide->location);
}
}
else if (leftType == Type::String) {
if (node.op == ' ') {
RaiseError("syntax_error",
"You must add the operator + between texts or expressions. For "
"example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
}
else if (node.op != '+') {
RaiseOperatorError(
_("You've used an operator that is not supported. Only + can be used "
"to concatenate texts."),
ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
}
} else if (leftType == Type::Object) {
RaiseOperatorError(
_("Operators (+, -, /, *) can't be used with an object name. Remove "
"the operator."),
node.rightHandSide->location);
} else if (leftType == Type::Variable) {
RaiseOperatorError(
_("Operators (+, -, /, *) can't be used in variable names. Remove "
"the operator from the variable name."),
node.rightHandSide->location);
}
parentType = leftType;
node.rightHandSide->Visit(*this);
const Type rightType = childType;
childType = leftType;
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
ReportAnyError(node);
node.factor->Visit(*this);
const Type rightType = childType;
if (rightType == Type::Number) {
if (node.op != '+' && node.op != '-') {
// This is actually a dead code because the parser takes them as
// binary operations with an empty left side which makes as much sense.
RaiseTypeError(
_("You've used an \"unary\" operator that is not supported. Operator "
"should be "
"either + or -."),
node.location);
}
} else if (rightType == Type::String) {
RaiseTypeError(
_("You've used an operator that is not supported. Only + can be used "
"to concatenate texts, and must be placed between two texts (or "
"expressions)."),
node.location);
} else if (rightType == Type::Object) {
RaiseTypeError(
_("Operators (+, -) can't be used with an object name. Remove the "
"operator."),
node.location);
} else if (rightType == Type::Variable) {
RaiseTypeError(
_("Operators (+, -) can't be used in variable names. Remove "
"the operator from the variable name."),
node.location);
}
}
void OnVisitNumberNode(NumberNode& node) override {
ReportAnyError(node);
childType = Type::Number;
CheckType(parentType, childType, node.location);
}
void OnVisitTextNode(TextNode& node) override {
ReportAnyError(node);
childType = Type::String;
CheckType(parentType, childType, node.location);
}
void OnVisitNumberNode(NumberNode& node) override { ReportAnyError(node); }
void OnVisitTextNode(TextNode& node) override { ReportAnyError(node); }
void OnVisitVariableNode(VariableNode& node) override {
ReportAnyError(node);
if (node.child) node.child->Visit(*this);
if (node.child) {
node.child->Visit(*this);
}
childType = Type::Variable;
CheckType(parentType, childType, node.location);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
ReportAnyError(node);
if (node.child) node.child->Visit(*this);
if (node.child) {
node.child->Visit(*this);
}
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
ReportAnyError(node);
Type currentParentType = parentType;
parentType = Type::NumberOrString;
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
parentType = currentParentType;
if (node.child) {
node.child->Visit(*this);
}
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
ReportAnyError(node);
if (parentType == Type::String) {
RaiseTypeError(_("You must wrap your text inside double quotes "
"(example: \"Hello world\")."),
node.location);
}
else if (parentType == Type::Number) {
RaiseTypeError(
_("You must enter a number."), node.location);
}
else if (parentType == Type::NumberOrString) {
RaiseTypeError(
_("You must enter a number or a text, wrapped inside double quotes "
"(example: \"Hello world\")."),
node.location);
}
else if (parentType != Type::Object && parentType != Type::Variable) {
// It can't happen.
RaiseTypeError(
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
node.location);
}
childType = parentType;
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
ReportAnyError(node);
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
ReportAnyError(node);
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
}
childType = ValidateFunction(node);
}
void OnVisitEmptyNode(EmptyNode& node) override {
ReportAnyError(node);
gd::String message;
if (parentType == Type::Number) {
message = _("You must enter a number or a valid expression call.");
} else if (parentType == Type::String) {
message = _(
"You must enter a text (between quotes) or a valid expression call.");
} else if (parentType == Type::Variable) {
message = _("You must enter a variable name.");
} else if (parentType == Type::Object) {
message = _("You must enter a valid object name.");
} else {
// It can't happen.
message = _("You must enter a valid expression.");
}
RaiseTypeError(message, node.location);
childType = Type::Empty;
}
void OnVisitEmptyNode(EmptyNode& node) override { ReportAnyError(node); }
private:
void ReportAnyError(ExpressionNode& node) {
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, Object, Empty};
Type ValidateFunction(const gd::FunctionCallNode& function);
void ReportAnyError(const ExpressionNode& node) {
if (node.diagnostic && node.diagnostic->IsError()) {
// Syntax errors are holden by the AST nodes.
// It's fine to give pointers on them as the AST live longer than errors
// handling.
errors.push_back(node.diagnostic.get());
}
}
void RaiseError(const gd::String &type,
const gd::String &message, const ExpressionParserLocation &location) {
auto diagnostic = gd::make_unique<ExpressionParserError>(
type, message, location);
errors.push_back(diagnostic.get());
// Errors found by the validator are not holden by the AST nodes.
// They must be owned by the validator to keep living while errors are
// handled by the caller.
supplementalErrors.push_back(std::move(diagnostic));
}
void RaiseTypeError(
const gd::String &message, const ExpressionParserLocation &location) {
RaiseError("type_error", message, location);
}
void RaiseOperatorError(
const gd::String &message, const ExpressionParserLocation &location) {
RaiseError("invalid_operator", message, location);
}
void CheckType(Type expect, Type actual, const ExpressionParserLocation &location) {
if (actual == Type::String) {
if (expect == Type::Number) {
RaiseTypeError(_("You entered a text, but a number was expected."),
location);
}
else if (expect != Type::String && expect != Type::NumberOrString) {
RaiseTypeError(
_("You entered a text, but this type was expected:") + " " + TypeToString(expect),
location);
}
}
else if (actual == Type::Number) {
if (expect == Type::String) {
RaiseTypeError(
_("You entered a number, but a text was expected (in quotes)."),
location);
}
else if (expect != Type::Number && expect != Type::NumberOrString) {
RaiseTypeError(
_("You entered a number, but this type was expected:") + " " + TypeToString(expect),
location);
}
}
}
static Type StringToType(const gd::String &type);
static const gd::String &TypeToString(Type type);
static const gd::String unknownTypeString;
static const gd::String numberTypeString;
static const gd::String stringTypeString;
static const gd::String numberOrStringTypeString;
static const gd::String variableTypeString;
static const gd::String objectTypeString;
static const gd::String identifierTypeString;
static const gd::String emptyTypeString;
std::vector<ExpressionParserDiagnostic*> errors;
std::vector<std::unique_ptr<ExpressionParserDiagnostic>> supplementalErrors;
Type childType;
Type parentType;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
};
} // namespace gd

View File

@@ -0,0 +1,172 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONVARIABLEOWNERFINDER_H
#define GDCORE_EXPRESSIONVARIABLEOWNERFINDER_H
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/Tools/Localization.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief Find the object name that should be used as a context of the
* expression or sub-expression that a given node represents.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWorker {
public:
/**
* \brief Helper function to find the object name that should be used as a
* context of the expression or sub-expression that a given node represents.
*/
static const gd::String GetObjectName(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::String& rootObjectName,
gd::ExpressionNode& node) {
gd::ExpressionVariableOwnerFinder typeFinder(
platform, globalObjectsContainer, objectsContainer, rootObjectName);
node.Visit(typeFinder);
return typeFinder.GetObjectName();
}
virtual ~ExpressionVariableOwnerFinder(){};
protected:
ExpressionVariableOwnerFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String& rootObjectName_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
rootObjectName(rootObjectName_),
objectName(""),
variableNode(nullptr) {};
/**
* \brief Get all the errors
*
* No errors means that the expression is valid.
*/
const gd::String &GetObjectName() {
return objectName;
};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
void OnVisitOperatorNode(OperatorNode& node) override {}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (variableNode != nullptr) {
// This is not possible
return;
}
if (node.parent == nullptr) {
objectName = rootObjectName;
return;
}
variableNode = &node;
node.parent->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (variableNode != nullptr) {
// This is not possible
return;
}
if (node.parent == nullptr) {
objectName = rootObjectName;
return;
}
// This node is not necessarily a variable node.
// It will be checked when visiting the FunctionCallNode.
variableNode = &node;
node.parent->Visit(*this);
}
void OnVisitEmptyNode(EmptyNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {
if (variableNode == nullptr) {
return;
}
int parameterIndex = -1;
for (int i = 0; i < functionCall.parameters.size(); i++) {
if (functionCall.parameters.at(i).get() == variableNode) {
parameterIndex = i;
break;
}
}
if (parameterIndex < 0) {
return;
}
const gd::ParameterMetadata* parameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform,
globalObjectsContainer,
objectsContainer,
functionCall,
parameterIndex);
if (parameterMetadata == nullptr
|| parameterMetadata->GetType() != "objectvar") {
return;
}
// The object on which the function is called is returned if no previous
// parameters are objects.
objectName = functionCall.objectName;
for (int previousIndex = parameterIndex - 1; previousIndex >= 0; previousIndex--) {
const gd::ParameterMetadata* previousParameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform,
globalObjectsContainer,
objectsContainer,
functionCall,
previousIndex);
if (previousParameterMetadata != nullptr
&& gd::ParameterMetadata::IsObject(previousParameterMetadata->GetType())) {
auto previousParameterNode = functionCall.parameters[previousIndex].get();
IdentifierNode* objectNode = dynamic_cast<IdentifierNode*>(previousParameterNode);
objectName = objectNode->identifierName;
return;
}
}
}
private:
gd::String objectName;
gd::ExpressionNode *variableNode;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::String &rootObjectName;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONVARIABLEOWNERFINDER_H

View File

@@ -11,7 +11,6 @@
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
@@ -146,17 +145,9 @@ bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
pNb < instruction.GetParametersCount();
++pNb) {
const gd::String& type = metadata.parameters[pNb].type;
const gd::String& expression =
instruction.GetParameter(pNb).GetPlainString();
const gd::Expression& expression = instruction.GetParameter(pNb);
gd::ExpressionParser2 parser(
platform, GetGlobalObjectsContainer(), GetObjectsContainer());
auto node = gd::ParameterMetadata::IsExpression("number", type)
? parser.ParseExpression("number", expression)
: (gd::ParameterMetadata::IsExpression("string", type)
? parser.ParseExpression("string", expression)
: std::unique_ptr<gd::ExpressionNode>());
auto node = expression.GetRootNode();
if (node) {
ExpressionParameterMover mover(GetGlobalObjectsContainer(),
GetObjectsContainer(),

View File

@@ -11,7 +11,6 @@
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
@@ -156,18 +155,9 @@ bool ExpressionsRenamer::DoVisitInstruction(gd::Instruction& instruction,
for (std::size_t pNb = 0; pNb < metadata.parameters.size() &&
pNb < instruction.GetParametersCount();
++pNb) {
const gd::String& type = metadata.parameters[pNb].type;
const gd::String& expression =
instruction.GetParameter(pNb).GetPlainString();
const gd::Expression& expression = instruction.GetParameter(pNb);
gd::ExpressionParser2 parser(
platform, GetGlobalObjectsContainer(), GetObjectsContainer());
auto node = gd::ParameterMetadata::IsExpression("number", type)
? parser.ParseExpression("number", expression)
: (gd::ParameterMetadata::IsExpression("string", type)
? parser.ParseExpression("string", expression)
: std::unique_ptr<gd::ExpressionNode>());
auto node = expression.GetRootNode();
if (node) {
ExpressionFunctionRenamer renamer(GetGlobalObjectsContainer(),
GetObjectsContainer(),

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

View File

@@ -31,6 +31,7 @@ class GD_CORE_API UsedExtensionsFinder
private:
UsedExtensionsFinder(gd::Project& project_) : project(project_){};
gd::Project& project;
gd::String rootType;
std::set<gd::String> usedExtensions;
// Object Visitor

View File

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

View File

@@ -32,7 +32,7 @@ class GD_CORE_API EventsFunctionTools {
* generation for example.
*/
static void FreeEventsFunctionToObjectsContainer(
gd::Project& project,
const gd::Project& project,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer);
@@ -46,7 +46,7 @@ class GD_CORE_API EventsFunctionTools {
* generation for example.
*/
static void BehaviorEventsFunctionToObjectsContainer(
gd::Project& project,
const gd::Project& project,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,

View File

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

View File

@@ -54,11 +54,8 @@ Layout::Layout()
oglFOV(90.0f),
oglZNear(1.0f),
oglZFar(500.0f),
disableInputWhenNotFocused(true)
#if defined(GD_IDE_ONLY)
,
disableInputWhenNotFocused(true),
profiler(NULL)
#endif
{
gd::Layer layer;
layer.SetCameraCount(1);
@@ -332,7 +329,6 @@ void Layout::UnserializeFrom(gd::Project& project,
disableInputWhenNotFocused =
element.GetBoolAttribute("disableInputWhenNotFocused");
#if defined(GD_IDE_ONLY)
editorSettings.UnserializeFrom(
element.GetChild("uiSettings", 0, "UISettings"));
@@ -340,7 +336,6 @@ 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(
@@ -410,13 +405,11 @@ 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) {
@@ -430,7 +423,6 @@ 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,
@@ -444,7 +436,7 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
type = project.GetObject(name).GetType();
// Search in groups
if (searchInGroups) {
else if (searchInGroups) {
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
if (layout.GetObjectGroups()[i].GetName() == name) {
// A group has the name searched
@@ -505,18 +497,16 @@ gd::String GD_CORE_API GetTypeOfBehavior(const gd::ObjectsContainer& project,
gd::String name,
bool searchInGroups) {
for (std::size_t i = 0; i < layout.GetObjectsCount(); ++i) {
vector<gd::String> behaviors = layout.GetObject(i).GetAllBehaviorNames();
for (std::size_t j = 0; j < behaviors.size(); ++j) {
if (layout.GetObject(i).GetBehavior(behaviors[j]).GetName() == name)
return layout.GetObject(i).GetBehavior(behaviors[j]).GetTypeName();
const auto &object = layout.GetObject(i);
if (object.HasBehaviorNamed(name)) {
return object.GetBehavior(name).GetTypeName();
}
}
for (std::size_t i = 0; i < project.GetObjectsCount(); ++i) {
vector<gd::String> behaviors = project.GetObject(i).GetAllBehaviorNames();
for (std::size_t j = 0; j < behaviors.size(); ++j) {
if (project.GetObject(i).GetBehavior(behaviors[j]).GetName() == name)
return project.GetObject(i).GetBehavior(behaviors[j]).GetTypeName();
const auto &object = project.GetObject(i);
if (object.HasBehaviorNamed(name)) {
return object.GetBehavior(name).GetTypeName();
}
}
@@ -612,6 +602,5 @@ GetBehaviorsOfObject(const gd::ObjectsContainer& project,
return behaviors;
}
#endif
} // namespace gd

View File

@@ -12,9 +12,7 @@
#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 {
@@ -24,6 +22,7 @@ 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;
@@ -80,13 +79,12 @@ 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(gd::Project& project,
gd::BehaviorContent* Object::AddNewBehavior(const gd::Project& project,
const gd::String& type,
const gd::String& name) {
const gd::BehaviorMetadata& behaviorMetadata =
@@ -109,11 +107,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");
@@ -184,9 +182,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"));
@@ -209,6 +207,5 @@ void Object::SerializeTo(SerializerElement& element) const {
DoSerializeTo(element);
}
#endif
} // namespace gd

View File

@@ -83,6 +83,14 @@ 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_; }
@@ -100,7 +108,6 @@ 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
*/
@@ -186,7 +193,6 @@ class GD_CORE_API Object {
return false;
};
///@}
#endif
/** \name Behaviors management
* Members functions related to behaviors management.
@@ -232,7 +238,6 @@ 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.
@@ -242,10 +247,9 @@ class GD_CORE_API Object {
* \return A pointer to the newly added behavior content. NULL if the creation
* failed.
*/
gd::BehaviorContent* AddNewBehavior(gd::Project& project,
gd::BehaviorContent* AddNewBehavior(const gd::Project& project,
const gd::String& type,
const gd::String& name);
#endif
/**
* \brief Get a read-only access to the map containing the behaviors with
@@ -296,13 +300,11 @@ 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.
@@ -313,6 +315,7 @@ 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>>
@@ -331,12 +334,10 @@ 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(gd::Project& project,
gd::Object& ObjectsContainer::InsertNewObject(const 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(gd::Project& project,
gd::Object& InsertNewObject(const gd::Project& project,
const gd::String& objectType,
const gd::String& name,
std::size_t position);

View File

@@ -51,6 +51,7 @@ Project::Project()
: name(_("Project")),
version("1.0.0"),
packageName("com.example.gamename"),
templateSlug(""),
orientation("landscape"),
folderProject(false),
windowWidth(800),
@@ -516,6 +517,7 @@ 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
@@ -747,6 +749,7 @@ 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"));
@@ -974,6 +977,7 @@ 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,6 +162,18 @@ 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
@@ -983,6 +995,8 @@ 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

@@ -60,7 +60,7 @@ class GD_CORE_API Resource {
* \see gd::Resource::GetFile
* \see gd::Resource::SetFile
*/
virtual bool UseFile() { return false; }
virtual bool UseFile() const { return false; }
/**
* \brief Return, if applicable, the String containing the file used by the
@@ -184,7 +184,7 @@ class GD_CORE_API ImageResource : public Resource {
*/
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() override { return true; }
virtual bool UseFile() const override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
@@ -234,7 +234,7 @@ class GD_CORE_API AudioResource : public Resource {
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() override { return true; }
virtual bool UseFile() const override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
@@ -286,7 +286,7 @@ class GD_CORE_API FontResource : public Resource {
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() override { return true; }
virtual bool UseFile() const override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
@@ -312,7 +312,7 @@ class GD_CORE_API VideoResource : public Resource {
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() override { return true; }
virtual bool UseFile() const override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
@@ -338,7 +338,7 @@ class GD_CORE_API JsonResource : public Resource {
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() override { return true; }
virtual bool UseFile() const override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
@@ -379,7 +379,7 @@ class GD_CORE_API BitmapFontResource : public Resource {
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() override { return true; }
virtual bool UseFile() const override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;

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

@@ -197,6 +197,17 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("object", _("Object 2 parameter"))
.AddParameter("objectvar", _("Variable for object 2"))
.SetFunctionName("getStringWith2ObjectParamAnd2ObjectVarParam");
extension
->AddStrExpression(
"GetStringWith1ObjectParamAnd2ObjectVarParam",
"Get string with 2 objectvar param one from the same object param",
"",
"",
"")
.AddParameter("object", _("Object 1 parameter"))
.AddParameter("objectvar", _("Variable for object 1"))
.AddParameter("objectvar", _("Variable for object 2"))
.SetFunctionName("getStringWith1ObjectParamAnd2ObjectVarParam");
auto& object = extension->AddObject<gd::Object>(
"Sprite", "Dummy Sprite", "Dummy sprite object", "");

View File

@@ -32,7 +32,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
group.AddObject("MyOtherSpriteObject");
group.AddObject("MyFakeObjectWithUnsupportedCapability");
gd::ExpressionParser2 parser(platform, project, layout1);
gd::ExpressionParser2 parser;
unsigned int maxDepth = 0;
gd::EventsCodeGenerationContext context(&maxDepth);
@@ -40,8 +40,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("Valid text generation") {
{
auto node = parser.ParseExpression("string", "\"hello world\"");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
auto node = parser.ParseExpression("\"hello world\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -49,8 +51,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello world\"");
}
{
auto node = parser.ParseExpression("string", "\"hello\" + \"world\" ");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
auto node = parser.ParseExpression("\"hello\" + \"world\" ");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -59,8 +63,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
}
{
auto node = parser.ParseExpression(
"string", "\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\"");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
"\"{\\\"hello\\\": \\\"world \\\\\\\" \\\"}\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -72,8 +78,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("Valid number generation") {
{
auto node = parser.ParseExpression("number", "12.45");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
auto node = parser.ParseExpression("12.45");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -85,8 +93,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("Invalid operators generation") {
// TODO: Should any error return directly 0 or ""?
{
auto node = parser.ParseExpression("number", "12.45 +");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
auto node = parser.ParseExpression("12.45 +");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -94,8 +104,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() == "12.45 + 0");
}
{
auto node = parser.ParseExpression("number", "12.45 * *");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
auto node = parser.ParseExpression("12.45 * *");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -106,16 +118,20 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("Valid unary operator generation") {
{
auto node = parser.ParseExpression("number", "-12.45");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
auto node = parser.ParseExpression("-12.45");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "-(12.45)");
}
{
auto node = parser.ParseExpression("number", "12.5 + -2. / (.3)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
auto node = parser.ParseExpression("12.5 + -2. / (.3)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
@@ -124,20 +140,24 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
}
SECTION("Valid function calls") {
{
SECTION("without parameter") {
auto node =
parser.ParseExpression("number", " 1 / MyExtension::GetNumber()");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
parser.ParseExpression(" 1 / MyExtension::GetNumber()");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "1 / getNumber()");
}
{
SECTION("number and string parameters") {
auto node = parser.ParseExpression(
"number", "MyExtension::GetNumberWith2Params(12, \"hello world\")");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
"MyExtension::GetNumberWith2Params(12, \"hello world\")");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -145,12 +165,13 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getNumberWith2Params(12, \"hello world\")");
}
{
SECTION("nested function call") {
auto node =
parser.ParseExpression("number",
"MyExtension::GetNumberWith2Params("
parser.ParseExpression("MyExtension::GetNumberWith2Params("
"MyExtension::GetNumber(), \"hello world\")");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -158,10 +179,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getNumberWith2Params(getNumber(), \"hello world\")");
}
{
SECTION("object function") {
auto node =
parser.ParseExpression("number", "MySpriteObject.GetObjectNumber()");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
parser.ParseExpression("MySpriteObject.GetObjectNumber()");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -169,11 +192,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() ==
"MySpriteObject.getObjectNumber() ?? 0");
}
{
SECTION("object function with nested free function") {
auto node = parser.ParseExpression(
"string",
"MySpriteObject.GetObjectStringWith1Param(MyExtension::GetNumber())");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -183,22 +207,11 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
}
}
SECTION("Valid function calls with optional arguments") {
{
auto node =
parser.ParseExpression("number", "MyExtension::MouseX(\"layer1\",)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getMouseX(\"\", \"layer1\", 0)");
// (first argument is the currentScene)
}
{
auto node = parser.ParseExpression("number",
"MyExtension::MouseX(\"layer1\",2+2)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
SECTION("with optional parameter set") {
auto node = parser.ParseExpression("MyExtension::MouseX(\"layer1\",2+2)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -207,13 +220,39 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"getMouseX(\"\", \"layer1\", 2 + 2)");
// (first argument is the currentScene)
}
}
SECTION(
"Valid function calls (deprecated way of specifying optional "
"arguments)") {
{
auto node = parser.ParseExpression("number", "MyExtension::MouseX(,)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
SECTION("with last optional parameter omit") {
auto node =
parser.ParseExpression("MyExtension::MouseX(\"layer1\")");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getMouseX(\"\", \"layer1\", 0)");
// (first argument is the currentScene)
}
SECTION("with last optional parameter omit (deprecated way)") {
auto node =
parser.ParseExpression("MyExtension::MouseX(\"layer1\",)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getMouseX(\"\", \"layer1\", 0)");
// (first argument is the currentScene)
}
SECTION("with explicit comma (deprecated way)") {
auto node = parser.ParseExpression("MyExtension::MouseX(,)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -223,15 +262,17 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
// (first argument is the currentScene)
}
}
SECTION("Invalid function calls") {
{
SECTION("unknown identifier in parameters") {
auto node =
parser.ParseExpression("string",
"MySpriteObject.GetObjectStringWith3Param("
parser.ParseExpression("MySpriteObject.GetObjectStringWith3Param("
"MySpriteObject.GetObjectNumber() / 2.3, "
"MySpriteObject.GetObjectStringWith1Param("
"MyExtension::GetNumber()), test)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -243,33 +284,38 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"/* Error during generation, unrecognized identifier type: "
"unknown with value test */ \"test\") ?? \"\"");
}
{
auto node = parser.ParseExpression(
"number",
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
context);
SECTION("missing parameter") {
{
auto node = parser.ParseExpression(
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getNumberWith2Params(getNumber(), /* Error during generation, "
"parameter not existing in the nodes */ \"\")");
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getNumberWith2Params(getNumber(), /* Error during generation, "
"parameter not existing in the nodes */ \"\")");
}
{
// Using GenerateExpressionCode, the default value of 0 should be returned
// as expression is invalid.
REQUIRE(
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"number",
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())") ==
"0");
}
}
{
// Using GenerateExpressionCode, the default value of 0 should be returned
// as expression is invalid.
REQUIRE(
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"number",
"MyExtension::GetNumberWith2Params(MyExtension::GetNumber())") ==
"0");
}
{
auto node = parser.ParseExpression("number", "MyExtension::Idontexist()");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
SECTION("unknown function") {
auto node = parser.ParseExpression("MyExtension::Idontexist()");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -278,11 +324,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"/* Error during generation, function not found: "
"MyExtension::Idontexist */ 0");
}
{
auto node = parser.ParseExpression("number",
"MyExtension::GetNumberWith2Params(1, "
SECTION("too much parameters") {
auto node = parser.ParseExpression("MyExtension::GetNumberWith2Params(1, "
"\"2\", MyExtension::GetNumber())");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -291,13 +338,14 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"getNumberWith2Params(1, \"2\")");
}
}
SECTION("Invalid function calls (capabilities)") {
{
SECTION("function calls (capabilities)") {
SECTION("supported capability") {
// Capability is supported, so the expression is valid.
auto node = parser.ParseExpression(
"string",
"MySpriteObject.GetSomethingRequiringEffectCapability(123)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -306,26 +354,29 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
expressionCodeGenerator.GetOutput() ==
"MySpriteObject.getSomethingRequiringEffectCapability(123) ?? \"\"");
}
{
SECTION("unsupported capability") {
// Capability is not supported, so the expression is not even valid.
auto node =
parser.ParseExpression("string",
"MyFakeObjectWithUnsupportedCapability."
parser.ParseExpression("MyFakeObjectWithUnsupportedCapability."
"GetSomethingRequiringEffectCapability(123)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"\"");
}
{
SECTION("group with partial support") {
// We use a group, capability is supported only by one object of the
// group. The expression itself is valid, but code generation should skip
// the objects with unsupported capability.
auto node = parser.ParseExpression(
"string", "AllObjects.GetSomethingRequiringEffectCapability(123)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
"AllObjects.GetSomethingRequiringEffectCapability(123)");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -336,51 +387,87 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"MySpriteObject.getSomethingRequiringEffectCapability(123) ?? \"\"");
}
}
SECTION("Function name") {
auto node =
parser.ParseExpression("MySpriteObject.GetObjectNumber");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "0");
}
SECTION("Invalid variables") {
{
// Test an empty expression
SECTION("empty variable") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "") == "fakeBadVariable");
}
{
// Test a unary operator
SECTION("only an unary operator") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "objectvar", "-") ==
"fakeBadVariable");
}
{
// Test an operator
SECTION("only a binary operator") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "globalvar", "/") ==
"fakeBadVariable");
}
}
SECTION("Invalid variables, using operators") {
{
// Test a unary operator
SECTION("unary operation") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "objectvar", "-(var1)") ==
"fakeBadVariable");
}
{
// Test an operator
SECTION("binary operation") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "globalvar", "var1+var2") ==
"fakeBadVariable");
}
{
// Test multiple operators
SECTION("multiple operation") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "globalvar", "var1/var2/var3/var4") ==
"fakeBadVariable");
}
}
SECTION("Valid variables") {
SECTION("simple variable") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable", "")
== "getLayoutVariable(myVariable)");
}
SECTION("child dot accessor") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable.myChild", "")
== "getLayoutVariable(myVariable).getChild(\"myChild\")");
}
SECTION("2 children") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable.child1.child2", "")
== "getLayoutVariable(myVariable).getChild(\"child1\").getChild(\"child2\")");
}
SECTION("bracket access") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[ \"hello\" + "
"\"world\" ]", "")
== "getLayoutVariable(myVariable).getChild(\"hello\" + \"world\")");
}
SECTION("object variable") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "objectvar", "myVariable", "MySpriteObject")
== "getVariableForObject(MySpriteObject, myVariable)");
}
}
SECTION("Valid function calls with variables") {
SECTION("Simple access") {
{
SECTION("Scene variable") {
auto node = parser.ParseExpression(
"number", "MyExtension::GetVariableAsNumber(myVariable)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
"MyExtension::GetVariableAsNumber(myVariable)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -388,11 +475,12 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() ==
"returnVariable(getLayoutVariable(myVariable))");
}
{
SECTION("Global variable") {
auto node = parser.ParseExpression(
"number",
"MyExtension::GetGlobalVariableAsNumber(myGlobalVariable)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -400,13 +488,76 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(expressionCodeGenerator.GetOutput() ==
"returnVariable(getProjectVariable(myGlobalVariable))");
}
SECTION("Variables on different objects") {
auto node = parser.ParseExpression(
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam("
"MySpriteObject, myVariable, MyOtherSpriteObject, myOtherVariable)");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getStringWith2ObjectParamAnd2ObjectVarParam(fakeObjectListOf_MySpriteObject, "
"getVariableForObject(MySpriteObject, myVariable), "
"fakeObjectListOf_MyOtherSpriteObject, "
"getVariableForObject(MyOtherSpriteObject, myOtherVariable))");
}
SECTION("Variables on the same object") {
auto node = parser.ParseExpression(
"MyExtension::GetStringWith1ObjectParamAnd2ObjectVarParam("
"MySpriteObject, myVariable, myOtherVariable)");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getStringWith1ObjectParamAnd2ObjectVarParam(fakeObjectListOf_MySpriteObject, "
"getVariableForObject(MySpriteObject, myVariable), "
"getVariableForObject(MySpriteObject, myOtherVariable))");
}
SECTION("Object variable with object function call") {
auto node = parser.ParseExpression(
"MySpriteObject.GetObjectVariableAsNumber(myVariable)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(
expressionCodeGenerator.GetOutput() ==
"MySpriteObject.returnVariable(getVariableForObject("
"MySpriteObject, myVariable)) ?? 0");
}
}
SECTION("Child access") {
{
SECTION("1 child") {
auto node = parser.ParseExpression(
"MyExtension::GetVariableAsNumber(myVariable.child1)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"returnVariable(getLayoutVariable(myVariable).getChild("
"\"child1\"))");
}
SECTION("2 children") {
auto node = parser.ParseExpression(
"number",
"MyExtension::GetVariableAsNumber(myVariable.child1.child2)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -415,12 +566,13 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"returnVariable(getLayoutVariable(myVariable).getChild("
"\"child1\").getChild(\"child2\"))");
}
{
SECTION("bracket access") {
auto node = parser.ParseExpression(
"number",
"MyExtension::GetVariableAsNumber(myVariable[ \"hello\" + "
"\"world\" ].child2)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -429,13 +581,14 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"returnVariable(getLayoutVariable(myVariable).getChild("
"\"hello\" + \"world\").getChild(\"child2\"))");
}
{
SECTION("bracket access with nested variable") {
auto node = parser.ParseExpression(
"number",
"MyExtension::GetVariableAsNumber(myVariable[ \"hello\" + "
"MySpriteObject.GetObjectStringWith1Param(MyOtherSpriteObject."
"GetObjectVariableAsNumber(mySecondVariable)) ].child2)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
@@ -462,8 +615,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
}
SECTION("Mixed test (1)") {
{
auto node = parser.ParseExpression("number", "-+-MyExtension::MouseX(,)");
gd::ExpressionCodeGenerator expressionCodeGenerator(codeGenerator,
auto node = parser.ParseExpression("-+-MyExtension::MouseX(,)");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);

View File

@@ -19,16 +19,17 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto& layout1 = project.InsertNewLayout("Layout1", 0);
layout1.InsertNewObject(project, "MyExtension::Sprite", "MyObject", 0);
gd::ExpressionParser2 parser(platform, project, layout1);
gd::ExpressionParser2 parser;
auto getCompletionsFor = [&](const gd::String& type,
const gd::String& expression,
size_t location) {
auto node = parser.ParseExpression(type, expression);
auto node = parser.ParseExpression(expression);
REQUIRE(node != nullptr);
return gd::ExpressionCompletionFinder::GetCompletionDescriptionsFor(
*node, location);
platform, project, layout1, type, *node, location);
};
const std::vector<gd::ExpressionCompletionDescription>
@@ -66,6 +67,24 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
REQUIRE(getCompletionsFor("number|string", "My", 2) ==
expectedEmptyCompletions);
}
SECTION("Object or expression completions in a variable name") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("string", "My", 0, 2),
gd::ExpressionCompletionDescription::ForExpression(
"string", "My", 0, 2)};
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 52) == expectedCompletions);
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 53) == expectedCompletions);
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 54) == expectedEmptyCompletions);
}
SECTION("Object or expression completions in a variable index") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("number", "My", 0, 2),
gd::ExpressionCompletionDescription::ForExpression(
"number", "My", 0, 2)};
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 52) == expectedCompletions);
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 53) == expectedCompletions);
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 54) == expectedEmptyCompletions);
}
SECTION("Object when type is an object") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("object", "My", 0, 2)};
@@ -143,6 +162,17 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
"MyExtension::GetVariableAsNumber(myVar",
33) == expectedCompletions);
}
SECTION("Object function with a Variable as argument") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForVariable(
"objectvar", "myVar", 35, 40, "MyObject")};
getCompletionsFor("number",
"MyObject.GetObjectVariableAsNumber(myVar",
35);
REQUIRE(getCompletionsFor("number",
"MyObject.GetObjectVariableAsNumber(myVar",
35) == expectedCompletions);
}
SECTION("Function with a Layer as argument") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForText(

View File

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

File diff suppressed because it is too large Load Diff

View File

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

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(platform, project, layout1);
gd::ExpressionParser2 parser;
SECTION("Check that no naughty string crash the parser") {
std::string inputFile = std::string(__FILE__) + "-blns.txt";
@@ -31,9 +31,9 @@ TEST_CASE("ExpressionParser2 - Naughty strings", "[common][events]") {
std::string line;
size_t count = 0;
while ( std::getline (myfile,line) ) {
auto node1 = parser.ParseExpression("string", line.c_str());
auto node1 = parser.ParseExpression(line.c_str());
REQUIRE(node1 != nullptr);
auto node2 = parser.ParseExpression("number", line.c_str());
auto node2 = parser.ParseExpression(line.c_str());
REQUIRE(node2 != nullptr);
count++;

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

File diff suppressed because one or more lines are too long

View File

@@ -47,6 +47,7 @@ namespace gdjs {
const isTextArea = this._object.getInputType() === 'text area';
this._input = document.createElement(isTextArea ? 'textarea' : 'input');
this._input.style.border = '1px solid black';
this._input.autocomplete = 'off';
this._input.style.borderRadius = '0px';
this._input.style.backgroundColor = 'white';
this._input.style.position = 'absolute';

View File

@@ -33,7 +33,6 @@ using namespace std;
namespace gdjs {
gd::String EventsCodeGenerator::GenerateEventsListCompleteFunctionCode(
gd::Project& project,
gdjs::EventsCodeGenerator& codeGenerator,
gd::String fullyQualifiedFunctionName,
gd::String functionArgumentsCode,
@@ -89,7 +88,7 @@ gd::String EventsCodeGenerator::GenerateEventsListCompleteFunctionCode(
}
gd::String EventsCodeGenerator::GenerateLayoutCode(
gd::Project& project,
const gd::Project& project,
const gd::Layout& scene,
const gd::String& codeNamespace,
std::set<gd::String>& includeFiles,
@@ -99,7 +98,6 @@ gd::String EventsCodeGenerator::GenerateLayoutCode(
codeGenerator.SetGenerateCodeForRuntime(compilationForRuntime);
gd::String output = GenerateEventsListCompleteFunctionCode(
project,
codeGenerator,
codeGenerator.GetCodeNamespaceAccessor() + "func",
"runtimeScene",
@@ -128,7 +126,6 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
codeGenerator.SetGenerateCodeForRuntime(compilationForRuntime);
gd::String output = GenerateEventsListCompleteFunctionCode(
project,
codeGenerator,
codeGenerator.GetCodeNamespaceAccessor() + "func",
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
@@ -191,7 +188,6 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
"Behavior");
gd::String output = GenerateEventsListCompleteFunctionCode(
project,
codeGenerator,
fullyQualifiedFunctionName,
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
@@ -981,7 +977,7 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
}
gd::String EventsCodeGenerator::GenerateParameterCodes(
const gd::String& parameter,
const gd::Expression& parameter,
const gd::ParameterMetadata& metadata,
gd::EventsCodeGenerationContext& context,
const gd::String& lastObjectName,
@@ -1224,7 +1220,7 @@ gd::String EventsCodeGenerator::GenerateProfilerSectionEnd(
ConvertToStringExplicit(section) + "); }";
}
EventsCodeGenerator::EventsCodeGenerator(gd::Project& project,
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project,
const gd::Layout& layout)
: gd::EventsCodeGenerator(project, layout, JsPlatform::Get()) {}

View File

@@ -44,7 +44,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
*
* \return JavaScript code
*/
static gd::String GenerateLayoutCode(gd::Project& project,
static gd::String GenerateLayoutCode(const gd::Project& project,
const gd::Layout& scene,
const gd::String& codeNamespace,
std::set<gd::String>& includeFiles,
@@ -174,7 +174,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
protected:
virtual gd::String GenerateParameterCodes(
const gd::String& parameter,
const gd::Expression& parameter,
const gd::ParameterMetadata& metadata,
gd::EventsCodeGenerationContext& context,
const gd::String& lastObjectName,
@@ -289,7 +289,6 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
private:
static gd::String GenerateEventsListCompleteFunctionCode(
gd::Project& project,
gdjs::EventsCodeGenerator& codeGenerator,
gd::String fullyQualifiedFunctionName,
gd::String functionArgumentsCode,
@@ -352,7 +351,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
/**
* \brief Construct a code generator for the specified project and layout.
*/
EventsCodeGenerator(gd::Project& project, const gd::Layout& layout);
EventsCodeGenerator(const gd::Project& project, const gd::Layout& layout);
/**
* \brief Construct a code generator for the specified objects and groups.

View File

@@ -22,7 +22,7 @@ namespace gdjs {
*/
class LayoutCodeGenerator {
public:
LayoutCodeGenerator(gd::Project& project_)
LayoutCodeGenerator(const gd::Project& project_)
: project(project_){};
/**
@@ -34,7 +34,7 @@ class LayoutCodeGenerator {
bool compilationForRuntime);
private:
gd::Project& project;
const gd::Project& project;
};
} // namespace gdjs

View File

@@ -367,14 +367,16 @@ BaseObjectExtension::BaseObjectExtension() {
codeGenerator,
context,
"number",
instruction.GetParameter(2).GetPlainString());
instruction.GetParameter(2).GetPlainString(),
instruction.GetParameter(0).GetPlainString());
gd::String expression2Code =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"number",
instruction.GetParameter(4).GetPlainString());
instruction.GetParameter(4).GetPlainString(),
instruction.GetParameter(0).GetPlainString());
gd::String op1 = instruction.GetParameter(1).GetPlainString();
gd::String newX =
@@ -422,14 +424,16 @@ BaseObjectExtension::BaseObjectExtension() {
codeGenerator,
context,
"number",
instruction.GetParameter(2).GetPlainString());
instruction.GetParameter(2).GetPlainString(),
instruction.GetParameter(0).GetPlainString());
gd::String expression2Code =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"number",
instruction.GetParameter(4).GetPlainString());
instruction.GetParameter(4).GetPlainString(),
instruction.GetParameter(0).GetPlainString());
gd::String op1 = instruction.GetParameter(1).GetPlainString();
gd::String newX = isNotAssignmentOperator(op1)

View File

@@ -20,20 +20,39 @@ CameraExtension::CameraExtension() {
GetAllActions()["CameraY"] // Deprecated
.SetFunctionName("gdjs.evtTools.camera.setCameraY")
.SetGetter("gdjs.evtTools.camera.getCameraY");
GetAllActions()["SetCameraX"]
GetAllActions()["SetCameraX"] // Deprecated
.SetFunctionName("gdjs.evtTools.camera.setCameraX")
.SetGetter("gdjs.evtTools.camera.getCameraX");
GetAllActions()["SetCameraY"]
GetAllActions()["SetCameraY"] // Deprecated
.SetFunctionName("gdjs.evtTools.camera.setCameraY")
.SetGetter("gdjs.evtTools.camera.getCameraY");
GetAllActions()["SetCameraCenterX"]
.SetFunctionName("gdjs.evtTools.camera.setCameraX")
.SetGetter("gdjs.evtTools.camera.getCameraX");
GetAllActions()["SetCameraCenterY"]
.SetFunctionName("gdjs.evtTools.camera.setCameraY")
.SetGetter("gdjs.evtTools.camera.getCameraY");
GetAllConditions()["CameraX"].SetFunctionName(
"gdjs.evtTools.camera.getCameraX");
"gdjs.evtTools.camera.getCameraX"); // Deprecated
GetAllConditions()["CameraY"].SetFunctionName(
"gdjs.evtTools.camera.getCameraY"); // Deprecated
GetAllConditions()["CameraCenterX"].SetFunctionName(
"gdjs.evtTools.camera.getCameraX");
GetAllConditions()["CameraCenterY"].SetFunctionName(
"gdjs.evtTools.camera.getCameraY");
GetAllConditions()["CameraWidth"].SetFunctionName(
"gdjs.evtTools.camera.getCameraWidth");
GetAllConditions()["CameraHeight"].SetFunctionName(
"gdjs.evtTools.camera.getCameraHeight");
GetAllConditions()["CameraBorderLeft"].SetFunctionName(
"gdjs.evtTools.camera.getCameraBorderLeft");
GetAllConditions()["CameraBorderRight"].SetFunctionName(
"gdjs.evtTools.camera.getCameraBorderRight");
GetAllConditions()["CameraBorderTop"].SetFunctionName(
"gdjs.evtTools.camera.getCameraBorderTop");
GetAllConditions()["CameraBorderBottom"].SetFunctionName(
"gdjs.evtTools.camera.getCameraBorderBottom");
GetAllActions()["ShowLayer"].SetFunctionName(
"gdjs.evtTools.camera.showLayer");
GetAllActions()["HideLayer"].SetFunctionName(
@@ -52,13 +71,25 @@ CameraExtension::CameraExtension() {
"gdjs.evtTools.camera.setCameraZoom");
GetAllExpressions()["CameraX"].SetFunctionName(
"gdjs.evtTools.camera.getCameraX");
"gdjs.evtTools.camera.getCameraX"); // Deprecated
GetAllExpressions()["VueX"].SetFunctionName(
"gdjs.evtTools.camera.getCameraX");
"gdjs.evtTools.camera.getCameraX"); // Deprecated
GetAllExpressions()["CameraY"].SetFunctionName(
"gdjs.evtTools.camera.getCameraY");
"gdjs.evtTools.camera.getCameraY"); // Deprecated
GetAllExpressions()["VueY"].SetFunctionName(
"gdjs.evtTools.camera.getCameraY"); // Deprecated
GetAllExpressions()["CameraCenterY"].SetFunctionName(
"gdjs.evtTools.camera.getCameraY");
GetAllExpressions()["CameraCenterX"].SetFunctionName(
"gdjs.evtTools.camera.getCameraX");
GetAllExpressions()["CameraBorderLeft"].SetFunctionName(
"gdjs.evtTools.camera.getCameraBorderLeft");
GetAllExpressions()["CameraBorderRight"].SetFunctionName(
"gdjs.evtTools.camera.getCameraBorderRight");
GetAllExpressions()["CameraBorderTop"].SetFunctionName(
"gdjs.evtTools.camera.getCameraBorderTop");
GetAllExpressions()["CameraBorderBottom"].SetFunctionName(
"gdjs.evtTools.camera.getCameraBorderBottom");
GetAllExpressions()["CameraAngle"].SetFunctionName(
"gdjs.evtTools.camera.getCameraRotation");
GetAllExpressions()["CameraRotation"].SetFunctionName(

View File

@@ -77,7 +77,10 @@ bool ExporterHelper::ExportProjectForPixiPreview(
fs.ClearDir(options.exportPath);
std::vector<gd::String> includesFiles;
// TODO Try to remove side effects to avoid the copy
// that destroys the AST in cache.
gd::Project exportedProject = options.project;
const gd::Project &immutableProject = exportedProject;
if (!options.fullLoadingScreen) {
// Most of the time, we skip the logo and minimum duration so that
@@ -105,26 +108,26 @@ bool ExporterHelper::ExportProjectForPixiPreview(
!options.websocketDebuggerServerAddress.empty(),
/*includeWindowMessageDebuggerClient=*/
options.useWindowMessageDebuggerClient,
exportedProject.GetLoadingScreen().GetGDevelopLogoStyle(),
immutableProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
// Export files for object and behaviors
ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
ExportObjectAndBehaviorsIncludes(immutableProject, includesFiles);
// Export effects (after engine libraries as they auto-register themselves to
// the engine)
ExportEffectIncludes(exportedProject, includesFiles);
ExportEffectIncludes(immutableProject, includesFiles);
previousTime = LogTimeSpent("Include files export", previousTime);
if (!options.projectDataOnlyExport) {
// Generate events code
if (!ExportEventsCode(exportedProject, codeOutputDir, includesFiles, true))
if (!ExportEventsCode(immutableProject, codeOutputDir, includesFiles, true))
return false;
// Export source files
if (!ExportExternalSourceFiles(
exportedProject, codeOutputDir, includesFiles)) {
immutableProject, codeOutputDir, includesFiles)) {
gd::LogError(
_("Error during exporting! Unable to export source files:\n") +
lastError);
@@ -623,7 +626,7 @@ void ExporterHelper::RemoveIncludes(bool pixiRenderers,
}
bool ExporterHelper::ExportEffectIncludes(
gd::Project &project, std::vector<gd::String> &includesFiles) {
const gd::Project &project, std::vector<gd::String> &includesFiles) {
std::set<gd::String> effectIncludes;
gd::EffectsCodeGenerator::GenerateEffectsIncludeFiles(
@@ -634,7 +637,7 @@ bool ExporterHelper::ExportEffectIncludes(
return true;
}
bool ExporterHelper::ExportEventsCode(gd::Project &project,
bool ExporterHelper::ExportEventsCode(const gd::Project &project,
gd::String outputDir,
std::vector<gd::String> &includesFiles,
bool exportForPreview) {
@@ -642,7 +645,7 @@ bool ExporterHelper::ExportEventsCode(gd::Project &project,
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
std::set<gd::String> eventsIncludes;
gd::Layout &layout = project.GetLayout(i);
const gd::Layout &layout = project.GetLayout(i);
LayoutCodeGenerator layoutCodeGenerator(project);
gd::String eventsOutput = layoutCodeGenerator.GenerateLayoutCompleteCode(
layout, eventsIncludes, !exportForPreview);
@@ -664,7 +667,7 @@ bool ExporterHelper::ExportEventsCode(gd::Project &project,
}
bool ExporterHelper::ExportExternalSourceFiles(
gd::Project &project,
const gd::Project &project,
gd::String outputDir,
std::vector<gd::String> &includesFiles) {
const auto &allFiles = project.GetAllSourceFiles();
@@ -765,7 +768,7 @@ bool ExporterHelper::ExportIncludesAndLibs(
}
void ExporterHelper::ExportObjectAndBehaviorsIncludes(
gd::Project &project, std::vector<gd::String> &includesFiles) {
const gd::Project &project, std::vector<gd::String> &includesFiles) {
auto addIncludeFiles = [&](const std::vector<gd::String> &newIncludeFiles) {
for (const auto &includeFile : newIncludeFiles) {
InsertUnique(includesFiles, includeFile);
@@ -798,7 +801,7 @@ void ExporterHelper::ExportObjectAndBehaviorsIncludes(
addObjectsIncludeFiles(project);
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
gd::Layout &layout = project.GetLayout(i);
const gd::Layout &layout = project.GetLayout(i);
addObjectsIncludeFiles(layout);
}
}

View File

@@ -224,7 +224,7 @@ class ExporterHelper {
* includesFiles A reference to a vector that will be filled with JS files to
* be exported along with the project. ( including "codeX.js" files ).
*/
bool ExportEventsCode(gd::Project &project,
bool ExportEventsCode(const gd::Project &project,
gd::String outputDir,
std::vector<gd::String> &includesFiles,
bool exportForPreview);
@@ -232,14 +232,14 @@ class ExporterHelper {
/**
* \brief Add the project effects include files.
*/
bool ExportEffectIncludes(gd::Project &project,
bool ExportEffectIncludes(const gd::Project &project,
std::vector<gd::String> &includesFiles);
/**
* \brief Add the include files for all the objects of the project
* and their behaviors.
*/
void ExportObjectAndBehaviorsIncludes(gd::Project &project,
void ExportObjectAndBehaviorsIncludes(const gd::Project &project,
std::vector<gd::String> &includesFiles);
/**
@@ -253,7 +253,7 @@ class ExporterHelper {
* with JS files to be exported along with the project. (including
* "ext-codeX.js" files).
*/
bool ExportExternalSourceFiles(gd::Project &project,
bool ExportExternalSourceFiles(const gd::Project &project,
gd::String outputDir,
std::vector<gd::String> &includesFiles);

View File

@@ -74,6 +74,62 @@ namespace gdjs {
return runtimeScene.getLayer(layer).getCameraHeight();
};
export const getCameraBorderLeft = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
cameraId: integer
): number {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return (
getCameraX(runtimeScene, layer, cameraId) -
getCameraWidth(runtimeScene, layer, cameraId) / 2
);
};
export const getCameraBorderRight = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
cameraId: integer
): number {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return (
getCameraX(runtimeScene, layer, cameraId) +
getCameraWidth(runtimeScene, layer, cameraId) / 2
);
};
export const getCameraBorderTop = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
cameraId: integer
): number {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return (
getCameraY(runtimeScene, layer, cameraId) -
getCameraHeight(runtimeScene, layer, cameraId) / 2
);
};
export const getCameraBorderBottom = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
cameraId: integer
): number {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return (
getCameraY(runtimeScene, layer, cameraId) +
getCameraHeight(runtimeScene, layer, cameraId) / 2
);
};
export const showLayer = function (
runtimeScene: gdjs.RuntimeScene,
layer: string

View File

@@ -761,7 +761,8 @@ namespace gdjs {
}
}
const totalCount = Object.keys(files).length;
const filesToLoad = Object.keys(files);
const totalCount = filesToLoad.length;
if (totalCount === 0) return onComplete(totalCount); // Nothing to load.
let loadedCount: integer = 0;
@@ -775,6 +776,7 @@ namespace gdjs {
if (loadedCount === totalCount) return onComplete(totalCount);
onProgress(loadedCount, totalCount);
loadNextFile();
};
const preloadAudioFile = (
@@ -796,29 +798,30 @@ namespace gdjs {
);
};
for (let file in files) {
if (files.hasOwnProperty(file)) {
const fileData = files[file][0];
if (!fileData.preloadAsSound && !fileData.preloadAsMusic) {
onLoad();
} else if (fileData.preloadAsSound && fileData.preloadAsMusic) {
let loadedOnce = false;
const callback = (_, error) => {
if (!loadedOnce) {
loadedOnce = true;
return;
}
onLoad(_, error);
};
const loadNextFile = () => {
if (!filesToLoad.length) return;
const file = filesToLoad.shift()!;
const fileData = files[file][0];
if (!fileData.preloadAsSound && !fileData.preloadAsMusic) {
onLoad();
} else if (fileData.preloadAsSound && fileData.preloadAsMusic) {
let loadedOnce = false;
const callback = (_, error) => {
if (!loadedOnce) {
loadedOnce = true;
return;
}
onLoad(_, error);
};
preloadAudioFile(file, callback, /* isMusic= */ true);
preloadAudioFile(file, callback, /* isMusic= */ false);
} else if (fileData.preloadAsSound) {
preloadAudioFile(file, onLoad, /* isMusic= */ false);
} else if (fileData.preloadAsMusic)
preloadAudioFile(file, onLoad, /* isMusic= */ true);
}
}
preloadAudioFile(file, callback, /* isMusic= */ true);
preloadAudioFile(file, callback, /* isMusic= */ false);
} else if (fileData.preloadAsSound) {
preloadAudioFile(file, onLoad, /* isMusic= */ false);
} else if (fileData.preloadAsMusic)
preloadAudioFile(file, onLoad, /* isMusic= */ true);
};
loadNextFile();
}
}

File diff suppressed because one or more lines are too long

View File

@@ -589,6 +589,46 @@ namespace gdjs {
};
canvas.onmouseenter = function (e) {
manager.onMouseEnter();
// There is no mouse event when the cursor is outside of the canvas.
// We catchup what happened.
{
const leftIsPressed = (e.buttons & 1) !== 0;
const leftWasPressed = manager.isMouseButtonPressed(
gdjs.InputManager.MOUSE_LEFT_BUTTON
);
if (leftIsPressed && !leftWasPressed) {
manager.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
}
if (!leftIsPressed && leftWasPressed) {
manager.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
}
}
{
const rightIsPressed = (e.buttons & 2) !== 0;
const rightWasPressed = manager.isMouseButtonPressed(
gdjs.InputManager.MOUSE_RIGHT_BUTTON
);
if (rightIsPressed && !rightWasPressed) {
manager.onMouseButtonPressed(gdjs.InputManager.MOUSE_RIGHT_BUTTON);
}
if (!rightIsPressed && rightWasPressed) {
manager.onMouseButtonReleased(gdjs.InputManager.MOUSE_RIGHT_BUTTON);
}
}
{
const middleIsPressed = (e.buttons & 4) !== 0;
const middleWasPressed = manager.isMouseButtonPressed(
gdjs.InputManager.MOUSE_MIDDLE_BUTTON
);
if (middleIsPressed && !middleWasPressed) {
manager.onMouseButtonPressed(gdjs.InputManager.MOUSE_MIDDLE_BUTTON);
}
if (!middleIsPressed && middleWasPressed) {
manager.onMouseButtonReleased(
gdjs.InputManager.MOUSE_MIDDLE_BUTTON
);
}
}
};
window.addEventListener(
'click',

114
GDJS/package-lock.json generated
View File

@@ -827,10 +827,11 @@
"dev": true
},
"node_modules/acorn": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
"integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"dev": true,
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3193,6 +3194,21 @@
"node": ">=0.4.x"
}
},
"node_modules/react": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
"integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
"dev": true,
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-ace": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/react-ace/-/react-ace-6.6.0.tgz",
@@ -3233,6 +3249,22 @@
"node": ">=8.10.0"
}
},
"node_modules/react-dom": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
"integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
"dev": true,
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.19.1"
},
"peerDependencies": {
"react": "^16.14.0"
}
},
"node_modules/react-frame-component": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/react-frame-component/-/react-frame-component-4.1.3.tgz",
@@ -3422,6 +3454,17 @@
"truncate-utf8-bytes": "^1.0.0"
}
},
"node_modules/scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"dev": true,
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -3932,6 +3975,18 @@
"acorn-globals": "^3.0.0"
}
},
"node_modules/with/node_modules/acorn": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
"integrity": "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/wordwrap": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
@@ -4598,10 +4653,11 @@
"dev": true
},
"acorn": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
"integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
"dev": true
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"dev": true,
"peer": true
},
"acorn-globals": {
"version": "3.1.0",
@@ -6499,6 +6555,18 @@
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
"dev": true
},
"react": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
"integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
"dev": true,
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
}
},
"react-ace": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/react-ace/-/react-ace-6.6.0.tgz",
@@ -6529,6 +6597,19 @@
"strip-indent": "^3.0.0"
}
},
"react-dom": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
"integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
"dev": true,
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.19.1"
}
},
"react-frame-component": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/react-frame-component/-/react-frame-component-4.1.3.tgz",
@@ -6688,6 +6769,17 @@
"truncate-utf8-bytes": "^1.0.0"
}
},
"scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"dev": true,
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -7080,6 +7172,14 @@
"requires": {
"acorn": "^3.1.0",
"acorn-globals": "^3.0.0"
},
"dependencies": {
"acorn": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
"integrity": "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw==",
"dev": true
}
}
},
"wordwrap": {

View File

@@ -385,6 +385,8 @@ interface Project {
void SetPlayableWithMobile(boolean playable);
void SetPackageName([Const] DOMString packageName);
[Const, Ref] DOMString GetPackageName();
void SetTemplateSlug([Const] DOMString templateSlug);
[Const, Ref] DOMString GetTemplateSlug();
void SetOrientation([Const] DOMString orientation);
[Const, Ref] DOMString GetOrientation();
void SetProjectUuid([Const] DOMString projectUuid);
@@ -552,6 +554,8 @@ interface gdObject {
void SetName([Const] DOMString name);
[Const, Ref] DOMString GetName();
void SetAssetStoreId([Const] DOMString assetStoreId);
[Const, Ref] DOMString GetAssetStoreId();
void SetType([Const] DOMString type);
[Const, Ref] DOMString GetType();
void SetTags([Const] DOMString tags);
@@ -1074,12 +1078,17 @@ interface Instruction {
void SetInverted(boolean inverted);
boolean IsInverted();
void SetParameter(unsigned long id, [Const] DOMString value);
[Const, Ref] DOMString GetParameter(unsigned long id);
[Const, Ref] Expression GetParameter(unsigned long id);
void SetParametersCount(unsigned long count);
unsigned long GetParametersCount();
[Ref] InstructionsList GetSubInstructions();
};
interface Expression {
[Const, Ref] DOMString GetPlainString();
ExpressionNode GetRootNode();
};
interface VectorPairStringTextFormatting {
unsigned long size();
[Const, Ref] DOMString WRAPPED_GetString(unsigned long id);
@@ -1267,6 +1276,7 @@ interface ParameterMetadata {
[Ref] ParameterMetadata SetDefaultValue([Const] DOMString defaultValue_);
boolean STATIC_IsObject([Const] DOMString param);
boolean STATIC_IsBehavior([Const] DOMString param);
boolean STATIC_IsExpression([Const] DOMString type_, [Const] DOMString parameterType);
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Const, Ref] SerializerElement element);
@@ -1935,7 +1945,7 @@ interface VectorEventsSearchResult {
interface EventsRefactorer {
void STATIC_RenameObjectInEvents([Const, Ref] Platform platform, [Ref] ObjectsContainer project, [Ref] ObjectsContainer layout, [Ref] EventsList events, [Const] DOMString oldName, [Const] DOMString newName);
void STATIC_RemoveObjectInEvents([Const, Ref] Platform platform, [Ref] ObjectsContainer project, [Ref] ObjectsContainer layout, [Ref] EventsList events, [Const] DOMString name);
void STATIC_ReplaceStringInEvents([Ref] ObjectsContainer project, [Ref] ObjectsContainer layout, [Ref] EventsList events, [Const] DOMString toReplace, [Const] DOMString newString, boolean matchCase, boolean inConditions, boolean inActions, boolean inEventStrings);
[Value] VectorEventsSearchResult STATIC_ReplaceStringInEvents([Ref] ObjectsContainer project, [Ref] ObjectsContainer layout, [Ref] EventsList events, [Const] DOMString toReplace, [Const] DOMString newString, boolean matchCase, boolean inConditions, boolean inActions, boolean inEventStrings);
[Value] VectorEventsSearchResult STATIC_SearchInEvents([Const, Ref] Platform platform, [Ref] EventsList events, [Const] DOMString search, boolean matchCase, boolean inConditions, boolean inActions, boolean inEventStrings, boolean inEventSentences);
};
@@ -2083,7 +2093,7 @@ interface ExpressionParser2NodeWorker {
};
interface ExpressionValidator {
void ExpressionValidator();
void ExpressionValidator([Const, Ref] Platform platform, [Const, Ref] ObjectsContainer globalObjectsContainer, [Const, Ref] ObjectsContainer objectsContainer, [Const] DOMString rootType);
[Const, Ref] VectorExpressionParserDiagnostic GetErrors();
@@ -2117,7 +2127,7 @@ interface VectorExpressionCompletionDescription {
};
interface ExpressionCompletionFinder {
[Value] VectorExpressionCompletionDescription STATIC_GetCompletionDescriptionsFor([Ref] ExpressionNode node, unsigned long location);
[Value] VectorExpressionCompletionDescription STATIC_GetCompletionDescriptionsFor([Const, Ref] Platform platform, [Const, Ref] ObjectsContainer globalObjectsContainer, [Const, Ref] ObjectsContainer objectsContainer, [Const] DOMString rootType, [Ref] ExpressionNode node, unsigned long location);
[Const, Ref] VectorExpressionCompletionDescription GetCompletionDescriptions();
@@ -2133,9 +2143,9 @@ interface UniquePtrExpressionNode {
};
interface ExpressionParser2 {
void ExpressionParser2([Const, Ref] Platform platform, [Const, Ref] ObjectsContainer globalObjectsContainer, [Const, Ref] ObjectsContainer objectsContainer);
void ExpressionParser2();
[Value] UniquePtrExpressionNode ParseExpression([Const] DOMString type, [Const] DOMString expression);
[Value] UniquePtrExpressionNode ParseExpression([Const] DOMString expression);
};
enum EventsFunction_FunctionType {
@@ -2507,6 +2517,7 @@ interface Direction {
boolean IsLooping();
void SetLoop(boolean enable);
float GetTimeBetweenFrames();
[Const, Ref] VectorString GetSpriteNames();
void SetTimeBetweenFrames(float time);
void SwapSprites(unsigned long first, unsigned long second);
void MoveSprite(unsigned long oldIndex, unsigned long newIndex);

View File

@@ -498,6 +498,7 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
#define STATIC_FromJSON(x) FromJSON(x)
#define STATIC_IsObject IsObject
#define STATIC_IsBehavior IsBehavior
#define STATIC_IsExpression IsExpression
#define STATIC_Get Get
#define STATIC_GetAllUseless GetAllUseless
#define STATIC_RemoveAllUseless RemoveAllUseless

View File

@@ -1954,9 +1954,9 @@ describe('libGD.js', function () {
let instr = new gd.Instruction();
instr.setParametersCount(3);
expect(instr.getParametersCount()).toBe(3);
expect(instr.getParameter(1)).toBe('');
expect(instr.getParameter(1).getPlainString()).toBe('');
instr.setParameter(2, 'MyValue');
expect(instr.getParameter(2)).toBe('MyValue');
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
instr.delete();
});
it('can be cloned', function () {
@@ -1966,14 +1966,14 @@ describe('libGD.js', function () {
let newInstr = instr.clone();
expect(newInstr.getParametersCount()).toBe(3);
expect(newInstr.getParameter(1)).toBe('');
expect(newInstr.getParameter(2)).toBe('MyValue');
expect(newInstr.getParameter(1).getPlainString()).toBe('');
expect(newInstr.getParameter(2).getPlainString()).toBe('MyValue');
newInstr.setParameter(2, 'MyChangedValue');
expect(instr.getParameter(2)).toBe('MyValue');
expect(newInstr.getParameter(2)).toBe('MyChangedValue');
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
expect(newInstr.getParameter(2).getPlainString()).toBe('MyChangedValue');
newInstr.delete();
expect(instr.getParameter(2)).toBe('MyValue');
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
instr.delete();
});
@@ -2065,9 +2065,9 @@ describe('libGD.js', function () {
expect(list2.get(1).getType()).toBe('Type2');
expect(list2.get(0).getParametersCount()).toBe(2);
expect(list2.get(1).getParametersCount()).toBe(1);
expect(list2.get(0).getParameter(0)).toBe('Param1');
expect(list2.get(0).getParameter(1)).toBe('Param2');
expect(list2.get(1).getParameter(0)).toBe('Param3');
expect(list2.get(0).getParameter(0).getPlainString()).toBe('Param1');
expect(list2.get(0).getParameter(1).getPlainString()).toBe('Param2');
expect(list2.get(1).getParameter(0).getPlainString()).toBe('Param3');
list2.delete();
project.delete();
@@ -3289,18 +3289,36 @@ describe('libGD.js', function () {
type,
expression,
expectedError,
expectedErrorPosition
expectedErrorPosition,
expectedError2,
expectedErrorPosition2
) {
const parser = new gd.ExpressionParser2(
const parser = new gd.ExpressionParser2();
const expressionNode = parser.parseExpression(expression).get();
const expressionValidator = new gd.ExpressionValidator(
gd.JsPlatform.get(),
project,
layout
);
const expressionNode = parser.parseExpression(type, expression).get();
const expressionValidator = new gd.ExpressionValidator();
layout,
type);
expressionNode.visit(expressionValidator);
if (expectedError) {
if (expectedError2) {
expect(expressionValidator.getErrors().size()).toBe(2);
expect(expressionValidator.getErrors().at(0).getMessage()).toBe(
expectedError
);
if (expectedErrorPosition)
expect(expressionValidator.getErrors().at(0).getStartPosition()).toBe(
expectedErrorPosition
);
expect(expressionValidator.getErrors().at(1).getMessage()).toBe(
expectedError2
);
if (expectedErrorPosition2)
expect(expressionValidator.getErrors().at(1).getStartPosition()).toBe(
expectedErrorPosition2
);
} else if (expectedError) {
expect(expressionValidator.getErrors().size()).toBe(1);
expect(expressionValidator.getErrors().at(0).getMessage()).toBe(
expectedError
@@ -3361,6 +3379,8 @@ describe('libGD.js', function () {
testExpression(
'number',
'3..14',
'More than one term was found. Verify that your expression is properly written.',
2,
'No operator found. Did you forget to enter an operator (like +, -, * or /) between numbers or expressions?',
2
);
@@ -3410,12 +3430,12 @@ describe('libGD.js', function () {
testExpression(
'number',
'abs(-5, 3)',
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name."
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be exactly 1"
);
testExpression(
'number',
'MouseX("", 0, 0) + 1',
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name."
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be: 0-2"
);
});
@@ -3434,7 +3454,7 @@ describe('libGD.js', function () {
testExpression(
'number',
'MySpriteObject.PointX("Point", 2)',
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name."
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be exactly 1"
);
});
@@ -3465,14 +3485,14 @@ describe('libGD.js', function () {
}
const expression = expressionWithCaret.replace('|', '');
const parser = new gd.ExpressionParser2(
gd.JsPlatform.get(),
project,
layout
);
const expressionNode = parser.parseExpression(type, expression).get();
const parser = new gd.ExpressionParser2();
const expressionNode = parser.parseExpression(expression).get();
const completionDescriptions =
gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
gd.JsPlatform.get(),
project,
layout,
type,
expressionNode,
// We're looking for completion for the character just before the caret.
Math.max(0, caretPosition - 1)

View File

@@ -21,8 +21,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -35,7 +33,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
parameters: ['SuccessVariable', '+', '1'],
},
],
events: [],
},
])
);
@@ -66,8 +63,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -88,7 +83,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
parameters: ['SuccessVariable', '+', '2'],
},
],
events: [],
},
])
);
@@ -125,8 +119,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -143,7 +135,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -191,8 +182,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -219,7 +208,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -276,8 +264,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -293,8 +279,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
events: [
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -314,7 +298,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
],
},
@@ -391,8 +374,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -403,8 +384,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
events: [
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -429,7 +408,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
],
},
@@ -506,8 +484,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
// Do a first filtering.
conditions: [
@@ -524,8 +500,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
events: [
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
// Filter with the more precise condition first.
conditions: [
@@ -537,8 +511,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
actions: [],
events: [
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
// Filter with a less precise condition then.
conditions: [
@@ -569,7 +541,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
],
},
@@ -670,8 +641,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
// Do a first filtering.
conditions: [
@@ -688,8 +657,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
events: [
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
// Filter with the more precise condition first.
conditions: [
@@ -701,8 +668,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
actions: [],
events: [
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
// Filter with a less precise condition then.
conditions: [
@@ -733,13 +698,10 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
// Add an event to prevent optimisation (reuse of the same objects list)
// in the previous event.
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -753,7 +715,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
},
],
actions: [],
events: [],
},
],
},
@@ -854,8 +815,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -866,8 +825,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
events: [
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -878,8 +835,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
actions: [],
events: [
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -909,13 +864,10 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
// Add an event to prevent optimisation (reuse of the same objects list)
// in the previous event.
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -929,7 +881,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
},
],
actions: [],
events: [],
},
],
},
@@ -1021,8 +972,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -1062,7 +1011,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -1171,8 +1119,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -1203,7 +1149,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -1314,8 +1259,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -1360,7 +1303,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -1490,8 +1432,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -1531,7 +1471,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -1690,7 +1629,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -1799,7 +1737,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -1917,7 +1854,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -1997,8 +1933,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -2038,7 +1972,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -2148,8 +2081,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [
{
@@ -2193,7 +2124,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
])
);
@@ -2310,8 +2240,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const eventsSerializerElement = gd.Serializer.fromJSON(
JSON.stringify([
{
disabled: false,
folded: false,
type: 'BuiltinCommonInstructions::Standard',
conditions: [],
actions: [
@@ -2336,7 +2264,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
parameters: ['SuccessVariable', '+', '1'],
},
],
events: [],
},
])
);
@@ -2509,7 +2436,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
],
},
@@ -2610,7 +2536,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
parameters: ['Counter', '=', '0'],
},
],
events: [],
},
{
infiniteLoopWarning: true,
@@ -2646,7 +2571,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
]);
@@ -2728,7 +2652,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
parameters: ['Counter', '=', '0'],
},
],
events: [],
},
{
infiniteLoopWarning: true,
@@ -2775,7 +2698,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
],
},
@@ -2800,8 +2722,7 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
const runCompiledEvents = generateCompiledEventsForEventsFunction(
gd,
project,
eventsFunction,
true
eventsFunction
);
eventsFunction.delete();
@@ -2880,7 +2801,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
]);
@@ -3018,7 +2938,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
parameters: ['MyOtherParamObject', 'TestVariable', '+', '4'],
},
],
events: [],
},
]);
@@ -3272,7 +3191,6 @@ describe('libGD.js - GDJS Async Code Generation integration tests', function ()
],
},
],
events: [],
},
],
},

View File

@@ -60,6 +60,7 @@ describe('libGD.js object serialization', function() {
obj.setName('testObject');
obj.setString('Text of the object, with 官话 characters');
obj.setTags('inventory, player');
obj.setAssetStoreId('1234');
var serializedObject = new gd.SerializerElement();
obj.serializeTo(serializedObject);
@@ -68,7 +69,7 @@ describe('libGD.js object serialization', function() {
obj.delete();
expect(jsonObject).toBe(
'{"bold":false,"italic":false,"name":"testObject","smoothed":true,"tags":"inventory, player","type":"TextObject::Text","underlined":false,"variables":[],"effects":[],"behaviors":[],"string":"Text of the object, with 官话 characters","font":"","characterSize":20.0,"color":{"b":0,"g":0,"r":0}}'
'{"assetStoreId":"1234","bold":false,"italic":false,"name":"testObject","smoothed":true,"tags":"inventory, player","type":"TextObject::Text","underlined":false,"variables":[],"effects":[],"behaviors":[],"string":"Text of the object, with 官话 characters","font":"","characterSize":20.0,"color":{"b":0,"g":0,"r":0}}'
);
});
});

View File

@@ -10,6 +10,7 @@ declare class gdDirection {
isLooping(): boolean;
setLoop(enable: boolean): void;
getTimeBetweenFrames(): number;
getSpriteNames(): gdVectorString;
setTimeBetweenFrames(time: number): void;
swapSprites(first: number, second: number): void;
moveSprite(oldIndex: number, newIndex: number): void;

View File

@@ -2,7 +2,7 @@
declare class gdEventsRefactorer {
static renameObjectInEvents(platform: gdPlatform, project: gdObjectsContainer, layout: gdObjectsContainer, events: gdEventsList, oldName: string, newName: string): void;
static removeObjectInEvents(platform: gdPlatform, project: gdObjectsContainer, layout: gdObjectsContainer, events: gdEventsList, name: string): void;
static replaceStringInEvents(project: gdObjectsContainer, layout: gdObjectsContainer, events: gdEventsList, toReplace: string, newString: string, matchCase: boolean, inConditions: boolean, inActions: boolean, inEventStrings: boolean): void;
static replaceStringInEvents(project: gdObjectsContainer, layout: gdObjectsContainer, events: gdEventsList, toReplace: string, newString: string, matchCase: boolean, inConditions: boolean, inActions: boolean, inEventStrings: boolean): gdVectorEventsSearchResult;
static searchInEvents(platform: gdPlatform, events: gdEventsList, search: string, matchCase: boolean, inConditions: boolean, inActions: boolean, inEventStrings: boolean, inEventSentences: boolean): gdVectorEventsSearchResult;
delete(): void;
ptr: number;

View File

@@ -0,0 +1,7 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdExpression {
getPlainString(): string;
getRootNode(): gdExpressionNode;
delete(): void;
ptr: number;
};

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdExpressionCompletionFinder {
static getCompletionDescriptionsFor(node: gdExpressionNode, location: number): gdVectorExpressionCompletionDescription;
static getCompletionDescriptionsFor(platform: gdPlatform, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, rootType: string, node: gdExpressionNode, location: number): gdVectorExpressionCompletionDescription;
getCompletionDescriptions(): gdVectorExpressionCompletionDescription;
delete(): void;
ptr: number;

View File

@@ -1,7 +1,7 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdExpressionParser2 {
constructor(platform: gdPlatform, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer): void;
parseExpression(type: string, expression: string): gdUniquePtrExpressionNode;
constructor(): void;
parseExpression(expression: string): gdUniquePtrExpressionNode;
delete(): void;
ptr: number;
};

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdExpressionValidator extends gdExpressionParser2NodeWorker {
constructor(): void;
constructor(platform: gdPlatform, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, rootType: string): void;
getErrors(): gdVectorExpressionParserDiagnostic;
delete(): void;
ptr: number;

View File

@@ -7,7 +7,7 @@ declare class gdInstruction {
setInverted(inverted: boolean): void;
isInverted(): boolean;
setParameter(id: number, value: string): void;
getParameter(id: number): string;
getParameter(id: number): gdExpression;
setParametersCount(count: number): void;
getParametersCount(): number;
getSubInstructions(): gdInstructionsList;

View File

@@ -4,6 +4,8 @@ declare class gdObject {
clone(): gdUniquePtrObject;
setName(name: string): void;
getName(): string;
setAssetStoreId(assetStoreId: string): void;
getAssetStoreId(): string;
setType(type: string): void;
getType(): string;
setTags(tags: string): void;

View File

@@ -19,6 +19,7 @@ declare class gdParameterMetadata {
setDefaultValue(defaultValue_: string): gdParameterMetadata;
static isObject(param: string): boolean;
static isBehavior(param: string): boolean;
static isExpression(type_: string, parameterType: string): boolean;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(element: gdSerializerElement): void;
delete(): void;

View File

@@ -19,6 +19,8 @@ declare class gdProject extends gdObjectsContainer {
setPlayableWithMobile(playable: boolean): void;
setPackageName(packageName: string): void;
getPackageName(): string;
setTemplateSlug(templateSlug: string): void;
getTemplateSlug(): string;
setOrientation(orientation: string): void;
getOrientation(): string;
setProjectUuid(projectUuid: string): void;

View File

@@ -104,6 +104,7 @@ declare class libGDevelop {
Serializer: Class<gdSerializer>;
InstructionsList: Class<gdInstructionsList>;
Instruction: Class<gdInstruction>;
Expression: Class<gdExpression>;
VectorPairStringTextFormatting: Class<gdVectorPairStringTextFormatting>;
TextFormatting: Class<gdTextFormatting>;
InstructionSentenceFormatter: Class<gdInstructionSentenceFormatter>;

View File

@@ -7,11 +7,11 @@ export const globalTypes = {
themeName: {
name: 'Theme',
description: 'Global theme for components',
defaultValue: 'GDevelop default',
defaultValue: 'GDevelop default Dark',
toolbar: {
icon: 'circlehollow',
// See theme names in ThemeRegistry.js
items: ['GDevelop default', 'Dark', 'Nord', 'Solarized Dark', 'One Dark'],
items: ['GDevelop default Dark', 'GDevelop default', 'Dark', 'Nord', 'Solarized Dark', 'One Dark'],
showName: true,
},
},

View File

@@ -8861,6 +8861,20 @@
"integrity": "sha512-USUftMYpmuMzeWobskoPfzDi+vkpe0dvcOBRNOscFrGxVp4jomnRxWuVohgqBow2xyIPC0S3gjxV/5079jhmDg==",
"dev": true
},
"node_modules/@storybook/builder-webpack4/node_modules/acorn": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"dev": true,
"optional": true,
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/@storybook/builder-webpack4/node_modules/babel-loader": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz",
@@ -11403,6 +11417,20 @@
"integrity": "sha512-USUftMYpmuMzeWobskoPfzDi+vkpe0dvcOBRNOscFrGxVp4jomnRxWuVohgqBow2xyIPC0S3gjxV/5079jhmDg==",
"dev": true
},
"node_modules/@storybook/manager-webpack4/node_modules/acorn": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"dev": true,
"optional": true,
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/@storybook/manager-webpack4/node_modules/babel-loader": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz",
@@ -29063,6 +29091,15 @@
"node": ">=6"
}
},
"node_modules/load-json-file/node_modules/type-fest": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
"integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/load-pkg": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/load-pkg/-/load-pkg-3.0.1.tgz",
@@ -39210,6 +39247,15 @@
"node": ">=8"
}
},
"node_modules/tempy/node_modules/type-fest": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
"integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/term-size": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
@@ -39765,12 +39811,17 @@
}
},
"node_modules/type-fest": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
"integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=6"
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/type-is": {
@@ -39802,9 +39853,9 @@
}
},
"node_modules/typescript": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -48773,6 +48824,14 @@
"integrity": "sha512-USUftMYpmuMzeWobskoPfzDi+vkpe0dvcOBRNOscFrGxVp4jomnRxWuVohgqBow2xyIPC0S3gjxV/5079jhmDg==",
"dev": true
},
"acorn": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"dev": true,
"optional": true,
"peer": true
},
"babel-loader": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz",
@@ -50611,6 +50670,14 @@
"integrity": "sha512-USUftMYpmuMzeWobskoPfzDi+vkpe0dvcOBRNOscFrGxVp4jomnRxWuVohgqBow2xyIPC0S3gjxV/5079jhmDg==",
"dev": true
},
"acorn": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"dev": true,
"optional": true,
"peer": true
},
"babel-loader": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz",
@@ -64592,6 +64659,14 @@
"pify": "^4.0.1",
"strip-bom": "^3.0.0",
"type-fest": "^0.3.0"
},
"dependencies": {
"type-fest": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
"integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
"dev": true
}
}
},
"load-pkg": {
@@ -72749,6 +72824,14 @@
"temp-dir": "^1.0.0",
"type-fest": "^0.3.1",
"unique-string": "^1.0.0"
},
"dependencies": {
"type-fest": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
"integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
"dev": true
}
}
},
"term-size": {
@@ -73184,10 +73267,12 @@
"dev": true
},
"type-fest": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
"integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
"dev": true
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"dev": true,
"optional": true,
"peer": true
},
"type-is": {
"version": "1.6.18",
@@ -73215,9 +73300,9 @@
}
},
"typescript": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
"dev": true
},
"ua-parser-js": {

View File

@@ -110,7 +110,7 @@
"reload-extensions": "node scripts/import-GDJS-Runtime.js --skip-clean",
"build-theme-resources": "node scripts/build-theme-resources.js",
"create-new-theme": "node scripts/create-new-theme.js",
"import-zipped-external-editors": "cd scripts && node import-zipped-editor.js piskel 5.0.0-beta82 b8e4d57b160ff93d3680168cd271af795412ea6c4c0da321aee2946345c7fb75 && node import-zipped-editor.js jfxr 5.0.0-beta55 8ac12b557c2ddba958c6f0d3e0c5df8cf3369a65262dcb90cf5c8a7a7d20bdf6 && node import-zipped-editor.js yarn 5.0.134 155f6d074dbb025b082ede0f9b6acd55ed293457441f4c55f084c2d27fbda61d"
"import-zipped-external-editors": "cd scripts && node import-zipped-editor.js piskel 5.0.0-beta82 b8e4d57b160ff93d3680168cd271af795412ea6c4c0da321aee2946345c7fb75 && node import-zipped-editor.js jfxr 5.0.0-beta55 8ac12b557c2ddba958c6f0d3e0c5df8cf3369a65262dcb90cf5c8a7a7d20bdf6 && node import-zipped-editor.js yarn 5.0.134 0199d369d80b72597118a1deaeb99b259d684c34a2bd3f6588a2a9aaa3a0a22f"
},
"eslintConfig": {
"extends": "react-app",

View File

@@ -8,48 +8,47 @@ style.registerAction({
let path = config.files[0].destination;
fs.writeFileSync(
path,
fs.readFileSync(path).toString().replace(':root', '.' + config.theme),
fs
.readFileSync(path)
.toString()
.replace(':root', '.' + config.theme)
);
},
undo() {
},
undo() {},
});
const registry = readThemeRegistry();
for (let registration of registry) {
const theme = registration.id;
style.extend({
source: [
'./src/UI/Theme/Global/styles.json', // Global styles
`./src/UI/Theme/${theme}/theme.json`, // Theme specific overrides
],
platforms: {
'css': {
theme,
'transformGroup': 'css',
'files': [
{
'format': 'css/variables',
'destination': `./src/UI/Theme/${theme}/${theme}Variables.css`,
},
],
'actions': [
'set_theme_class',
],
style
.extend({
source: [
`${__dirname}/../src/UI/Theme/Global/styles.json`, // Global styles
`${__dirname}/../src/UI/Theme/${theme}/theme.json`, // Theme specific overrides
],
platforms: {
css: {
theme,
transformGroup: 'css',
files: [
{
format: 'css/variables',
destination: `${__dirname}/../src/UI/Theme/${theme}/${theme}Variables.css`,
},
],
actions: ['set_theme_class'],
},
js: {
transformGroup: 'js',
files: [
{
format: 'json/flat',
destination: `${__dirname}/../src/UI/Theme/${theme}/${theme}Variables.json`,
},
],
},
},
'js': {
'transformGroup': 'js',
'files': [
{
'format': 'json/flat',
'destination': `./src/UI/Theme/${theme}/${theme}Variables.json`,
},
],
},
},
})
})
.buildAllPlatforms();
}

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