mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
343 Commits
v5.0.0-bet
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
91072f7328 | ||
![]() |
39334c6e55 | ||
![]() |
5f1a7bd72d | ||
![]() |
9ed2665542 | ||
![]() |
ee338f6657 | ||
![]() |
8f876c51dc | ||
![]() |
23a409b80d | ||
![]() |
4e0f9ebec4 | ||
![]() |
2ca593ba2b | ||
![]() |
c63bb625e5 | ||
![]() |
c57e172299 | ||
![]() |
a9cdeae475 | ||
![]() |
931b945b21 | ||
![]() |
e2f21b8d3c | ||
![]() |
6ab2cb1384 | ||
![]() |
f8e0288a44 | ||
![]() |
ff48589661 | ||
![]() |
50bdca3c44 | ||
![]() |
00eda8ced8 | ||
![]() |
7ca5ef6e6c | ||
![]() |
ff8f7e5877 | ||
![]() |
c8739e3c24 | ||
![]() |
69eacedc2b | ||
![]() |
e5f229e3f7 | ||
![]() |
74e43f2b43 | ||
![]() |
a04f641415 | ||
![]() |
2e5a9e2cfa | ||
![]() |
cd4bfd767a | ||
![]() |
d9135636fe | ||
![]() |
88e08ab7d8 | ||
![]() |
6a3af0d57a | ||
![]() |
95b4091085 | ||
![]() |
5556766059 | ||
![]() |
1332582a03 | ||
![]() |
77177063d8 | ||
![]() |
ed7ddd2b67 | ||
![]() |
990f59d093 | ||
![]() |
6326c185f4 | ||
![]() |
9c6972ec0a | ||
![]() |
814577edff | ||
![]() |
a8ea4b8fe7 | ||
![]() |
494666e690 | ||
![]() |
e5a24e3e32 | ||
![]() |
68771be104 | ||
![]() |
9d2bff9442 | ||
![]() |
6a08fb9a86 | ||
![]() |
c96c3ff1a2 | ||
![]() |
788d557f0e | ||
![]() |
4f17d526ab | ||
![]() |
bc27364bb8 | ||
![]() |
1fd719fb41 | ||
![]() |
f7e93c2a13 | ||
![]() |
c268b19264 | ||
![]() |
6baef705eb | ||
![]() |
fe8295a6e3 | ||
![]() |
de616de3fc | ||
![]() |
9e725c58b5 | ||
![]() |
37028de2f4 | ||
![]() |
509dd8ff10 | ||
![]() |
4c38bcffa8 | ||
![]() |
90c2cc7e44 | ||
![]() |
bd6e4206a2 | ||
![]() |
9cf5755a90 | ||
![]() |
11c29f444e | ||
![]() |
11475b9cf3 | ||
![]() |
e5476f5712 | ||
![]() |
e7457c7564 | ||
![]() |
fd015f9ee4 | ||
![]() |
394eb9488c | ||
![]() |
dd771ea3d1 | ||
![]() |
140c7f52cb | ||
![]() |
cb14f7cfa5 | ||
![]() |
93e8dd4002 | ||
![]() |
b91a2da81c | ||
![]() |
d6f99c5841 | ||
![]() |
d2dc352c2a | ||
![]() |
f3dc69ea68 | ||
![]() |
f4522291fc | ||
![]() |
60d7901054 | ||
![]() |
b19e71fe85 | ||
![]() |
2b9524651f | ||
![]() |
680aa3fa6b | ||
![]() |
25f8bddfcf | ||
![]() |
a02e5952a3 | ||
![]() |
b382b99ece | ||
![]() |
f6b16da334 | ||
![]() |
130912f3c8 | ||
![]() |
49418351d4 | ||
![]() |
cbad5de106 | ||
![]() |
1e33a1c6f0 | ||
![]() |
997c251a07 | ||
![]() |
a14e854f4e | ||
![]() |
b0af6c88fe | ||
![]() |
08b1f3b5fe | ||
![]() |
b392192def | ||
![]() |
1759e85b84 | ||
![]() |
a3d223ae39 | ||
![]() |
d2fa8c43cf | ||
![]() |
dc0dcb673f | ||
![]() |
fa2c1bed79 | ||
![]() |
8489cc3e70 | ||
![]() |
0150e197b0 | ||
![]() |
f5a6ca0246 | ||
![]() |
a53b63680c | ||
![]() |
2489a26a08 | ||
![]() |
2346e41936 | ||
![]() |
0b5980d0b6 | ||
![]() |
99fc0b7b46 | ||
![]() |
fb45454951 | ||
![]() |
9abfa741ce | ||
![]() |
980081516a | ||
![]() |
f1bed6ead9 | ||
![]() |
e139c0218b | ||
![]() |
ffd0cf8808 | ||
![]() |
ae87d3298e | ||
![]() |
6b7a9dd39c | ||
![]() |
5a3686d6a3 | ||
![]() |
bfef000cc6 | ||
![]() |
c000a735bb | ||
![]() |
21e034863e | ||
![]() |
72b883654b | ||
![]() |
b1152b9059 | ||
![]() |
7d48b85d42 | ||
![]() |
fb8926dd66 | ||
![]() |
9ce195e371 | ||
![]() |
f88f8b60d6 | ||
![]() |
4eb8ddfba6 | ||
![]() |
aaab3cb212 | ||
![]() |
6b3ce705aa | ||
![]() |
988a7fdb9d | ||
![]() |
11592b11c4 | ||
![]() |
e8791fcdf9 | ||
![]() |
e661923fd3 | ||
![]() |
61c57059fa | ||
![]() |
922019eef0 | ||
![]() |
79ca28fbdb | ||
![]() |
124079c50f | ||
![]() |
2e42fc01be | ||
![]() |
770aad5672 | ||
![]() |
bef1b9fb1e | ||
![]() |
831dce0f51 | ||
![]() |
2da4e79d06 | ||
![]() |
804a07c56e | ||
![]() |
b367f13116 | ||
![]() |
581d7716f7 | ||
![]() |
19de7aefbc | ||
![]() |
57759aa1b8 | ||
![]() |
07876afc28 | ||
![]() |
b9029fba4d | ||
![]() |
c4ba357296 | ||
![]() |
a47acbb82a | ||
![]() |
f1f93c9be0 | ||
![]() |
f56d864efb | ||
![]() |
017f8cf554 | ||
![]() |
d8546c5547 | ||
![]() |
0bc6e41709 | ||
![]() |
943fac772d | ||
![]() |
327e4cb6a3 | ||
![]() |
277989f329 | ||
![]() |
2bdae4ed14 | ||
![]() |
4bfbd7c78f | ||
![]() |
7b76564dda | ||
![]() |
41a2b87dd0 | ||
![]() |
397781bb98 | ||
![]() |
b4fa741717 | ||
![]() |
6e16bd764f | ||
![]() |
1071b66c92 | ||
![]() |
9ff6d91717 | ||
![]() |
bda1eb01d2 | ||
![]() |
dfcaf472c7 | ||
![]() |
c8a9da6aea | ||
![]() |
daa50931ae | ||
![]() |
f84a97a4f2 | ||
![]() |
10d5c403a7 | ||
![]() |
15471e6e28 | ||
![]() |
7ccebc69fa | ||
![]() |
4c57fbc01f | ||
![]() |
ed58ebd3be | ||
![]() |
9cae4fb264 | ||
![]() |
83e7314863 | ||
![]() |
8ec56164af | ||
![]() |
ba0c4a9bc4 | ||
![]() |
8706dc727d | ||
![]() |
7ee9facb34 | ||
![]() |
4190cbda88 | ||
![]() |
aa73b01bbc | ||
![]() |
b242c7863f | ||
![]() |
41550ee10f | ||
![]() |
7af0999f59 | ||
![]() |
93e0ccc163 | ||
![]() |
94303fccc2 | ||
![]() |
33949fd93c | ||
![]() |
bd40bb892c | ||
![]() |
c2cf935bd9 | ||
![]() |
c7d5ab8013 | ||
![]() |
e8c93a5622 | ||
![]() |
348459481a | ||
![]() |
e7348f08c4 | ||
![]() |
ef96adee92 | ||
![]() |
66b3ec1686 | ||
![]() |
b77eb123e8 | ||
![]() |
dcba4272e0 | ||
![]() |
c2dd5a0a09 | ||
![]() |
a6ae265705 | ||
![]() |
94fb2fede6 | ||
![]() |
7bcaf55342 | ||
![]() |
cc9632e7c1 | ||
![]() |
3ff5dd7cd2 | ||
![]() |
1193e1bbd0 | ||
![]() |
9c55b0acc6 | ||
![]() |
d03c1964cb | ||
![]() |
e5695aacf6 | ||
![]() |
8e6d2da9f7 | ||
![]() |
5c043fd04a | ||
![]() |
4edbd9d377 | ||
![]() |
c6f21955a3 | ||
![]() |
03cc406459 | ||
![]() |
c5d855b768 | ||
![]() |
2aba1c57c4 | ||
![]() |
9614549436 | ||
![]() |
f1730c239d | ||
![]() |
acb19bf8ed | ||
![]() |
4d63fbcca0 | ||
![]() |
eb19b6ba21 | ||
![]() |
d2747782b4 | ||
![]() |
8202e6b38e | ||
![]() |
6deb4fa122 | ||
![]() |
3990064da9 | ||
![]() |
a315eabdce | ||
![]() |
faa02d4459 | ||
![]() |
8b294ae369 | ||
![]() |
cb43eb7780 | ||
![]() |
765295fc5d | ||
![]() |
beb3bde4a8 | ||
![]() |
fe5b519917 | ||
![]() |
8661fbef07 | ||
![]() |
afd11d2480 | ||
![]() |
c5ad9715df | ||
![]() |
4362e8dd42 | ||
![]() |
372fa46709 | ||
![]() |
c43cfcd49e | ||
![]() |
c471a0af6d | ||
![]() |
b1ea60e1d2 | ||
![]() |
f745907f9d | ||
![]() |
92df124a92 | ||
![]() |
96f26c89ab | ||
![]() |
3f6428dfcc | ||
![]() |
22c6a57394 | ||
![]() |
dc942e6abc | ||
![]() |
f9430a0da1 | ||
![]() |
94ac7166ed | ||
![]() |
88b20240ff | ||
![]() |
af93149f6a | ||
![]() |
732a716be4 | ||
![]() |
11b660e05d | ||
![]() |
6155606d20 | ||
![]() |
89e3853296 | ||
![]() |
df655f2269 | ||
![]() |
6b6ec6f06f | ||
![]() |
d80a47e569 | ||
![]() |
9cf9d09f3a | ||
![]() |
8fa23c5463 | ||
![]() |
1c65e3c655 | ||
![]() |
b1e292e04e | ||
![]() |
e2f8f70d54 | ||
![]() |
d04faa039b | ||
![]() |
4e59573042 | ||
![]() |
07fce517d6 | ||
![]() |
ac90b982ac | ||
![]() |
4df974a4d7 | ||
![]() |
cebf1e2a84 | ||
![]() |
650676cc6e | ||
![]() |
a3614a85b8 | ||
![]() |
ef52fec3ca | ||
![]() |
ca6f11b55a | ||
![]() |
7015962aa0 | ||
![]() |
4df7f0d10f | ||
![]() |
8c7ffe319a | ||
![]() |
710c2f0304 | ||
![]() |
727fa8a538 | ||
![]() |
f45e4c2049 | ||
![]() |
3bdf612f8e | ||
![]() |
4f7c91190e | ||
![]() |
5ed0c57e48 | ||
![]() |
3955612e3b | ||
![]() |
8eede20b07 | ||
![]() |
3df9b29c3e | ||
![]() |
280906dd3a | ||
![]() |
94e81ddf40 | ||
![]() |
392e602651 | ||
![]() |
0d60a54fa7 | ||
![]() |
e95a336dd2 | ||
![]() |
b8ccf02f70 | ||
![]() |
a564a484a7 | ||
![]() |
b9c1f5f6a7 | ||
![]() |
243bc93fe5 | ||
![]() |
d544319302 | ||
![]() |
0fa5988995 | ||
![]() |
746b2f5480 | ||
![]() |
2cc7c8740e | ||
![]() |
aa30052dca | ||
![]() |
beb1cf0631 | ||
![]() |
78640d74c8 | ||
![]() |
7becb0be4b | ||
![]() |
6c789b7eb0 | ||
![]() |
f743a785ad | ||
![]() |
bac8aa14fa | ||
![]() |
3ebb483e32 | ||
![]() |
4fbbd34d40 | ||
![]() |
24091db88b | ||
![]() |
4ec3f1f082 | ||
![]() |
991378e004 | ||
![]() |
dfaee92d24 | ||
![]() |
b07c71cb9c | ||
![]() |
cb0c0f903f | ||
![]() |
a81a121a8d | ||
![]() |
7bf892c7eb | ||
![]() |
ce4fdbe4f8 | ||
![]() |
d82bbdc1af | ||
![]() |
62dca91637 | ||
![]() |
1e1c5f7206 | ||
![]() |
7c9e6ee6f1 | ||
![]() |
5690de71c5 | ||
![]() |
eae2a06a4e | ||
![]() |
4affa25672 | ||
![]() |
7b30f63cb1 | ||
![]() |
574bdaaf41 | ||
![]() |
bff43ee771 | ||
![]() |
9b9b6c5996 | ||
![]() |
721872e45b | ||
![]() |
edd9ec94cf | ||
![]() |
c986c52409 | ||
![]() |
168b8d3fea | ||
![]() |
47025329dd | ||
![]() |
9f05a65ed2 | ||
![]() |
370c6d03ad | ||
![]() |
b650ff3aa5 | ||
![]() |
285ff6f5f5 | ||
![]() |
81f292941f | ||
![]() |
eb27ba7c86 | ||
![]() |
01a844c356 | ||
![]() |
054e48227c | ||
![]() |
9adc40a55d |
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install sdk-fastcomp-1.37.37-64bit && ./emsdk activate sdk-fastcomp-1.37.37-64bit && cd ..
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
|
||||
- run:
|
||||
name: Install Wine for Electron builder
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -44,9 +44,6 @@
|
||||
/Binaries/**/JsPlatform/*.dll.a
|
||||
/Binaries/Output/Release_Windows/newIDE
|
||||
*.autosave
|
||||
/Binaries/Output/libGD.js/Release
|
||||
/Binaries/Output/libGD.js/Debug
|
||||
/Binaries/Output/libGD.js/libGD.raw.js
|
||||
!/GDCpp/scripts/bcp.exe
|
||||
!/scripts/libgettextlib-0-17.dll
|
||||
!/scripts/libgettextsrc-0-17.dll
|
||||
|
@@ -24,7 +24,7 @@ addons:
|
||||
- /$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)/commit/$(git rev-parse HEAD)
|
||||
- /$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)/latest
|
||||
paths:
|
||||
- Binaries/Output/libGD.js/Release
|
||||
- Binaries/embuild/GDevelop.js
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
@@ -63,8 +63,8 @@ install:
|
||||
# Install Emscripten (for GDevelop.js)
|
||||
- git clone https://github.com/juj/emsdk.git
|
||||
- cd emsdk
|
||||
- ./emsdk install sdk-fastcomp-1.37.37-64bit
|
||||
- ./emsdk activate sdk-fastcomp-1.37.37-64bit
|
||||
- ./emsdk install 1.39.6
|
||||
- ./emsdk activate 1.39.6
|
||||
- source ./emsdk_env.sh
|
||||
- cd ..
|
||||
# Install GDevelop.js dependencies and compile it
|
||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -83,7 +83,8 @@
|
||||
"__threading_support": "cpp",
|
||||
"any": "cpp",
|
||||
"array": "cpp",
|
||||
"cinttypes": "cpp"
|
||||
"cinttypes": "cpp",
|
||||
"numeric": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
|
@@ -51,7 +51,12 @@ file(GLOB_RECURSE formatted_source_files tests/* GDCore/Events/* GDCore/Extensio
|
||||
list(REMOVE_ITEM formatted_source_files "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs.h" "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs_dialogs_bitmaps.cpp")
|
||||
gd_add_clang_utils(GDCore "${formatted_source_files}")
|
||||
|
||||
add_library(GDCore SHARED ${source_files})
|
||||
IF(EMSCRIPTEN)
|
||||
# Emscripten treats all libraries as static libraries
|
||||
add_library(GDCore STATIC ${source_files})
|
||||
ELSE()
|
||||
add_library(GDCore SHARED ${source_files})
|
||||
ENDIF()
|
||||
add_dependencies(GDCore GDVersion)
|
||||
IF(EMSCRIPTEN)
|
||||
set_target_properties(GDCore PROPERTIES SUFFIX ".bc")
|
||||
|
@@ -12,6 +12,15 @@ using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
vector<gd::String> CommentEvent::GetAllSearchableStrings() const {
|
||||
vector<gd::String> allSearchableStrings;
|
||||
|
||||
allSearchableStrings.push_back(com1);
|
||||
allSearchableStrings.push_back(com2); ///< Com2 is deprecated
|
||||
|
||||
return allSearchableStrings;
|
||||
}
|
||||
|
||||
void CommentEvent::SerializeTo(SerializerElement &element) const {
|
||||
element.AddChild("color")
|
||||
.SetAttribute("r", r)
|
||||
|
@@ -7,10 +7,11 @@
|
||||
#define COMMENTEVENT_H
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
namespace gd {
|
||||
class Layout;
|
||||
class Project;
|
||||
}
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -45,6 +46,8 @@ class GD_CORE_API CommentEvent : public gd::BaseEvent {
|
||||
const gd::String& GetComment() const { return com1; }
|
||||
void SetComment(const gd::String& comment) { com1 = comment; }
|
||||
|
||||
virtual std::vector<gd::String> GetAllSearchableStrings() const;
|
||||
|
||||
virtual void SerializeTo(SerializerElement& element) const;
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
|
@@ -5,10 +5,6 @@
|
||||
*/
|
||||
|
||||
#include "ForEachEvent.h"
|
||||
#include <iostream>
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
|
||||
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
|
||||
#include "GDCore/Events/Serialization.h"
|
||||
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
@@ -35,12 +31,17 @@ vector<gd::InstructionsList*> ForEachEvent::GetAllActionsVectors() {
|
||||
return allActions;
|
||||
}
|
||||
|
||||
vector<gd::Expression*> ForEachEvent::GetAllExpressions() {
|
||||
vector<gd::Expression*> allExpressions;
|
||||
allExpressions.push_back(&objectsToPick);
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
ForEachEvent::GetAllExpressionsWithMetadata() {
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
allExpressionsWithMetadata;
|
||||
auto metadata = gd::ParameterMetadata().SetType("object");
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&objectsToPick, metadata));
|
||||
|
||||
return allExpressions;
|
||||
return allExpressionsWithMetadata;
|
||||
}
|
||||
|
||||
vector<const gd::InstructionsList*> ForEachEvent::GetAllConditionsVectors()
|
||||
const {
|
||||
vector<const gd::InstructionsList*> allConditions;
|
||||
@@ -56,11 +57,15 @@ vector<const gd::InstructionsList*> ForEachEvent::GetAllActionsVectors() const {
|
||||
return allActions;
|
||||
}
|
||||
|
||||
vector<const gd::Expression*> ForEachEvent::GetAllExpressions() const {
|
||||
vector<const gd::Expression*> allExpressions;
|
||||
allExpressions.push_back(&objectsToPick);
|
||||
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
ForEachEvent::GetAllExpressionsWithMetadata() const {
|
||||
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
allExpressionsWithMetadata;
|
||||
auto metadata = gd::ParameterMetadata().SetType("object");
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&objectsToPick, metadata));
|
||||
|
||||
return allExpressions;
|
||||
return allExpressionsWithMetadata;
|
||||
}
|
||||
|
||||
void ForEachEvent::SerializeTo(SerializerElement& element) const {
|
||||
|
@@ -50,10 +50,13 @@ class GD_CORE_API ForEachEvent : public gd::BaseEvent {
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllConditionsVectors()
|
||||
const;
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllActionsVectors() const;
|
||||
virtual std::vector<const gd::Expression*> GetAllExpressions() const;
|
||||
virtual std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
GetAllExpressionsWithMetadata() const;
|
||||
|
||||
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors();
|
||||
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors();
|
||||
virtual std::vector<gd::Expression*> GetAllExpressions();
|
||||
virtual std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
GetAllExpressionsWithMetadata();
|
||||
|
||||
virtual void SerializeTo(SerializerElement& element) const;
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
|
@@ -19,6 +19,14 @@ namespace gd {
|
||||
GroupEvent::GroupEvent()
|
||||
: BaseEvent(), creationTime(0), colorR(74), colorG(176), colorB(228) {}
|
||||
|
||||
vector<gd::String> GroupEvent::GetAllSearchableStrings() const {
|
||||
vector<gd::String> allSearchableStrings;
|
||||
|
||||
allSearchableStrings.push_back(name);
|
||||
|
||||
return allSearchableStrings;
|
||||
}
|
||||
|
||||
void GroupEvent::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", name);
|
||||
element.SetAttribute("source", source);
|
||||
|
@@ -106,6 +106,8 @@ class GD_CORE_API GroupEvent : public gd::BaseEvent {
|
||||
virtual const gd::EventsList& GetSubEvents() const { return events; };
|
||||
virtual gd::EventsList& GetSubEvents() { return events; };
|
||||
|
||||
virtual std::vector<gd::String> GetAllSearchableStrings() const;
|
||||
|
||||
virtual void SerializeTo(SerializerElement& element) const;
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
|
@@ -5,9 +5,6 @@
|
||||
*/
|
||||
|
||||
#include "RepeatEvent.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
|
||||
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
|
||||
#include "GDCore/Events/Serialization.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
@@ -34,11 +31,15 @@ vector<gd::InstructionsList*> RepeatEvent::GetAllActionsVectors() {
|
||||
return allActions;
|
||||
}
|
||||
|
||||
vector<gd::Expression*> RepeatEvent::GetAllExpressions() {
|
||||
vector<gd::Expression*> allExpressions;
|
||||
allExpressions.push_back(&repeatNumberExpression);
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
RepeatEvent::GetAllExpressionsWithMetadata() {
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
allExpressionsWithMetadata;
|
||||
auto metadata = gd::ParameterMetadata().SetType("expression");
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&repeatNumberExpression, metadata));
|
||||
|
||||
return allExpressions;
|
||||
return allExpressionsWithMetadata;
|
||||
}
|
||||
|
||||
vector<const gd::InstructionsList*> RepeatEvent::GetAllConditionsVectors()
|
||||
@@ -56,11 +57,15 @@ vector<const gd::InstructionsList*> RepeatEvent::GetAllActionsVectors() const {
|
||||
return allActions;
|
||||
}
|
||||
|
||||
vector<const gd::Expression*> RepeatEvent::GetAllExpressions() const {
|
||||
vector<const gd::Expression*> allExpressions;
|
||||
allExpressions.push_back(&repeatNumberExpression);
|
||||
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
RepeatEvent::GetAllExpressionsWithMetadata() const {
|
||||
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
allExpressionsWithMetadata;
|
||||
auto metadata = gd::ParameterMetadata().SetType("expression");
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&repeatNumberExpression, metadata));
|
||||
|
||||
return allExpressions;
|
||||
return allExpressionsWithMetadata;
|
||||
}
|
||||
|
||||
void RepeatEvent::SerializeTo(SerializerElement& element) const {
|
||||
|
@@ -45,11 +45,14 @@ class GD_CORE_API RepeatEvent : public gd::BaseEvent {
|
||||
|
||||
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors();
|
||||
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors();
|
||||
virtual std::vector<gd::Expression*> GetAllExpressions();
|
||||
virtual std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
GetAllExpressionsWithMetadata();
|
||||
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllConditionsVectors()
|
||||
const;
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllActionsVectors() const;
|
||||
virtual std::vector<const gd::Expression*> GetAllExpressions() const;
|
||||
virtual std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
GetAllExpressionsWithMetadata() const;
|
||||
|
||||
virtual void SerializeTo(SerializerElement& element) const;
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
|
@@ -6,9 +6,6 @@
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "WhileEvent.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
|
||||
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
|
||||
#include "GDCore/Events/Serialization.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
|
||||
#include "GDCore/Events/CodeGeneration/ExpressionCodeGenerator.h"
|
||||
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser.h"
|
||||
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
|
||||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
@@ -541,6 +541,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
if (MetadataProvider::HasBehaviorAction(
|
||||
platform, behaviorType, action.GetType()) &&
|
||||
instrInfos.parameters.size() >= 2) {
|
||||
|
||||
std::vector<gd::String> realObjects =
|
||||
ExpandObjectsName(objectName, context);
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
@@ -630,7 +631,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
|
||||
argOutput = "\"" + argOutput + "\"";
|
||||
} else if (ParameterMetadata::IsBehavior(metadata.type)) {
|
||||
argOutput = "\"" + ConvertToString(parameter) + "\"";
|
||||
argOutput = GenerateGetBehaviorNameCode(parameter);
|
||||
} else if (metadata.type == "key") {
|
||||
argOutput = "\"" + ConvertToString(parameter) + "\"";
|
||||
} else if (metadata.type == "password" || metadata.type == "musicfile" ||
|
||||
@@ -662,7 +663,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
if (argOutput.empty()) {
|
||||
if (!metadata.type.empty())
|
||||
cout << "Warning: Unknown type of parameter \"" << metadata.type
|
||||
<< "\".";
|
||||
<< "\"." << std::endl;
|
||||
argOutput += "\"" + ConvertToString(parameter) + "\"";
|
||||
}
|
||||
}
|
||||
|
@@ -35,10 +35,6 @@ namespace gd {
|
||||
* \brief Internal class used to generate code from events
|
||||
*/
|
||||
class GD_CORE_API EventsCodeGenerator {
|
||||
// Compatiblity with old ExpressionParser
|
||||
friend class CallbacksForGeneratingExpressionCode;
|
||||
friend class VariableCodeGenerationCallbacks;
|
||||
// end of compatibility code
|
||||
friend class ExpressionCodeGenerator;
|
||||
|
||||
public:
|
||||
|
@@ -24,117 +24,14 @@
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
// Compatibility with old ExpressionParser
|
||||
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
|
||||
#include "GDCore/Events/CodeGeneration/VariableParserCallbacks.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser.h"
|
||||
#include "GDCore/Events/Parsers/VariableParser.h"
|
||||
// end of compatibility code
|
||||
|
||||
namespace gd {
|
||||
|
||||
bool ExpressionCodeGenerator::useOldExpressionParser = false;
|
||||
|
||||
gd::String ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
EventsCodeGenerator& codeGenerator,
|
||||
EventsCodeGenerationContext& context,
|
||||
const gd::String& type,
|
||||
const gd::String& expression,
|
||||
const gd::String& objectName) {
|
||||
// Compatibility with old ExpressionParser
|
||||
if (useOldExpressionParser) {
|
||||
if (type == "number") {
|
||||
gd::String code = "";
|
||||
gd::CallbacksForGeneratingExpressionCode callbacks(
|
||||
code, codeGenerator, context);
|
||||
gd::ExpressionParser parser(expression);
|
||||
if (!parser.ParseMathExpression(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
callbacks) ||
|
||||
code.empty()) {
|
||||
std::cout << "Error (old ExpressionParser): \""
|
||||
<< parser.GetFirstError() << "\" in: \"" << expression
|
||||
<< "\" (number)" << std::endl;
|
||||
code = "0";
|
||||
}
|
||||
|
||||
return code;
|
||||
} else if (type == "string") {
|
||||
gd::String code = "";
|
||||
gd::CallbacksForGeneratingExpressionCode callbacks(
|
||||
code, codeGenerator, context);
|
||||
gd::ExpressionParser parser(expression);
|
||||
if (!parser.ParseStringExpression(
|
||||
codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
callbacks) ||
|
||||
code.empty()) {
|
||||
std::cout << "Error (old ExpressionParser): \""
|
||||
<< parser.GetFirstError() << "\" in: \"" << expression
|
||||
<< "\" (string)" << std::endl;
|
||||
code = "\"\"";
|
||||
}
|
||||
|
||||
return code;
|
||||
} else if (type == "scenevar") {
|
||||
gd::String code = "";
|
||||
gd::VariableCodeGenerationCallbacks callbacks(
|
||||
code,
|
||||
codeGenerator,
|
||||
context,
|
||||
gd::EventsCodeGenerator::LAYOUT_VARIABLE);
|
||||
|
||||
gd::VariableParser parser(expression);
|
||||
if (!parser.Parse(callbacks)) {
|
||||
std::cout << "Error (old VariableParser) :" << parser.GetFirstError()
|
||||
<< " in: " << expression << std::endl;
|
||||
code = codeGenerator.GenerateBadVariable();
|
||||
}
|
||||
return code;
|
||||
} else if (type == "globalvar") {
|
||||
gd::String code = "";
|
||||
gd::VariableCodeGenerationCallbacks callbacks(
|
||||
code,
|
||||
codeGenerator,
|
||||
context,
|
||||
gd::EventsCodeGenerator::PROJECT_VARIABLE);
|
||||
|
||||
gd::VariableParser parser(expression);
|
||||
if (!parser.Parse(callbacks)) {
|
||||
std::cout << "Error (old VariableParser) :" << parser.GetFirstError()
|
||||
<< " in: " << expression << std::endl;
|
||||
code = codeGenerator.GenerateBadVariable();
|
||||
}
|
||||
return code;
|
||||
} else if (type == "objectvar") {
|
||||
gd::String code = "";
|
||||
|
||||
// Object is either the object of the previous parameter or, if it is
|
||||
// empty, the object being picked by the instruction.
|
||||
gd::String object =
|
||||
objectName.empty() ? context.GetCurrentObject() : objectName;
|
||||
|
||||
gd::VariableCodeGenerationCallbacks callbacks(
|
||||
code, codeGenerator, context, object);
|
||||
|
||||
gd::VariableParser parser(expression);
|
||||
if (!parser.Parse(callbacks)) {
|
||||
std::cout << "Error (old VariableParser) :" << parser.GetFirstError()
|
||||
<< " in: " << expression << std::endl;
|
||||
code = codeGenerator.GenerateBadVariable();
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
std::cout << "Type error (old ExpressionParser): type \"" << type
|
||||
<< "\" is not supported" << std::endl;
|
||||
return "/* Error during code generation: type " + type +
|
||||
" is not supported for old ExpressionParser. */ 0";
|
||||
}
|
||||
// end of compatibility code
|
||||
|
||||
gd::ExpressionParser2 parser(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups());
|
||||
@@ -230,7 +127,7 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
}
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitFunctionNode(FunctionNode& node) {
|
||||
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(node.expressionMetadata)) {
|
||||
output += "/* Error during generation, function not found: " +
|
||||
codeGenerator.ConvertToString(node.functionName) + " for type " +
|
||||
@@ -462,4 +359,8 @@ void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
output += GenerateDefaultValue(node.type);
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) {
|
||||
output += GenerateDefaultValue(node.type);
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -60,11 +60,6 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
|
||||
const gd::String& expression,
|
||||
const gd::String& objectName = "");
|
||||
|
||||
static void UseOldExpressionParser(bool enable) {
|
||||
useOldExpressionParser = enable;
|
||||
};
|
||||
static bool IsUsingOldExpressionParser() { return useOldExpressionParser; };
|
||||
|
||||
const gd::String& GetOutput() { return output; };
|
||||
|
||||
protected:
|
||||
@@ -78,7 +73,8 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override;
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override;
|
||||
void OnVisitFunctionNode(FunctionNode& node) override;
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override;
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override;
|
||||
void OnVisitEmptyNode(EmptyNode& node) override;
|
||||
|
||||
private:
|
||||
@@ -107,11 +103,9 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
|
||||
gd::String output;
|
||||
EventsCodeGenerator& codeGenerator;
|
||||
EventsCodeGenerationContext& context;
|
||||
|
||||
static bool useOldExpressionParser;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_ExpressionCodeGenerator_H
|
||||
#endif
|
||||
#endif
|
||||
|
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "ExpressionsCodeGeneration.h"
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser.h"
|
||||
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
|
||||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
CallbacksForGeneratingExpressionCode::CallbacksForGeneratingExpressionCode(
|
||||
gd::String& plainExpression_,
|
||||
EventsCodeGenerator& codeGenerator_,
|
||||
EventsCodeGenerationContext& context_)
|
||||
: plainExpression(plainExpression_),
|
||||
codeGenerator(codeGenerator_),
|
||||
context(context_) {}
|
||||
|
||||
void CallbacksForGeneratingExpressionCode::OnConstantToken(gd::String text) {
|
||||
plainExpression += text;
|
||||
};
|
||||
|
||||
void CallbacksForGeneratingExpressionCode::OnStaticFunction(
|
||||
gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo) {
|
||||
codeGenerator.AddIncludeFiles(
|
||||
expressionInfo.codeExtraInformation.GetIncludeFiles());
|
||||
|
||||
// Launch custom code generator if needed
|
||||
if (expressionInfo.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
plainExpression += expressionInfo.codeExtraInformation.customCodeGenerator(
|
||||
parameters, codeGenerator, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: For strings expressions, function without name is a string.
|
||||
if (GetReturnType() == "string" && functionName.empty()) {
|
||||
if (parameters.empty()) return;
|
||||
plainExpression +=
|
||||
codeGenerator.ConvertToStringExplicit(parameters[0].GetPlainString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare parameters
|
||||
std::vector<gd::String> parametersCode =
|
||||
codeGenerator.GenerateParametersCodes(
|
||||
parameters, expressionInfo.parameters, context);
|
||||
gd::String parametersStr;
|
||||
for (std::size_t i = 0; i < parametersCode.size(); ++i) {
|
||||
if (i != 0) parametersStr += ", ";
|
||||
parametersStr += parametersCode[i];
|
||||
}
|
||||
|
||||
plainExpression += expressionInfo.codeExtraInformation.functionCallName +
|
||||
"(" + parametersStr + ")";
|
||||
};
|
||||
|
||||
void CallbacksForGeneratingExpressionCode::OnObjectFunction(
|
||||
gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo) {
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups = codeGenerator.GetGlobalObjectsAndGroups();
|
||||
const gd::ObjectsContainer& objectsAndGroups = codeGenerator.GetObjectsAndGroups();
|
||||
|
||||
codeGenerator.AddIncludeFiles(
|
||||
expressionInfo.codeExtraInformation.GetIncludeFiles());
|
||||
if (parameters.empty()) return;
|
||||
|
||||
// Launch custom code generator if needed
|
||||
if (expressionInfo.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
plainExpression += expressionInfo.codeExtraInformation.customCodeGenerator(
|
||||
parameters, codeGenerator, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare parameters
|
||||
std::vector<gd::String> parametersCode =
|
||||
codeGenerator.GenerateParametersCodes(
|
||||
parameters, expressionInfo.parameters, context);
|
||||
gd::String parametersStr;
|
||||
for (std::size_t i = 1; i < parametersCode.size(); ++i) {
|
||||
if (i != 1) parametersStr += ", ";
|
||||
parametersStr += parametersCode[i];
|
||||
}
|
||||
|
||||
gd::String output = GetReturnType() == "string" ? "\"\"" : "0";
|
||||
|
||||
// Get object(s) concerned by function call
|
||||
std::vector<gd::String> realObjects =
|
||||
codeGenerator.ExpandObjectsName(parameters[0].GetPlainString(), context);
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
|
||||
gd::String objectType = gd::GetTypeOfObject(globalObjectsAndGroups, objectsAndGroups, realObjects[i]);
|
||||
const ObjectMetadata& objInfo = MetadataProvider::GetObjectMetadata(
|
||||
codeGenerator.GetPlatform(), objectType);
|
||||
|
||||
// Build gd::String to access the object
|
||||
codeGenerator.AddIncludeFiles(objInfo.includeFiles);
|
||||
output = codeGenerator.GenerateObjectFunctionCall(
|
||||
realObjects[i],
|
||||
objInfo,
|
||||
expressionInfo.codeExtraInformation,
|
||||
parametersStr,
|
||||
output,
|
||||
context);
|
||||
}
|
||||
|
||||
plainExpression += output;
|
||||
};
|
||||
|
||||
void CallbacksForGeneratingExpressionCode::OnObjectBehaviorFunction(
|
||||
gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo) {
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups = codeGenerator.GetGlobalObjectsAndGroups();
|
||||
const gd::ObjectsContainer& objectsAndGroups = codeGenerator.GetObjectsAndGroups();
|
||||
|
||||
codeGenerator.AddIncludeFiles(
|
||||
expressionInfo.codeExtraInformation.GetIncludeFiles());
|
||||
if (parameters.size() < 2) return;
|
||||
|
||||
// Launch custom code generator if needed
|
||||
if (expressionInfo.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
plainExpression += expressionInfo.codeExtraInformation.customCodeGenerator(
|
||||
parameters, codeGenerator, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare parameters
|
||||
std::vector<gd::String> parametersCode =
|
||||
codeGenerator.GenerateParametersCodes(
|
||||
parameters, expressionInfo.parameters, context);
|
||||
gd::String parametersStr;
|
||||
for (std::size_t i = 2; i < parametersCode.size(); ++i) {
|
||||
if (i != 2) parametersStr += ", ";
|
||||
parametersStr += parametersCode[i];
|
||||
}
|
||||
|
||||
// Get object(s) concerned by function call
|
||||
std::vector<gd::String> realObjects =
|
||||
codeGenerator.ExpandObjectsName(parameters[0].GetPlainString(), context);
|
||||
|
||||
gd::String output = GetReturnType() == "string" ? "\"\"" : "0";
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
|
||||
// Cast the object if needed
|
||||
gd::String behaviorType =
|
||||
gd::GetTypeOfBehavior(globalObjectsAndGroups, objectsAndGroups, parameters[1].GetPlainString());
|
||||
const BehaviorMetadata& autoInfo = MetadataProvider::GetBehaviorMetadata(
|
||||
codeGenerator.GetPlatform(), behaviorType);
|
||||
|
||||
// Build gd::String to access the behavior
|
||||
codeGenerator.AddIncludeFiles(autoInfo.includeFiles);
|
||||
output = codeGenerator.GenerateObjectBehaviorFunctionCall(
|
||||
realObjects[i],
|
||||
parameters[1].GetPlainString(),
|
||||
autoInfo,
|
||||
expressionInfo.codeExtraInformation,
|
||||
parametersStr,
|
||||
output,
|
||||
context);
|
||||
}
|
||||
|
||||
plainExpression += output;
|
||||
};
|
||||
|
||||
bool CallbacksForGeneratingExpressionCode::OnSubMathExpression(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups,
|
||||
const gd::ObjectsContainer& objectsAndGroups,
|
||||
gd::Expression& expression) {
|
||||
gd::String newExpression;
|
||||
|
||||
CallbacksForGeneratingExpressionCode callbacks(
|
||||
newExpression, codeGenerator, context);
|
||||
|
||||
gd::ExpressionParser parser(expression.GetPlainString());
|
||||
if (!parser.ParseMathExpression(platform, globalObjectsAndGroups, objectsAndGroups, callbacks)) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
firstErrorStr = callbacks.GetFirstError();
|
||||
firstErrorPos = callbacks.GetFirstErrorPosition();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallbacksForGeneratingExpressionCode::OnSubTextExpression(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups,
|
||||
const gd::ObjectsContainer& objectsAndGroups,
|
||||
gd::Expression& expression) {
|
||||
gd::String newExpression;
|
||||
|
||||
CallbacksForGeneratingExpressionCode callbacks(
|
||||
newExpression, codeGenerator, context);
|
||||
|
||||
gd::ExpressionParser parser(expression.GetPlainString());
|
||||
if (!parser.ParseStringExpression(platform, globalObjectsAndGroups, objectsAndGroups, callbacks)) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
firstErrorStr = callbacks.GetFirstError();
|
||||
firstErrorPos = callbacks.GetFirstErrorPosition();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef EXPRESSIONSCODEGENERATION_H
|
||||
#define EXPRESSIONSCODEGENERATION_H
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class ExpressionMetadata;
|
||||
class Expression;
|
||||
class Project;
|
||||
class Layout;
|
||||
class Layout;
|
||||
class EventsCodeGenerationContext;
|
||||
class EventsCodeGenerator;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
// TODO: Replace and remove (ExpressionCodeGenerator)
|
||||
|
||||
/**
|
||||
* \brief Used to generate code from expressions.
|
||||
*
|
||||
* Usage example :
|
||||
* \code
|
||||
* gd::String expressionOutputCppCode;
|
||||
*
|
||||
* CallbacksForGeneratingExpressionCode callbacks(expressionOutputCppCode,
|
||||
* codeGenerator, context); gd::ExpressionParser
|
||||
* parser(theOriginalGameDevelopExpression);
|
||||
* parser.ParseStringExpression(platform, project, scene, callbacks);
|
||||
*
|
||||
* if (expressionOutputCppCode.empty()) expressionOutputCppCode = "\"\""; //If
|
||||
* generation failed, we make sure output code is not empty. \endcode \see
|
||||
* EventsCodeGenerator
|
||||
*/
|
||||
class GD_CORE_API CallbacksForGeneratingExpressionCode
|
||||
: public gd::ParserCallbacks {
|
||||
public:
|
||||
CallbacksForGeneratingExpressionCode(gd::String& output,
|
||||
EventsCodeGenerator& codeGenerator_,
|
||||
EventsCodeGenerationContext& context_);
|
||||
virtual ~CallbacksForGeneratingExpressionCode(){};
|
||||
|
||||
void OnConstantToken(gd::String text);
|
||||
void OnStaticFunction(gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo);
|
||||
void OnObjectFunction(gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo);
|
||||
void OnObjectBehaviorFunction(gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo);
|
||||
bool OnSubMathExpression(const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::Expression& expression);
|
||||
bool OnSubTextExpression(const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::Expression& expression);
|
||||
|
||||
private:
|
||||
gd::String& plainExpression;
|
||||
EventsCodeGenerator& codeGenerator;
|
||||
EventsCodeGenerationContext& context;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // EXPRESSIONSCODEGENERATION_H
|
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* GDevelop C++ Platform
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "VariableParserCallbacks.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
|
||||
#include "GDCore/Events/CodeGeneration/ExpressionCodeGenerator.h"
|
||||
#include "GDCore/Events/Parsers/VariableParser.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/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
VariableCodeGenerationCallbacks::VariableCodeGenerationCallbacks(
|
||||
gd::String& output_,
|
||||
gd::EventsCodeGenerator& codeGenerator_,
|
||||
gd::EventsCodeGenerationContext& context_,
|
||||
const gd::EventsCodeGenerator::VariableScope& scope_)
|
||||
: output(output_),
|
||||
codeGenerator(codeGenerator_),
|
||||
context(context_),
|
||||
scope(scope_) {
|
||||
if (scope == gd::EventsCodeGenerator::OBJECT_VARIABLE) {
|
||||
std::cout << "ERROR: Initializing VariableCodeGenerationCallbacks with "
|
||||
"OBJECT_VARIABLE without object.";
|
||||
}
|
||||
}
|
||||
|
||||
VariableCodeGenerationCallbacks::VariableCodeGenerationCallbacks(
|
||||
gd::String& output_,
|
||||
gd::EventsCodeGenerator& codeGenerator_,
|
||||
gd::EventsCodeGenerationContext& context_,
|
||||
const gd::String& object_)
|
||||
: output(output_),
|
||||
codeGenerator(codeGenerator_),
|
||||
context(context_),
|
||||
scope(gd::EventsCodeGenerator::OBJECT_VARIABLE),
|
||||
object(object_) {}
|
||||
|
||||
void VariableCodeGenerationCallbacks::OnRootVariable(gd::String variableName) {
|
||||
output += codeGenerator.GenerateGetVariable(variableName, scope, context, object);
|
||||
}
|
||||
|
||||
void VariableCodeGenerationCallbacks::OnChildVariable(gd::String variableName) {
|
||||
output += codeGenerator.GenerateVariableAccessor(variableName);
|
||||
}
|
||||
|
||||
void VariableCodeGenerationCallbacks::OnChildSubscript(
|
||||
gd::String stringExpression) {
|
||||
gd::String argumentCode = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "string", stringExpression);
|
||||
|
||||
output += codeGenerator.GenerateVariableBracketAccessor(argumentCode);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* GDevelop C++ Platform
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#ifndef VARIABLEPARSERCALLBACKS_H
|
||||
#define VARIABLEPARSERCALLBACKS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/VariableParser.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "EventsCodeGenerator.h"
|
||||
namespace gd {
|
||||
class EventsCodeGenerationContext;
|
||||
} // namespace gd
|
||||
|
||||
// TODO: Replace and remove (ExpressionCodeGenerator)
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Callbacks called to generate the code for getting a variable.
|
||||
*
|
||||
* Usage example:
|
||||
\code
|
||||
VariableCodeGenerationCallbacks callbacks(output, eventsCodeGenerator,
|
||||
context, VariableCodeGenerationCallbacks::LAYOUT_VARIABLE);
|
||||
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
{
|
||||
//Error during parsing the variable name:
|
||||
output = "runtimeContext->GetSceneVariables().GetBadVariable()";
|
||||
}
|
||||
|
||||
//"output" now contains the C++ code to return the variable.
|
||||
\endcode
|
||||
*/
|
||||
class VariableCodeGenerationCallbacks : public gd::VariableParserCallbacks {
|
||||
public:
|
||||
/**
|
||||
* \brief Default constructor for generating code for a layout/global
|
||||
* variable. \param output The string in which the code will be generated.
|
||||
* \param codeGenerator The code generator being used.
|
||||
* \param context The current code generation context.
|
||||
* \param scope The scope of the variable being accessed: LAYOUT_VARIABLE,
|
||||
* PROJECT_VARIABLE.
|
||||
*/
|
||||
VariableCodeGenerationCallbacks(gd::String& output,
|
||||
gd::EventsCodeGenerator& codeGenerator_,
|
||||
gd::EventsCodeGenerationContext& context_,
|
||||
const gd::EventsCodeGenerator::VariableScope& scope_);
|
||||
/**
|
||||
|
||||
* \brief Default constructor for generating code for an object variable.
|
||||
* \param output The string in which the code will be generated.
|
||||
* \param codeGenerator The code generator being used.
|
||||
* \param context The current code generation context.
|
||||
* \param object The name of the object
|
||||
*/
|
||||
VariableCodeGenerationCallbacks(gd::String& output,
|
||||
gd::EventsCodeGenerator& codeGenerator_,
|
||||
gd::EventsCodeGenerationContext& context_,
|
||||
const gd::String& object);
|
||||
|
||||
/**
|
||||
* \brief Called when the first variable has been parsed.
|
||||
* \param variableName The variable name.
|
||||
*/
|
||||
virtual void OnRootVariable(gd::String variableName);
|
||||
|
||||
/**
|
||||
* \brief Called when accessing the child of a structure variable.
|
||||
* \param variableName The child variable name.
|
||||
*/
|
||||
virtual void OnChildVariable(gd::String variableName);
|
||||
|
||||
/**
|
||||
* \brief Called when accessing the child of a structure variable using a
|
||||
* string expression in square brackets. \param variableName The expression
|
||||
* used to access the child variable.
|
||||
*/
|
||||
virtual void OnChildSubscript(gd::String stringExpression);
|
||||
|
||||
private:
|
||||
gd::String& output;
|
||||
gd::EventsCodeGenerator& codeGenerator;
|
||||
gd::EventsCodeGenerationContext& context;
|
||||
gd::EventsCodeGenerator::VariableScope scope;
|
||||
const gd::String object; ///< The object name, when scope == OBJECT_VARIABLE.
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // VARIABLEPARSERCALLBACKS_H
|
||||
#endif
|
@@ -12,6 +12,7 @@
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/Events/InstructionsList.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class EventsList;
|
||||
@@ -90,8 +91,8 @@ class GD_CORE_API BaseEvent {
|
||||
bool HasSubEvents() const;
|
||||
|
||||
/**
|
||||
* Event must be able to return all conditions std::vector they have.
|
||||
* Used to preprocess the conditions.
|
||||
* \brief Return a list of all conditions of the event.
|
||||
* \note Used to preprocess or search in the conditions.
|
||||
*/
|
||||
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors() {
|
||||
std::vector<gd::InstructionsList*> noConditions;
|
||||
@@ -104,8 +105,8 @@ class GD_CORE_API BaseEvent {
|
||||
};
|
||||
|
||||
/**
|
||||
* Event must be able to return all actions std::vector they have.
|
||||
* Used to preprocess the actions.
|
||||
* \brief Return a list of all actions of the event.
|
||||
* \note Used to preprocess or search in the actions.
|
||||
*/
|
||||
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors() {
|
||||
std::vector<gd::InstructionsList*> noActions;
|
||||
@@ -118,15 +119,26 @@ class GD_CORE_API BaseEvent {
|
||||
};
|
||||
|
||||
/**
|
||||
* Event must be able to return all expressions they have.
|
||||
* Used to preprocess the expressions.
|
||||
* \brief Return a list of all strings of the event.
|
||||
* \note Used to preprocess or search in the event strings.
|
||||
*/
|
||||
virtual std::vector<gd::Expression*> GetAllExpressions() {
|
||||
std::vector<gd::Expression*> noExpr;
|
||||
virtual std::vector<gd::String> GetAllSearchableStrings() const {
|
||||
std::vector<gd::String> noSearchableStrings;
|
||||
return noSearchableStrings;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return a list of all expressions of the event, each with their associated metadata.
|
||||
* \note Used to preprocess or search in the expressions of the event.
|
||||
*/
|
||||
virtual std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
GetAllExpressionsWithMetadata() {
|
||||
std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> > noExpr;
|
||||
return noExpr;
|
||||
};
|
||||
virtual std::vector<const gd::Expression*> GetAllExpressions() const {
|
||||
std::vector<const gd::Expression*> noExpr;
|
||||
virtual std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
GetAllExpressionsWithMetadata() const {
|
||||
std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> > noExpr;
|
||||
return noExpr;
|
||||
};
|
||||
|
||||
|
@@ -99,6 +99,23 @@ bool EventsList::Contains(const gd::BaseEvent& eventToSearch,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EventsList::MoveEventToAnotherEventsList(const gd::BaseEvent& eventToMove,
|
||||
gd::EventsList& newEventsList,
|
||||
std::size_t newPosition) {
|
||||
|
||||
for (std::size_t i = 0; i < GetEventsCount(); ++i) {
|
||||
if (events[i].get() == &eventToMove) {
|
||||
std::shared_ptr<BaseEvent> event = events[i];
|
||||
events.erase(events.begin() + i);
|
||||
|
||||
newEventsList.InsertEvent(event, newPosition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EventsList::EventsList(const EventsList& other) { Init(other); }
|
||||
|
||||
EventsList& EventsList::operator=(const EventsList& other) {
|
||||
|
@@ -52,7 +52,8 @@ class GD_CORE_API EventsList {
|
||||
* \brief Insert the specified event to the list.
|
||||
* \note The event passed by parameter is not copied.
|
||||
* \param event The smart pointer to the event that must be inserted into the
|
||||
* list \param position Insertion position. If the position is invalid, the
|
||||
* list
|
||||
* \param position Insertion position. If the position is invalid, the
|
||||
* object is inserted at the end of the objects list.
|
||||
*/
|
||||
void InsertEvent(std::shared_ptr<gd::BaseEvent> event,
|
||||
@@ -142,6 +143,25 @@ class GD_CORE_API EventsList {
|
||||
*/
|
||||
bool Contains(const gd::BaseEvent& eventToSearch,
|
||||
bool recursive = true) const;
|
||||
|
||||
/**
|
||||
* Move the specified event, that must be in the events list, to another
|
||||
* events list *without* invalidating the event (i.e: without
|
||||
* destroying/cloning it) in memory.
|
||||
*
|
||||
* \warning newEventsList is supposed not to be contained inside the event
|
||||
* (you should not try
|
||||
* to move an event inside one of its children/grand children events).
|
||||
*
|
||||
* \param eventToMove The event to be moved
|
||||
* \param newEventsList The new events list
|
||||
* \param newPosition The position in the new events list
|
||||
* \return true if the move was made, false otherwise (for example, if
|
||||
* eventToMove is not found in the list)
|
||||
*/
|
||||
bool MoveEventToAnotherEventsList(const gd::BaseEvent& eventToMove,
|
||||
gd::EventsList& newEventsList,
|
||||
std::size_t newPosition);
|
||||
///@}
|
||||
|
||||
/** \name std::vector API compatibility
|
||||
|
@@ -1,897 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Events/Parsers/ExpressionParser.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#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/Platform.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String ExpressionParser::parserSeparators = " ,+-*/%.<>=&|;()#^![]{}";
|
||||
|
||||
size_t ExpressionParser::GetMinimalParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata>& parametersInfos) {
|
||||
size_t nb = 0;
|
||||
for (std::size_t i = 0; i < parametersInfos.size(); ++i) {
|
||||
if (!parametersInfos[i].optional && !parametersInfos[i].codeOnly) nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
|
||||
size_t ExpressionParser::GetMaximalParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata>& parametersInfos) {
|
||||
size_t nb = 0;
|
||||
for (std::size_t i = 0; i < parametersInfos.size(); ++i) {
|
||||
if (!parametersInfos[i].codeOnly) nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add blank parameters when code-only parameters are expected.
|
||||
* \param Parameters information
|
||||
* \param vector of parameters without code only parameters.
|
||||
*/
|
||||
std::vector<gd::Expression> CompleteParameters(
|
||||
const std::vector<gd::ParameterMetadata>& parametersInfo,
|
||||
const std::vector<gd::Expression>& parameters) {
|
||||
std::vector<gd::Expression> completeParameters = parameters;
|
||||
for (std::size_t i = 0; i < parametersInfo.size();
|
||||
++i) // Code only parameters are not included in expressions parameters.
|
||||
{
|
||||
if (parametersInfo[i].codeOnly) {
|
||||
if (i > completeParameters.size()) {
|
||||
cout << "Bad parameter count in expression.";
|
||||
}
|
||||
|
||||
if (i >= completeParameters.size())
|
||||
completeParameters.push_back(gd::Expression(""));
|
||||
else
|
||||
completeParameters.insert(completeParameters.begin() + i,
|
||||
gd::Expression(""));
|
||||
} else {
|
||||
if (i >= completeParameters.size()) {
|
||||
completeParameters.push_back(gd::Expression(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
return completeParameters;
|
||||
}
|
||||
|
||||
bool ExpressionParser::ValidSyntax(const gd::String& str) {
|
||||
static const gd::String numerics = "0123456789.e";
|
||||
static const gd::String operators = "+/*-%";
|
||||
|
||||
size_t parenthesisLevel = 0;
|
||||
gd::String lastOperator;
|
||||
|
||||
bool parsingNumber = false;
|
||||
bool parsingScientificNotationNumber = false;
|
||||
bool parsingDecimalNumber = false;
|
||||
bool requestNumber = false;
|
||||
gd::String lastNumber;
|
||||
bool numberWasParsedLast = false;
|
||||
|
||||
for (auto it = str.begin(); it != str.end(); ++it) {
|
||||
char32_t currentChar = *it;
|
||||
if (currentChar == U' ' || currentChar == U'\n') {
|
||||
if (requestNumber) {
|
||||
firstErrorStr = _("Number expected");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parsingNumber) {
|
||||
parsingNumber = false;
|
||||
parsingScientificNotationNumber = false;
|
||||
parsingDecimalNumber = false;
|
||||
requestNumber = false;
|
||||
lastNumber.clear();
|
||||
numberWasParsedLast = true;
|
||||
}
|
||||
} else if (numerics.find(currentChar) != gd::String::npos) {
|
||||
requestNumber = false;
|
||||
|
||||
if (currentChar == U'.') {
|
||||
if (!parsingNumber) {
|
||||
firstErrorStr = _("Syntax error");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parsingDecimalNumber) {
|
||||
firstErrorStr = _("Syntax error in a number.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
parsingDecimalNumber = true;
|
||||
}
|
||||
|
||||
if (currentChar == U'e') {
|
||||
if (parsingScientificNotationNumber) {
|
||||
firstErrorStr = _("Syntax error in a number.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
parsingScientificNotationNumber = true;
|
||||
requestNumber = true;
|
||||
}
|
||||
|
||||
if (numberWasParsedLast) {
|
||||
firstErrorStr = _("Operator missing before a number");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
parsingNumber = true;
|
||||
lastNumber += currentChar;
|
||||
} else if (currentChar == U')') {
|
||||
if (requestNumber) {
|
||||
firstErrorStr = _("Number expected");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parsingNumber) {
|
||||
parsingNumber = false;
|
||||
parsingScientificNotationNumber = false;
|
||||
parsingDecimalNumber = false;
|
||||
lastNumber.clear();
|
||||
numberWasParsedLast = true;
|
||||
}
|
||||
|
||||
if (!numberWasParsedLast) {
|
||||
firstErrorStr = _("Superfluous operator before a paranthesis");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parenthesisLevel > 0)
|
||||
parenthesisLevel--;
|
||||
else {
|
||||
firstErrorStr = _("Bad closing paranthesis");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto previousIt = it;
|
||||
--previousIt;
|
||||
if (*previousIt == U'(') {
|
||||
firstErrorStr = _("Empty paranthesis");
|
||||
|
||||
return false;
|
||||
}
|
||||
} else if (currentChar == U'(') {
|
||||
if (requestNumber) {
|
||||
firstErrorStr = _("Number expected");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parsingNumber) {
|
||||
parsingNumber = false;
|
||||
parsingScientificNotationNumber = false;
|
||||
parsingDecimalNumber = false;
|
||||
lastNumber.clear();
|
||||
numberWasParsedLast = true;
|
||||
}
|
||||
|
||||
if (numberWasParsedLast) {
|
||||
firstErrorStr = _("Operator missing before a paranthesis");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
parenthesisLevel++;
|
||||
numberWasParsedLast = false;
|
||||
} else if (operators.find(currentChar) != gd::String::npos) {
|
||||
if (currentChar == U'-' && parsingNumber &&
|
||||
parsingScientificNotationNumber) {
|
||||
lastNumber += currentChar;
|
||||
requestNumber = true;
|
||||
} else {
|
||||
if (requestNumber) {
|
||||
firstErrorStr = _("Number expected");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parsingNumber) {
|
||||
parsingNumber = false;
|
||||
parsingScientificNotationNumber = false;
|
||||
parsingDecimalNumber = false;
|
||||
lastNumber.clear();
|
||||
numberWasParsedLast = true;
|
||||
}
|
||||
|
||||
if (currentChar != U'-' && currentChar != U'+' &&
|
||||
!numberWasParsedLast) {
|
||||
firstErrorStr = _("Operators without any number between them");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
numberWasParsedLast = false;
|
||||
}
|
||||
} else {
|
||||
firstErrorStr = _("Syntax error");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsingNumber) {
|
||||
parsingNumber = false;
|
||||
parsingScientificNotationNumber = false;
|
||||
parsingDecimalNumber = false;
|
||||
lastNumber.clear();
|
||||
numberWasParsedLast = true;
|
||||
} else if (requestNumber) {
|
||||
firstErrorStr = _("Number expected");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parenthesisLevel != 0) {
|
||||
firstErrorStr = _("Paranthesis mismatch");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!numberWasParsedLast) {
|
||||
firstErrorStr = _("Alone operator at the end of the expression");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpressionParser::ParseMathExpression(const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::ParserCallbacks& callbacks) {
|
||||
callbacks.SetReturnType("expression");
|
||||
gd::String expression = expressionPlainString;
|
||||
|
||||
size_t parsePosition = 0;
|
||||
|
||||
size_t firstPointPos = expression.find(".");
|
||||
size_t firstParPos = expression.find("(");
|
||||
|
||||
gd::String expressionWithoutFunctions;
|
||||
gd::String nonFunctionToken;
|
||||
size_t nonFunctionTokenStartPos = gd::String::npos;
|
||||
|
||||
while (firstPointPos != string::npos || firstParPos != string::npos) {
|
||||
// Identify name
|
||||
size_t nameEnd = firstPointPos < firstParPos ? firstPointPos : firstParPos;
|
||||
size_t nameStart = expression.find_last_of(parserSeparators, nameEnd - 1);
|
||||
nameStart++;
|
||||
|
||||
gd::String nameBefore = expression.substr(nameStart, nameEnd - nameStart);
|
||||
gd::String objectName = nameBefore.FindAndReplace("~", " ");
|
||||
|
||||
// Identify function name
|
||||
gd::String functionName = nameBefore;
|
||||
size_t functionNameEnd = nameEnd;
|
||||
vector<gd::Expression> parameters;
|
||||
|
||||
bool nameIsFunction = firstPointPos > firstParPos;
|
||||
if (!nameIsFunction) {
|
||||
parameters.push_back(gd::Expression(objectName));
|
||||
functionNameEnd = expression.find_first_of(" (", nameEnd);
|
||||
if (nameEnd + 1 < expression.length())
|
||||
functionName =
|
||||
expression.substr(nameEnd + 1, functionNameEnd - (nameEnd + 1));
|
||||
if (functionNameEnd == string::npos) {
|
||||
functionName = "";
|
||||
functionNameEnd = expression.length() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we're going to identify the expression
|
||||
gd::ExpressionMetadata instructionInfos;
|
||||
|
||||
if (functionName.substr(0, functionName.length() - 1)
|
||||
.find_first_of(parserSeparators) == string::npos) {
|
||||
bool functionFound = false;
|
||||
bool staticFunctionFound = false;
|
||||
bool objectFunctionFound = false;
|
||||
bool behaviorFunctionFound = false;
|
||||
|
||||
// First try to bind to a static expression
|
||||
if (nameIsFunction &&
|
||||
MetadataProvider::HasExpression(platform, functionName)) {
|
||||
functionFound = true;
|
||||
staticFunctionFound = true;
|
||||
instructionInfos =
|
||||
MetadataProvider::GetExpressionMetadata(platform, functionName);
|
||||
}
|
||||
// Then search in object expression
|
||||
else if (!nameIsFunction &&
|
||||
MetadataProvider::HasObjectExpression(
|
||||
platform,
|
||||
gd::GetTypeOfObject(project, layout, objectName),
|
||||
functionName)) {
|
||||
functionFound = true;
|
||||
objectFunctionFound = true;
|
||||
instructionInfos = MetadataProvider::GetObjectExpressionMetadata(
|
||||
platform,
|
||||
gd::GetTypeOfObject(project, layout, objectName),
|
||||
functionName);
|
||||
}
|
||||
// And in behaviors expressions
|
||||
else if (!nameIsFunction) {
|
||||
size_t firstDoublePoints = functionName.find("::");
|
||||
if (firstDoublePoints != string::npos) {
|
||||
gd::String autoName = functionName.substr(0, firstDoublePoints);
|
||||
if (firstDoublePoints + 2 < functionName.length())
|
||||
functionName = functionName.substr(firstDoublePoints + 2,
|
||||
functionName.length());
|
||||
else
|
||||
functionName = "";
|
||||
|
||||
if (MetadataProvider::HasBehaviorExpression(
|
||||
platform,
|
||||
gd::GetTypeOfBehavior(project, layout, autoName),
|
||||
functionName)) {
|
||||
parameters.push_back(gd::Expression(autoName));
|
||||
functionFound = true;
|
||||
behaviorFunctionFound = true;
|
||||
|
||||
instructionInfos = MetadataProvider::GetBehaviorExpressionMetadata(
|
||||
platform,
|
||||
gd::GetTypeOfBehavior(project, layout, autoName),
|
||||
functionName);
|
||||
|
||||
// Verify that object has behavior.
|
||||
vector<gd::String> behaviors =
|
||||
gd::GetBehaviorsOfObject(project, layout, objectName);
|
||||
if (find(behaviors.begin(), behaviors.end(), autoName) ==
|
||||
behaviors.end()) {
|
||||
cout << "Bad behavior requested" << endl;
|
||||
functionFound = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (functionFound) // Add the function
|
||||
{
|
||||
// Identify parameters
|
||||
size_t parametersEnd = expression.find_first_of("(", functionNameEnd);
|
||||
gd::String currentParameterStr;
|
||||
char32_t previousChar = '(';
|
||||
bool takeSymbolsInAccount = true;
|
||||
if (parametersEnd != string::npos) {
|
||||
size_t level = 0;
|
||||
parametersEnd++;
|
||||
|
||||
while (parametersEnd < expression.length() &&
|
||||
!(expression[parametersEnd] == ')' && level == 0)) {
|
||||
// Be sure we are not in quotes
|
||||
if (expression[parametersEnd] == U'\"' && previousChar != U'\\')
|
||||
takeSymbolsInAccount = !takeSymbolsInAccount;
|
||||
|
||||
// So as to be sure paranthesis don't belong to a parameter
|
||||
if (expression[parametersEnd] == U'(' && takeSymbolsInAccount)
|
||||
level++;
|
||||
if (expression[parametersEnd] == U')' && takeSymbolsInAccount)
|
||||
level--;
|
||||
|
||||
// Add the character to the current parameter or terminate the
|
||||
// latter
|
||||
if ((expression[parametersEnd] == U',' && level == 0) &&
|
||||
takeSymbolsInAccount) {
|
||||
parameters.push_back(currentParameterStr);
|
||||
currentParameterStr.clear();
|
||||
} else
|
||||
currentParameterStr += expression[parametersEnd];
|
||||
|
||||
previousChar = expression[parametersEnd];
|
||||
parametersEnd++;
|
||||
}
|
||||
if (currentParameterStr.find_first_not_of(" ") !=
|
||||
string::npos) // Add last parameter if needed
|
||||
{
|
||||
parameters.push_back(currentParameterStr);
|
||||
}
|
||||
|
||||
// Testing function call is properly closed
|
||||
if (parametersEnd == expression.length() ||
|
||||
expression[parametersEnd] != U')') {
|
||||
firstErrorStr = _("Paranthesis not closed");
|
||||
firstErrorPos = parametersEnd - 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Testing the number of parameters
|
||||
if (parameters.size() >
|
||||
GetMaximalParametersNumber(instructionInfos.parameters) ||
|
||||
parameters.size() <
|
||||
GetMinimalParametersNumber(instructionInfos.parameters)) {
|
||||
firstErrorPos = functionNameEnd;
|
||||
firstErrorStr = _("Incorrect number of parameters");
|
||||
firstErrorStr += " ";
|
||||
firstErrorStr += _("Expected (maximum) :");
|
||||
firstErrorStr += gd::String::From(
|
||||
GetMaximalParametersNumber(instructionInfos.parameters));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Preparing parameters
|
||||
parameters =
|
||||
CompleteParameters(instructionInfos.parameters, parameters);
|
||||
for (std::size_t i = 0; i < instructionInfos.parameters.size(); ++i) {
|
||||
if (!PrepareParameter(platform,
|
||||
project,
|
||||
layout,
|
||||
callbacks,
|
||||
parameters[i],
|
||||
instructionInfos.parameters[i],
|
||||
functionNameEnd))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
firstErrorPos = functionNameEnd;
|
||||
firstErrorStr = _("Parameters' parenthesis missing");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
callbacks.OnConstantToken(
|
||||
nonFunctionToken +
|
||||
expression.substr(parsePosition, nameStart - parsePosition));
|
||||
expressionWithoutFunctions +=
|
||||
expression.substr(parsePosition, nameStart - parsePosition);
|
||||
nonFunctionToken.clear();
|
||||
nonFunctionTokenStartPos = gd::String::npos;
|
||||
|
||||
if (objectFunctionFound)
|
||||
callbacks.OnObjectFunction(
|
||||
functionName, parameters, instructionInfos);
|
||||
else if (behaviorFunctionFound)
|
||||
callbacks.OnObjectBehaviorFunction(
|
||||
functionName, parameters, instructionInfos);
|
||||
else if (staticFunctionFound)
|
||||
callbacks.OnStaticFunction(
|
||||
functionName, parameters, instructionInfos);
|
||||
|
||||
if (objectFunctionFound || behaviorFunctionFound || staticFunctionFound)
|
||||
expressionWithoutFunctions += "0";
|
||||
|
||||
parsePosition = parametersEnd + 1;
|
||||
firstPointPos = expression.find(".", parametersEnd + 1);
|
||||
firstParPos = expression.find("(", parametersEnd + 1);
|
||||
} else // Math function or math constant : Pass it.
|
||||
{
|
||||
nonFunctionToken += expression.substr(
|
||||
parsePosition, functionNameEnd + 1 - parsePosition);
|
||||
expressionWithoutFunctions += expression.substr(
|
||||
parsePosition, functionNameEnd + 1 - parsePosition);
|
||||
nonFunctionTokenStartPos = (nonFunctionTokenStartPos != gd::String::npos
|
||||
? nonFunctionTokenStartPos
|
||||
: parsePosition);
|
||||
parsePosition = functionNameEnd + 1;
|
||||
firstPointPos = expression.find(".", functionNameEnd + 1);
|
||||
firstParPos = expression.find("(", functionNameEnd + 1);
|
||||
}
|
||||
} else // Not a function call : Pass it
|
||||
{
|
||||
nonFunctionToken +=
|
||||
expression.substr(parsePosition, nameEnd + 1 - parsePosition);
|
||||
expressionWithoutFunctions +=
|
||||
expression.substr(parsePosition, nameEnd + 1 - parsePosition);
|
||||
nonFunctionTokenStartPos = (nonFunctionTokenStartPos != gd::String::npos
|
||||
? nonFunctionTokenStartPos
|
||||
: parsePosition);
|
||||
parsePosition = nameEnd + 1;
|
||||
firstPointPos = expression.find(".", nameEnd + 1);
|
||||
firstParPos = expression.find("(", nameEnd + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (parsePosition < expression.length() || !nonFunctionToken.empty())
|
||||
callbacks.OnConstantToken(
|
||||
nonFunctionToken +
|
||||
expression.substr(parsePosition, expression.length()));
|
||||
|
||||
expressionWithoutFunctions +=
|
||||
expression.substr(parsePosition, expression.length());
|
||||
|
||||
return ValidSyntax(expressionWithoutFunctions);
|
||||
}
|
||||
|
||||
bool ExpressionParser::ParseStringExpression(const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::ParserCallbacks& callbacks) {
|
||||
callbacks.SetReturnType("string");
|
||||
gd::String expression = expressionPlainString;
|
||||
|
||||
size_t parsePosition = 0;
|
||||
|
||||
// Searching for first token.
|
||||
size_t firstPointPos = expression.find(".");
|
||||
size_t firstParPos = expression.find("(");
|
||||
size_t firstQuotePos = expression.find("\"");
|
||||
|
||||
if (firstPointPos == string::npos && firstParPos == string::npos &&
|
||||
firstQuotePos == string::npos) {
|
||||
firstErrorPos = 0;
|
||||
firstErrorStr =
|
||||
_("The expression is invalid or empty. Enter a text ( surrounded by "
|
||||
"quotes ) or a function.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
while (firstPointPos != string::npos || firstParPos != string::npos ||
|
||||
firstQuotePos != string::npos) {
|
||||
if (firstQuotePos < firstPointPos &&
|
||||
firstQuotePos < firstParPos) // Adding a constant text
|
||||
{
|
||||
callbacks.OnConstantToken(
|
||||
expression.substr(parsePosition, firstQuotePos - parsePosition));
|
||||
|
||||
// Finding start and end of quotes
|
||||
size_t finalQuotePosition = expression.find("\"", firstQuotePos + 1);
|
||||
while (finalQuotePosition ==
|
||||
expression.find("\\\"", finalQuotePosition - 1) + 1)
|
||||
finalQuotePosition = expression.find("\"", finalQuotePosition + 1);
|
||||
|
||||
if (finalQuotePosition == string::npos) {
|
||||
firstErrorPos = firstQuotePos;
|
||||
firstErrorStr = _("Quotes not closed.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generating final text, by replacing \" by quotes
|
||||
gd::String finalText = expression.substr(
|
||||
firstQuotePos + 1, finalQuotePosition - (firstQuotePos + 1));
|
||||
|
||||
size_t foundPos = finalText.find("\\\"");
|
||||
while (foundPos != string::npos) {
|
||||
if (foundPos != string::npos) finalText.replace(foundPos, 2, "\"");
|
||||
foundPos = finalText.find("\\\"", foundPos);
|
||||
}
|
||||
|
||||
// Adding constant text instruction
|
||||
//(Function without name is considered as a constant text)
|
||||
vector<gd::Expression> parameters;
|
||||
parameters.push_back(finalText);
|
||||
gd::ExpressionMetadata noParametersInfo;
|
||||
|
||||
callbacks.OnStaticFunction("", parameters, noParametersInfo);
|
||||
|
||||
parsePosition = finalQuotePosition + 1;
|
||||
} else // Adding a function
|
||||
{
|
||||
// Identify name
|
||||
size_t nameEnd =
|
||||
firstPointPos < firstParPos ? firstPointPos : firstParPos;
|
||||
size_t nameStart = expression.find_last_of(parserSeparators, nameEnd - 1);
|
||||
nameStart++;
|
||||
|
||||
callbacks.OnConstantToken(
|
||||
expression.substr(parsePosition, nameStart - parsePosition));
|
||||
|
||||
gd::String nameBefore = expression.substr(nameStart, nameEnd - nameStart);
|
||||
gd::String objectName = nameBefore.FindAndReplace("~", " ");
|
||||
|
||||
// Identify function name
|
||||
gd::String functionName = nameBefore;
|
||||
size_t functionNameEnd = nameEnd;
|
||||
vector<gd::Expression> parameters;
|
||||
|
||||
bool nameIsFunction = firstPointPos > firstParPos;
|
||||
if (!nameIsFunction) {
|
||||
parameters.push_back(gd::Expression(objectName));
|
||||
functionNameEnd = expression.find_first_of("( ", nameEnd);
|
||||
if (nameEnd + 1 < expression.length())
|
||||
functionName =
|
||||
expression.substr(nameEnd + 1, functionNameEnd - (nameEnd + 1));
|
||||
}
|
||||
|
||||
// Identify parameters
|
||||
size_t parametersEnd = expression.find_first_of("(", functionNameEnd) + 1;
|
||||
char32_t previousChar = U'(';
|
||||
bool takeSymbolsInAccount = true;
|
||||
size_t level = 0;
|
||||
gd::String currentParameterStr;
|
||||
while (parametersEnd < expression.length() &&
|
||||
!(expression[parametersEnd] == U')' && level == 0)) {
|
||||
// Be sure we are not in quotes
|
||||
if (expression[parametersEnd] == U'\"' && previousChar != U'\\')
|
||||
takeSymbolsInAccount = !takeSymbolsInAccount;
|
||||
|
||||
// So as to be sure paranthesis don't belong to a parameter
|
||||
if (expression[parametersEnd] == U'(' && takeSymbolsInAccount) level++;
|
||||
if (expression[parametersEnd] == U')' && takeSymbolsInAccount) level--;
|
||||
|
||||
// Add the character to the current parameter or terminate the latter
|
||||
if ((expression[parametersEnd] == ',' && level == 0) &&
|
||||
takeSymbolsInAccount) {
|
||||
gd::Expression currentParameter(currentParameterStr);
|
||||
parameters.push_back(currentParameter);
|
||||
|
||||
currentParameterStr.clear();
|
||||
} else
|
||||
currentParameterStr += expression[parametersEnd];
|
||||
|
||||
previousChar = expression[parametersEnd];
|
||||
parametersEnd++;
|
||||
}
|
||||
|
||||
if (parametersEnd == expression.length() ||
|
||||
expression[parametersEnd] != U')') {
|
||||
firstErrorPos = parametersEnd - 1;
|
||||
firstErrorStr = _("Paranthesis not closed");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentParameterStr.find_first_not_of(" ") !=
|
||||
string::npos) // Add last parameter if needed
|
||||
{
|
||||
gd::Expression lastParameter(currentParameterStr);
|
||||
parameters.push_back(lastParameter);
|
||||
}
|
||||
|
||||
bool functionFound = false;
|
||||
|
||||
// First try to bind to a static str expression
|
||||
if (nameIsFunction &&
|
||||
MetadataProvider::HasStrExpression(platform, functionName)) {
|
||||
functionFound = true;
|
||||
const gd::ExpressionMetadata& expressionInfo =
|
||||
MetadataProvider::GetStrExpressionMetadata(platform, functionName);
|
||||
|
||||
// Testing the number of parameters
|
||||
if (parameters.size() >
|
||||
GetMaximalParametersNumber(expressionInfo.parameters) ||
|
||||
parameters.size() <
|
||||
GetMinimalParametersNumber(expressionInfo.parameters)) {
|
||||
firstErrorPos = functionNameEnd;
|
||||
firstErrorStr = _("Incorrect number of parameters");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Preparing parameters
|
||||
parameters = CompleteParameters(expressionInfo.parameters, parameters);
|
||||
for (std::size_t i = 0;
|
||||
i < parameters.size() && i < expressionInfo.parameters.size();
|
||||
++i) {
|
||||
if (!PrepareParameter(platform,
|
||||
project,
|
||||
layout,
|
||||
callbacks,
|
||||
parameters[i],
|
||||
expressionInfo.parameters[i],
|
||||
functionNameEnd))
|
||||
return false;
|
||||
}
|
||||
|
||||
callbacks.OnStaticFunction(functionName, parameters, expressionInfo);
|
||||
}
|
||||
// Then an object member expression
|
||||
else if (!nameIsFunction &&
|
||||
MetadataProvider::HasObjectStrExpression(
|
||||
platform,
|
||||
gd::GetTypeOfObject(project, layout, objectName),
|
||||
functionName)) {
|
||||
functionFound = true;
|
||||
const gd::ExpressionMetadata& expressionInfo =
|
||||
MetadataProvider::GetObjectStrExpressionMetadata(
|
||||
platform,
|
||||
gd::GetTypeOfObject(project, layout, nameBefore),
|
||||
functionName);
|
||||
|
||||
// Testing the number of parameters
|
||||
if (parameters.size() >
|
||||
GetMaximalParametersNumber(expressionInfo.parameters) ||
|
||||
parameters.size() <
|
||||
GetMinimalParametersNumber(expressionInfo.parameters)) {
|
||||
firstErrorPos = functionNameEnd;
|
||||
firstErrorStr = _("Incorrect number of parameters");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Preparing parameters
|
||||
parameters = CompleteParameters(expressionInfo.parameters, parameters);
|
||||
for (std::size_t i = 0;
|
||||
i < parameters.size() && i < expressionInfo.parameters.size();
|
||||
++i) {
|
||||
if (!PrepareParameter(platform,
|
||||
project,
|
||||
layout,
|
||||
callbacks,
|
||||
parameters[i],
|
||||
expressionInfo.parameters[i],
|
||||
functionNameEnd))
|
||||
return false;
|
||||
}
|
||||
|
||||
callbacks.OnObjectFunction(functionName, parameters, expressionInfo);
|
||||
}
|
||||
// And search behaviors expressions
|
||||
else {
|
||||
size_t firstDoublePoints = functionName.find("::");
|
||||
if (firstDoublePoints != string::npos) {
|
||||
gd::String autoName = functionName.substr(0, firstDoublePoints);
|
||||
if (firstDoublePoints + 2 < functionName.length())
|
||||
functionName = functionName.substr(firstDoublePoints + 2,
|
||||
functionName.length());
|
||||
else
|
||||
functionName = "";
|
||||
|
||||
if (MetadataProvider::HasBehaviorStrExpression(
|
||||
platform,
|
||||
gd::GetTypeOfBehavior(project, layout, autoName),
|
||||
functionName)) {
|
||||
parameters.push_back(gd::Expression(autoName));
|
||||
functionFound = true;
|
||||
|
||||
const gd::ExpressionMetadata& expressionInfo =
|
||||
MetadataProvider::GetBehaviorStrExpressionMetadata(
|
||||
platform,
|
||||
gd::GetTypeOfBehavior(project, layout, autoName),
|
||||
functionName);
|
||||
|
||||
// Verify that object has behavior.
|
||||
vector<gd::String> behaviors =
|
||||
gd::GetBehaviorsOfObject(project, layout, objectName);
|
||||
if (find(behaviors.begin(), behaviors.end(), autoName) ==
|
||||
behaviors.end()) {
|
||||
cout << "Bad behavior requested" << endl;
|
||||
functionFound = false;
|
||||
} else {
|
||||
// Testing the number of parameters
|
||||
if (parameters.size() >
|
||||
GetMaximalParametersNumber(expressionInfo.parameters) ||
|
||||
parameters.size() <
|
||||
GetMinimalParametersNumber(expressionInfo.parameters)) {
|
||||
firstErrorPos = functionNameEnd;
|
||||
firstErrorStr = _("Incorrect number of parameters");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Preparing parameters
|
||||
parameters =
|
||||
CompleteParameters(expressionInfo.parameters, parameters);
|
||||
for (std::size_t i = 0; i < parameters.size() &&
|
||||
i < expressionInfo.parameters.size();
|
||||
++i) {
|
||||
if (!PrepareParameter(platform,
|
||||
project,
|
||||
layout,
|
||||
callbacks,
|
||||
parameters[i],
|
||||
expressionInfo.parameters[i],
|
||||
functionNameEnd))
|
||||
return false;
|
||||
}
|
||||
|
||||
callbacks.OnObjectBehaviorFunction(
|
||||
functionName, parameters, expressionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note : _No_ support for implicit conversion from math result to string
|
||||
|
||||
if (!functionFound) // Function was not found
|
||||
{
|
||||
firstErrorPos = nameStart;
|
||||
firstErrorStr = _("Function not recognized.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
parsePosition = parametersEnd + 1;
|
||||
}
|
||||
|
||||
// Searching for next token
|
||||
size_t firstPlusPos = expression.find("+", parsePosition);
|
||||
firstPointPos = expression.find(".", parsePosition);
|
||||
firstParPos = expression.find("(", parsePosition);
|
||||
firstQuotePos = expression.find("\"", parsePosition);
|
||||
|
||||
// Checking for a + between token
|
||||
if ((firstPointPos != string::npos || firstParPos != string::npos ||
|
||||
firstQuotePos != string::npos)) {
|
||||
size_t nextTokenPos = firstPointPos;
|
||||
if (firstParPos < nextTokenPos) nextTokenPos = firstParPos;
|
||||
if (firstQuotePos < nextTokenPos) nextTokenPos = firstQuotePos;
|
||||
|
||||
if (nextTokenPos < firstPlusPos) {
|
||||
firstErrorPos = nextTokenPos;
|
||||
firstErrorStr = _("Symbol missing between two +.");
|
||||
|
||||
return false;
|
||||
} else if (expression.find("+", firstPlusPos + 1) < nextTokenPos) {
|
||||
firstErrorPos = firstPlusPos;
|
||||
firstErrorStr = _("Symbol missing between two +.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expression.substr(parsePosition, expression.length())
|
||||
.find_first_not_of(" \n") != gd::String::npos) {
|
||||
firstErrorPos = parsePosition;
|
||||
firstErrorStr = _("Bad symbol at the end of the expression.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpressionParser::PrepareParameter(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
ParserCallbacks& callbacks,
|
||||
gd::Expression& parameter,
|
||||
const gd::ParameterMetadata& parametersInfo,
|
||||
const size_t positionInExpression) {
|
||||
if (ParameterMetadata::IsExpression("number", parametersInfo.type)) {
|
||||
if (parametersInfo.optional && parameter.GetPlainString().empty())
|
||||
parameter = parametersInfo.GetDefaultValue().empty()
|
||||
? gd::Expression("0")
|
||||
: gd::Expression(parametersInfo.GetDefaultValue());
|
||||
|
||||
if (!callbacks.OnSubMathExpression(platform, project, layout, parameter)) {
|
||||
firstErrorStr = callbacks.firstErrorStr;
|
||||
firstErrorPos = callbacks.firstErrorPos + positionInExpression;
|
||||
|
||||
return false;
|
||||
}
|
||||
} else if (ParameterMetadata::IsExpression("string", parametersInfo.type)) {
|
||||
if (parametersInfo.optional && parameter.GetPlainString().empty())
|
||||
parameter = parametersInfo.GetDefaultValue().empty()
|
||||
? gd::Expression("\"\"")
|
||||
: gd::Expression(parametersInfo.GetDefaultValue());
|
||||
|
||||
if (!callbacks.OnSubTextExpression(platform, project, layout, parameter)) {
|
||||
firstErrorStr = callbacks.firstErrorStr;
|
||||
firstErrorPos = callbacks.firstErrorPos + positionInExpression;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ExpressionParser::ExpressionParser(const gd::String& expressionPlainString_)
|
||||
: expressionPlainString(expressionPlainString_) {}
|
||||
|
||||
} // namespace gd
|
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EXPRESSIONPARSER_H
|
||||
#define GDCORE_EXPRESSIONPARSER_H
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ParserCallbacks;
|
||||
class ObjectsContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/** \brief Parse an expression
|
||||
*
|
||||
* Parse an expression, calling callbacks when a token is reached
|
||||
* \see gd::ParserCallbacks
|
||||
*/
|
||||
class GD_CORE_API ExpressionParser {
|
||||
public:
|
||||
ExpressionParser(const gd::String &expressionPlainString_);
|
||||
virtual ~ExpressionParser(){};
|
||||
|
||||
/**
|
||||
* \brief Parse the expression, calling each functor when necessary
|
||||
* \return True if expression was correctly parsed.
|
||||
*/
|
||||
bool ParseMathExpression(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &project,
|
||||
const gd::ObjectsContainer &layout,
|
||||
gd::ParserCallbacks &callbacks);
|
||||
|
||||
/**
|
||||
* \brief Parse the expression, calling each functor when necessary
|
||||
* \return True if expression was correctly parsed.
|
||||
*/
|
||||
bool ParseStringExpression(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &project,
|
||||
const gd::ObjectsContainer &layout,
|
||||
gd::ParserCallbacks &callbacks);
|
||||
|
||||
/**
|
||||
* \brief Return the description of the error that was found
|
||||
*/
|
||||
const gd::String &GetFirstError() { return firstErrorStr; }
|
||||
|
||||
/**
|
||||
* \brief Return the position of the error that was found
|
||||
* \return The position, or gd::String::npos if no error is found
|
||||
*/
|
||||
size_t GetFirstErrorPosition() { return firstErrorPos; }
|
||||
|
||||
private:
|
||||
gd::String firstErrorStr;
|
||||
size_t firstErrorPos;
|
||||
|
||||
/**
|
||||
* Tool function to add a parameter
|
||||
*/
|
||||
bool AddParameterToList(const gd::ObjectsContainer &project,
|
||||
const gd::ObjectsContainer &layout,
|
||||
ParserCallbacks &,
|
||||
std::vector<gd::Expression> ¶meters,
|
||||
gd::String parameterStr,
|
||||
std::vector<gd::ParameterMetadata> parametersInfos,
|
||||
const size_t positionInExpression);
|
||||
|
||||
/**
|
||||
* Tool function to prepare a parameter
|
||||
*/
|
||||
bool PrepareParameter(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &project,
|
||||
const gd::ObjectsContainer &layout,
|
||||
ParserCallbacks &,
|
||||
gd::Expression ¶meter,
|
||||
const gd::ParameterMetadata ¶metersInfo,
|
||||
const size_t positionInExpression);
|
||||
|
||||
/**
|
||||
* Return the minimal number of parameters which can be used when calling an
|
||||
* expression ( i.e. ParametersCount-OptionalParameters-CodeOnlyParameters )
|
||||
*/
|
||||
size_t GetMinimalParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata> ¶metersInfos);
|
||||
|
||||
/**
|
||||
* Return the maximal number of parameters which can be used when calling an
|
||||
* expression ( i.e. ParametersCount-CodeOnlyParameters )
|
||||
*/
|
||||
size_t GetMaximalParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata> ¶metersInfos);
|
||||
|
||||
bool ValidSyntax(const gd::String &str);
|
||||
|
||||
gd::String expressionPlainString;
|
||||
static gd::String parserSeparators;
|
||||
};
|
||||
|
||||
/** \brief Callbacks called by parser during parsing
|
||||
*
|
||||
* Parser will call the appropriate functions during parsing, allowing to do
|
||||
* special works. \see gd::ExpressionParser
|
||||
*/
|
||||
class GD_CORE_API ParserCallbacks {
|
||||
friend class ExpressionParser;
|
||||
|
||||
public:
|
||||
ParserCallbacks() : returnType("expression"){};
|
||||
virtual ~ParserCallbacks(){};
|
||||
|
||||
/**
|
||||
* \brief Get the type of the expression for which callbacks are used:
|
||||
* "expression" or "string".
|
||||
*/
|
||||
const gd::String &GetReturnType() { return returnType; }
|
||||
|
||||
virtual void OnConstantToken(gd::String text) = 0;
|
||||
|
||||
virtual void OnStaticFunction(
|
||||
gd::String functionName,
|
||||
const std::vector<gd::Expression> ¶meters,
|
||||
const gd::ExpressionMetadata &expressionInfo) = 0;
|
||||
|
||||
virtual void OnObjectFunction(
|
||||
gd::String functionName,
|
||||
const std::vector<gd::Expression> ¶meters,
|
||||
const gd::ExpressionMetadata &expressionInfo) = 0;
|
||||
|
||||
virtual void OnObjectBehaviorFunction(
|
||||
gd::String functionName,
|
||||
const std::vector<gd::Expression> ¶meters,
|
||||
const gd::ExpressionMetadata &expressionInfo) = 0;
|
||||
|
||||
virtual bool OnSubMathExpression(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &project,
|
||||
const gd::ObjectsContainer &layout,
|
||||
gd::Expression &expression) = 0;
|
||||
virtual bool OnSubTextExpression(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &project,
|
||||
const gd::ObjectsContainer &layout,
|
||||
gd::Expression &expression) = 0;
|
||||
|
||||
/**
|
||||
* \brief Return the description of the error that was found
|
||||
*/
|
||||
const gd::String &GetFirstError() { return firstErrorStr; }
|
||||
|
||||
/**
|
||||
* \brief Return the position of the error that was found
|
||||
* \return The position, or gd::String::npos if no error is found
|
||||
*/
|
||||
size_t GetFirstErrorPosition() { return firstErrorPos; }
|
||||
|
||||
protected:
|
||||
gd::String firstErrorStr;
|
||||
size_t firstErrorPos;
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Set the return type of the expression: Done by ExpressionParser
|
||||
* according to which Parse* method is called. \see gd::ExpressionParser
|
||||
*/
|
||||
void SetReturnType(gd::String type) { returnType = type; }
|
||||
|
||||
gd::String returnType; // The type of the expression ("expression" (default),
|
||||
// "string"...)
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDEXPRESSIONPARSER_H
|
@@ -22,15 +22,6 @@ using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String ExpressionParser2::NUMBER_FIRST_CHAR = ".0123456789";
|
||||
gd::String ExpressionParser2::DOT = ".";
|
||||
gd::String ExpressionParser2::PARAMETERS_SEPARATOR = ",";
|
||||
gd::String ExpressionParser2::QUOTE = "\"";
|
||||
gd::String ExpressionParser2::BRACKETS = "()[]{}";
|
||||
gd::String ExpressionParser2::EXPRESSION_OPERATORS = "+-<>?^=\\:!";
|
||||
gd::String ExpressionParser2::TERM_OPERATORS = "/*";
|
||||
gd::String ExpressionParser2::UNARY_OPERATORS = "+-";
|
||||
gd::String ExpressionParser2::WHITESPACES = " \n\r";
|
||||
gd::String ExpressionParser2::NAMESPACE_SEPARATOR = "::";
|
||||
|
||||
ExpressionParser2::ExpressionParser2(
|
||||
@@ -76,7 +67,7 @@ size_t GetMaximumParametersNumber(
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ExpressionParserDiagnostic> ExpressionParser2::ValidateFunction(
|
||||
const gd::FunctionNode& function, size_t functionStartPosition) {
|
||||
const gd::FunctionCallNode& function, size_t functionStartPosition) {
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(
|
||||
function.expressionMetadata)) {
|
||||
return gd::make_unique<ExpressionParserError>(
|
||||
@@ -118,11 +109,13 @@ std::unique_ptr<ExpressionParserDiagnostic> ExpressionParser2::ValidateFunction(
|
||||
}
|
||||
|
||||
std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
|
||||
SkipWhitespace();
|
||||
if (!IsAnyChar("\"")) {
|
||||
size_t textStartPosition = GetCurrentPosition();
|
||||
SkipAllWhitespaces();
|
||||
if (!CheckIfChar(IsQuote)) {
|
||||
auto text = gd::make_unique<TextNode>("");
|
||||
text->diagnostic =
|
||||
RaiseSyntaxError(_("A text must start with a double quote (\")."));
|
||||
text->location = ExpressionParserLocation(textStartPosition, GetCurrentPosition());
|
||||
return text;
|
||||
}
|
||||
SkipChar();
|
||||
@@ -157,6 +150,7 @@ std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
|
||||
}
|
||||
|
||||
auto text = gd::make_unique<TextNode>(parsedText);
|
||||
text->location = ExpressionParserLocation(textStartPosition, GetCurrentPosition());
|
||||
if (!textParsingHasEnded) {
|
||||
text->diagnostic =
|
||||
RaiseSyntaxError(_("A text must end with a double quote (\"). Add a "
|
||||
@@ -167,24 +161,25 @@ std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
|
||||
}
|
||||
|
||||
std::unique_ptr<NumberNode> ExpressionParser2::ReadNumber() {
|
||||
SkipWhitespace();
|
||||
size_t numberStartPosition = GetCurrentPosition();
|
||||
SkipAllWhitespaces();
|
||||
gd::String parsedNumber;
|
||||
|
||||
bool numberHasStarted = false;
|
||||
bool digitFound = false;
|
||||
bool dotFound = false;
|
||||
while (!IsEndReached()) {
|
||||
if (IsAnyChar("0")) {
|
||||
if (CheckIfChar(IsZeroDigit)) {
|
||||
numberHasStarted = true;
|
||||
digitFound = true;
|
||||
if (!parsedNumber.empty()) { // Ignore leading 0s.
|
||||
parsedNumber += GetCurrentChar();
|
||||
}
|
||||
} else if (IsAnyChar("123456789")) {
|
||||
} else if (CheckIfChar(IsNonZeroDigit)) {
|
||||
numberHasStarted = true;
|
||||
digitFound = true;
|
||||
parsedNumber += GetCurrentChar();
|
||||
} else if (IsAnyChar(".") && !dotFound) {
|
||||
} else if (CheckIfChar(IsDot) && !dotFound) {
|
||||
numberHasStarted = true;
|
||||
dotFound = true;
|
||||
if (parsedNumber == "") {
|
||||
@@ -209,6 +204,7 @@ std::unique_ptr<NumberNode> ExpressionParser2::ReadNumber() {
|
||||
// valid in most languages so we allow this.
|
||||
|
||||
auto number = gd::make_unique<NumberNode>(parsedNumber);
|
||||
number->location = ExpressionParserLocation(numberStartPosition, GetCurrentPosition());
|
||||
if (!numberHasStarted || !digitFound) {
|
||||
number->diagnostic = RaiseSyntaxError(
|
||||
_("A number was expected. You must enter a number here."));
|
||||
|
@@ -48,8 +48,9 @@ class GD_CORE_API ExpressionParser2 {
|
||||
*
|
||||
* \param type Type of the expression: "string", "number",
|
||||
* type supported by gd::ParameterMetadata::IsObject, types supported by
|
||||
* gd::ParameterMetadata::IsExpression or "unknown". \param expression The
|
||||
* expression to parse \param objectName Specify the object name, only for the
|
||||
* gd::ParameterMetadata::IsExpression or "unknown".
|
||||
* \param expression The expression to parse
|
||||
* \param objectName Specify the object name, only for the
|
||||
* case of "objectvar" type.
|
||||
*
|
||||
* \return The node representing the expression as a parsed tree.
|
||||
@@ -71,18 +72,21 @@ class GD_CORE_API ExpressionParser2 {
|
||||
///@{
|
||||
std::unique_ptr<ExpressionNode> Start(const gd::String &type,
|
||||
const gd::String &objectName = "") {
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
auto expression = Expression(type, objectName);
|
||||
|
||||
// Check for extra characters at the end of the expression
|
||||
if (!IsEndReached()) {
|
||||
auto op = gd::make_unique<OperatorNode>();
|
||||
op->op = ' ';
|
||||
auto op = gd::make_unique<OperatorNode>(type, ' ');
|
||||
op->leftHandSide = std::move(expression);
|
||||
op->rightHandSide = ReadUntilEnd("unknown");
|
||||
|
||||
op->rightHandSide->diagnostic = RaiseSyntaxError(
|
||||
_("The expression has extra character at the end that should be "
|
||||
"removed (or completed if your expression is not finished)."));
|
||||
|
||||
op->location = ExpressionParserLocation(expressionStartPosition,
|
||||
GetCurrentPosition());
|
||||
return std::move(op);
|
||||
}
|
||||
|
||||
@@ -91,22 +95,24 @@ class GD_CORE_API ExpressionParser2 {
|
||||
|
||||
std::unique_ptr<ExpressionNode> Expression(
|
||||
const gd::String &type, const gd::String &objectName = "") {
|
||||
SkipWhitespace();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
std::unique_ptr<ExpressionNode> leftHandSide = Term(type, objectName);
|
||||
|
||||
SkipWhitespace();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
if (IsEndReached()) return leftHandSide;
|
||||
if (IsAnyChar(",)]")) return leftHandSide;
|
||||
if (IsAnyChar(EXPRESSION_OPERATORS)) {
|
||||
auto op = gd::make_unique<OperatorNode>();
|
||||
op->op = GetCurrentChar();
|
||||
if (CheckIfChar(IsExpressionEndingChar)) return leftHandSide;
|
||||
if (CheckIfChar(IsExpressionOperator)) {
|
||||
auto op = gd::make_unique<OperatorNode>(type, GetCurrentChar());
|
||||
op->leftHandSide = std::move(leftHandSide);
|
||||
op->diagnostic = ValidateOperator(type, GetCurrentChar());
|
||||
SkipChar();
|
||||
op->rightHandSide = Expression(type, objectName);
|
||||
|
||||
op->location = ExpressionParserLocation(expressionStartPosition,
|
||||
GetCurrentPosition());
|
||||
return std::move(op);
|
||||
}
|
||||
|
||||
@@ -124,31 +130,35 @@ class GD_CORE_API ExpressionParser2 {
|
||||
"properly written.");
|
||||
}
|
||||
|
||||
auto op = gd::make_unique<OperatorNode>();
|
||||
op->op = ' ';
|
||||
auto op = gd::make_unique<OperatorNode>(type, ' ');
|
||||
op->leftHandSide = std::move(leftHandSide);
|
||||
op->rightHandSide = Expression(type, objectName);
|
||||
op->location =
|
||||
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
|
||||
return std::move(op);
|
||||
}
|
||||
|
||||
std::unique_ptr<ExpressionNode> Term(const gd::String &type,
|
||||
const gd::String &objectName) {
|
||||
SkipWhitespace();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
std::unique_ptr<ExpressionNode> factor = Factor(type, objectName);
|
||||
SkipWhitespace();
|
||||
|
||||
SkipAllWhitespaces();
|
||||
|
||||
// This while loop is used instead of a recursion (like in Expression)
|
||||
// to guarantee the proper operator precedence. (Expression could also
|
||||
// be reworked to use a while loop).
|
||||
while (IsAnyChar(TERM_OPERATORS)) {
|
||||
auto op = gd::make_unique<OperatorNode>();
|
||||
op->op = GetCurrentChar();
|
||||
while (CheckIfChar(IsTermOperator)) {
|
||||
auto op = gd::make_unique<OperatorNode>(type, GetCurrentChar());
|
||||
op->leftHandSide = std::move(factor);
|
||||
op->diagnostic = ValidateOperator(type, GetCurrentChar());
|
||||
SkipChar();
|
||||
op->rightHandSide = Factor(type, objectName);
|
||||
SkipWhitespace();
|
||||
op->location = ExpressionParserLocation(expressionStartPosition,
|
||||
GetCurrentPosition());
|
||||
SkipAllWhitespaces();
|
||||
|
||||
factor = std::move(op);
|
||||
}
|
||||
@@ -158,12 +168,12 @@ class GD_CORE_API ExpressionParser2 {
|
||||
|
||||
std::unique_ptr<ExpressionNode> Factor(const gd::String &type,
|
||||
const gd::String &objectName) {
|
||||
SkipWhitespace();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
std::unique_ptr<ExpressionNode> factor;
|
||||
|
||||
if (IsAnyChar(QUOTE)) {
|
||||
if (CheckIfChar(IsQuote)) {
|
||||
factor = ReadText();
|
||||
if (type == "number")
|
||||
factor->diagnostic =
|
||||
@@ -173,14 +183,17 @@ class GD_CORE_API ExpressionParser2 {
|
||||
factor->diagnostic = RaiseTypeError(
|
||||
_("You entered a text, but this type was expected:") + type,
|
||||
expressionStartPosition);
|
||||
} else if (IsAnyChar(UNARY_OPERATORS)) {
|
||||
auto unaryOperator = gd::make_unique<UnaryOperatorNode>(GetCurrentChar());
|
||||
} else if (CheckIfChar(IsUnaryOperator)) {
|
||||
auto unaryOperator =
|
||||
gd::make_unique<UnaryOperatorNode>(type, GetCurrentChar());
|
||||
unaryOperator->diagnostic = ValidateUnaryOperator(type, GetCurrentChar());
|
||||
SkipChar();
|
||||
unaryOperator->factor = Factor(type, objectName);
|
||||
|
||||
unaryOperator->location = ExpressionParserLocation(
|
||||
expressionStartPosition, GetCurrentPosition());
|
||||
factor = std::move(unaryOperator);
|
||||
} else if (IsAnyChar(NUMBER_FIRST_CHAR)) {
|
||||
} else if (CheckIfChar(IsNumberFirstChar)) {
|
||||
factor = ReadNumber();
|
||||
if (type == "string")
|
||||
factor->diagnostic = RaiseTypeError(
|
||||
@@ -190,16 +203,16 @@ class GD_CORE_API ExpressionParser2 {
|
||||
factor->diagnostic = RaiseTypeError(
|
||||
_("You entered a number, but this type was expected:") + type,
|
||||
expressionStartPosition);
|
||||
} else if (IsAnyChar("(")) {
|
||||
} else if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
SkipChar();
|
||||
factor = SubExpression(type, objectName);
|
||||
|
||||
if (!IsAnyChar(")")) {
|
||||
if (!CheckIfChar(IsClosingParenthesis)) {
|
||||
factor->diagnostic =
|
||||
RaiseSyntaxError(_("Missing a closing parenthesis. Add a closing "
|
||||
"parenthesis for each opening parenthesis."));
|
||||
}
|
||||
SkipIfIsAnyChar(")");
|
||||
SkipIfChar(IsClosingParenthesis);
|
||||
} else if (IsIdentifierAllowedChar()) {
|
||||
// This is a place where the grammar differs according to the
|
||||
// type being expected.
|
||||
@@ -218,92 +231,132 @@ class GD_CORE_API ExpressionParser2 {
|
||||
|
||||
std::unique_ptr<SubExpressionNode> SubExpression(
|
||||
const gd::String &type, const gd::String &objectName) {
|
||||
return std::move(
|
||||
gd::make_unique<SubExpressionNode>(Expression(type, objectName)));
|
||||
size_t expressionStartPosition = GetCurrentPosition();
|
||||
auto subExpression =
|
||||
gd::make_unique<SubExpressionNode>(type, Expression(type, objectName));
|
||||
subExpression->location =
|
||||
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
|
||||
|
||||
return std::move(subExpression);
|
||||
};
|
||||
|
||||
std::unique_ptr<IdentifierOrFunctionOrEmptyNode> Identifier(
|
||||
const gd::String &type) {
|
||||
size_t identifierStartPosition = GetCurrentPosition();
|
||||
gd::String name = ReadIdentifierName();
|
||||
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
|
||||
Identifier(const gd::String &type) {
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
gd::String name = identifierAndLocation.name;
|
||||
auto nameLocation = identifierAndLocation.location;
|
||||
|
||||
SkipWhitespace();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
// We consider a namespace separator to be allowed here and be part of the
|
||||
// function name (or object name, but object names are not allowed to
|
||||
// contain a ":"). This is because functions from extensions have their
|
||||
// extension name prefix, and separated by the namespace separator. This
|
||||
// could maybe be refactored to create different nodes in the future.
|
||||
if (IsNamespaceSeparator()) {
|
||||
SkipNamespaceSeparator();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
auto postNamespaceIdentifierAndLocation = ReadIdentifierName();
|
||||
name += NAMESPACE_SEPARATOR;
|
||||
name += ReadIdentifierName();
|
||||
name += postNamespaceIdentifierAndLocation.name;
|
||||
ExpressionParserLocation completeNameLocation(
|
||||
nameLocation.GetStartPosition(),
|
||||
postNamespaceIdentifierAndLocation.location.GetEndPosition());
|
||||
nameLocation = completeNameLocation;
|
||||
}
|
||||
|
||||
if (IsAnyChar("(")) {
|
||||
SkipChar();
|
||||
return FreeFunction(type, name, identifierStartPosition);
|
||||
} else if (IsAnyChar(DOT)) {
|
||||
SkipChar();
|
||||
if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
ExpressionParserLocation openingParenthesisLocation = SkipChar();
|
||||
return FreeFunction(type, name, nameLocation, openingParenthesisLocation);
|
||||
} else if (CheckIfChar(IsDot)) {
|
||||
ExpressionParserLocation dotLocation = SkipChar();
|
||||
SkipAllWhitespaces();
|
||||
return ObjectFunctionOrBehaviorFunction(
|
||||
type, name, identifierStartPosition);
|
||||
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\")."),
|
||||
identifierStartPosition);
|
||||
nameLocation.GetStartPosition());
|
||||
} else if (type == "number") {
|
||||
identifier->diagnostic = RaiseTypeError(_("You must enter a number."),
|
||||
identifierStartPosition);
|
||||
identifier->diagnostic = RaiseTypeError(
|
||||
_("You must enter a number."), nameLocation.GetStartPosition());
|
||||
} else if (!gd::ParameterMetadata::IsObject(type)) {
|
||||
identifier->diagnostic = RaiseTypeError(
|
||||
_("You've entered a name, but this type was expected:") + type,
|
||||
identifierStartPosition);
|
||||
nameLocation.GetStartPosition());
|
||||
}
|
||||
|
||||
identifier->location = ExpressionParserLocation(
|
||||
nameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
return std::move(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<VariableNode> Variable(const gd::String &type,
|
||||
const gd::String &objectName) {
|
||||
size_t identifierStartPosition = GetCurrentPosition();
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
const gd::String &name = identifierAndLocation.name;
|
||||
const auto &nameLocation = identifierAndLocation.location;
|
||||
|
||||
gd::String name = ReadIdentifierName();
|
||||
auto variable = gd::make_unique<VariableNode>(type, name, objectName);
|
||||
variable->child = VariableAccessorOrVariableBracketAccessor();
|
||||
|
||||
variable->location = ExpressionParserLocation(
|
||||
nameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
variable->nameLocation = nameLocation;
|
||||
return std::move(variable);
|
||||
}
|
||||
|
||||
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
|
||||
VariableAccessorOrVariableBracketAccessor() {
|
||||
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode> child;
|
||||
SkipWhitespace();
|
||||
if (IsAnyChar("[")) {
|
||||
size_t childStartPosition = GetCurrentPosition();
|
||||
|
||||
SkipAllWhitespaces();
|
||||
if (CheckIfChar(IsOpeningSquareBracket)) {
|
||||
SkipChar();
|
||||
child =
|
||||
auto child =
|
||||
gd::make_unique<VariableBracketAccessorNode>(Expression("string"));
|
||||
|
||||
if (!IsAnyChar("]")) {
|
||||
if (!CheckIfChar(IsClosingSquareBracket)) {
|
||||
child->diagnostic =
|
||||
RaiseSyntaxError(_("Missing a closing bracket. Add a closing "
|
||||
"bracket for each opening bracket."));
|
||||
}
|
||||
SkipIfIsAnyChar("]");
|
||||
SkipIfChar(IsClosingSquareBracket);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
} else if (IsAnyChar(DOT)) {
|
||||
SkipChar();
|
||||
SkipWhitespace();
|
||||
child->location =
|
||||
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
|
||||
|
||||
child = gd::make_unique<VariableAccessorNode>(ReadIdentifierName());
|
||||
return std::move(child);
|
||||
} else if (CheckIfChar(IsDot)) {
|
||||
auto dotLocation = SkipChar();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
auto child =
|
||||
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
child->nameLocation = identifierAndLocation.location;
|
||||
child->dotLocation = dotLocation;
|
||||
child->location =
|
||||
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
|
||||
|
||||
return std::move(child);
|
||||
}
|
||||
|
||||
return child;
|
||||
return std::move(
|
||||
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>());
|
||||
}
|
||||
|
||||
std::unique_ptr<FunctionNode> FreeFunction(const gd::String &type,
|
||||
const gd::String &functionFullName,
|
||||
size_t functionStartPosition) {
|
||||
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
|
||||
|
||||
@@ -315,32 +368,49 @@ class GD_CORE_API ExpressionParser2 {
|
||||
: MetadataProvider::GetStrExpressionMetadata(
|
||||
platform, functionFullName);
|
||||
|
||||
auto parametersAndError = Parameters(metadata.parameters);
|
||||
auto function = gd::make_unique<FunctionNode>(
|
||||
type, std::move(parametersAndError.first), metadata, functionFullName);
|
||||
function->diagnostic = std::move(parametersAndError.second);
|
||||
auto parametersNode = Parameters(metadata.parameters);
|
||||
auto function = gd::make_unique<FunctionCallNode>(
|
||||
type, std::move(parametersNode.parameters), metadata, functionFullName);
|
||||
function->diagnostic = std::move(parametersNode.diagnostic);
|
||||
if (!function->diagnostic)
|
||||
function->diagnostic = ValidateFunction(*function, functionStartPosition);
|
||||
function->diagnostic =
|
||||
ValidateFunction(*function, identifierLocation.GetStartPosition());
|
||||
|
||||
function->location = ExpressionParserLocation(
|
||||
identifierLocation.GetStartPosition(), GetCurrentPosition());
|
||||
function->functionNameLocation = identifierLocation;
|
||||
function->openingParenthesisLocation = openingParenthesisLocation;
|
||||
function->closingParenthesisLocation =
|
||||
parametersNode.closingParenthesisLocation;
|
||||
return std::move(function);
|
||||
}
|
||||
|
||||
std::unique_ptr<FunctionOrEmptyNode> ObjectFunctionOrBehaviorFunction(
|
||||
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode>
|
||||
ObjectFunctionOrBehaviorFunction(
|
||||
const gd::String &type,
|
||||
const gd::String &objectName,
|
||||
size_t functionStartPosition) {
|
||||
gd::String objectFunctionOrBehaviorName = ReadIdentifierName();
|
||||
const ExpressionParserLocation &objectNameLocation,
|
||||
const ExpressionParserLocation &objectNameDotLocation) {
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
const gd::String &objectFunctionOrBehaviorName = identifierAndLocation.name;
|
||||
const auto &objectFunctionOrBehaviorNameLocation =
|
||||
identifierAndLocation.location;
|
||||
|
||||
SkipWhitespace();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
if (IsNamespaceSeparator()) {
|
||||
SkipNamespaceSeparator();
|
||||
ExpressionParserLocation namespaceSeparatorLocation =
|
||||
SkipNamespaceSeparator();
|
||||
SkipAllWhitespaces();
|
||||
return BehaviorFunction(type,
|
||||
objectName,
|
||||
objectFunctionOrBehaviorName,
|
||||
functionStartPosition);
|
||||
} else if (IsAnyChar("(")) {
|
||||
SkipChar();
|
||||
objectNameLocation,
|
||||
objectNameDotLocation,
|
||||
objectFunctionOrBehaviorNameLocation,
|
||||
namespaceSeparatorLocation);
|
||||
} else if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
ExpressionParserLocation openingParenthesisLocation = SkipChar();
|
||||
|
||||
gd::String objectType =
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName);
|
||||
@@ -354,40 +424,60 @@ class GD_CORE_API ExpressionParser2 {
|
||||
: MetadataProvider::GetObjectStrExpressionMetadata(
|
||||
platform, objectType, objectFunctionOrBehaviorName);
|
||||
|
||||
auto parametersAndError = Parameters(metadata.parameters, objectName);
|
||||
auto function =
|
||||
gd::make_unique<FunctionNode>(type,
|
||||
objectName,
|
||||
std::move(parametersAndError.first),
|
||||
metadata,
|
||||
objectFunctionOrBehaviorName);
|
||||
function->diagnostic = std::move(parametersAndError.second);
|
||||
auto parametersNode = Parameters(metadata.parameters, objectName);
|
||||
auto function = gd::make_unique<FunctionCallNode>(
|
||||
type,
|
||||
objectName,
|
||||
std::move(parametersNode.parameters),
|
||||
metadata,
|
||||
objectFunctionOrBehaviorName);
|
||||
function->diagnostic = std::move(parametersNode.diagnostic);
|
||||
if (!function->diagnostic)
|
||||
function->diagnostic =
|
||||
ValidateFunction(*function, functionStartPosition);
|
||||
ValidateFunction(*function, objectNameLocation.GetStartPosition());
|
||||
|
||||
function->location = ExpressionParserLocation(
|
||||
objectNameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
function->objectNameLocation = objectNameLocation;
|
||||
function->objectNameDotLocation = objectNameDotLocation;
|
||||
function->functionNameLocation = objectFunctionOrBehaviorNameLocation;
|
||||
function->openingParenthesisLocation = openingParenthesisLocation;
|
||||
function->closingParenthesisLocation =
|
||||
parametersNode.closingParenthesisLocation;
|
||||
return std::move(function);
|
||||
}
|
||||
|
||||
auto node = gd::make_unique<EmptyNode>(type);
|
||||
auto node = gd::make_unique<ObjectFunctionNameNode>(
|
||||
type, objectName, objectFunctionOrBehaviorName);
|
||||
node->diagnostic = RaiseSyntaxError(
|
||||
_("An opening parenthesis (for an object expression), or double colon "
|
||||
"(::) was expected (for a behavior expression)."));
|
||||
|
||||
node->location = ExpressionParserLocation(
|
||||
objectNameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
node->objectNameLocation = objectNameLocation;
|
||||
node->objectNameDotLocation = objectNameDotLocation;
|
||||
node->objectFunctionOrBehaviorNameLocation =
|
||||
objectFunctionOrBehaviorNameLocation;
|
||||
return std::move(node);
|
||||
}
|
||||
|
||||
std::unique_ptr<FunctionOrEmptyNode> BehaviorFunction(
|
||||
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode> BehaviorFunction(
|
||||
const gd::String &type,
|
||||
const gd::String &objectName,
|
||||
const gd::String &behaviorName,
|
||||
size_t functionStartPosition) {
|
||||
gd::String functionName = ReadIdentifierName();
|
||||
const ExpressionParserLocation &objectNameLocation,
|
||||
const ExpressionParserLocation &objectNameDotLocation,
|
||||
const ExpressionParserLocation &behaviorNameLocation,
|
||||
const ExpressionParserLocation &behaviorNameNamespaceSeparatorLocation) {
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
const gd::String &functionName = identifierAndLocation.name;
|
||||
const auto &functionNameLocation = identifierAndLocation.location;
|
||||
|
||||
SkipWhitespace();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
if (IsAnyChar("(")) {
|
||||
SkipChar();
|
||||
if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
ExpressionParserLocation openingParenthesisLocation = SkipChar();
|
||||
|
||||
gd::String behaviorType = GetTypeOfBehavior(
|
||||
globalObjectsContainer, objectsContainer, behaviorName);
|
||||
@@ -400,35 +490,61 @@ class GD_CORE_API ExpressionParser2 {
|
||||
: MetadataProvider::GetBehaviorStrExpressionMetadata(
|
||||
platform, behaviorType, functionName);
|
||||
|
||||
auto parametersAndError =
|
||||
auto parametersNode =
|
||||
Parameters(metadata.parameters, objectName, behaviorName);
|
||||
auto function =
|
||||
gd::make_unique<FunctionNode>(type,
|
||||
objectName,
|
||||
behaviorName,
|
||||
std::move(parametersAndError.first),
|
||||
metadata,
|
||||
functionName);
|
||||
function->diagnostic = std::move(parametersAndError.second);
|
||||
auto function = gd::make_unique<FunctionCallNode>(
|
||||
type,
|
||||
objectName,
|
||||
behaviorName,
|
||||
std::move(parametersNode.parameters),
|
||||
metadata,
|
||||
functionName);
|
||||
function->diagnostic = std::move(parametersNode.diagnostic);
|
||||
if (!function->diagnostic)
|
||||
function->diagnostic =
|
||||
ValidateFunction(*function, functionStartPosition);
|
||||
ValidateFunction(*function, objectNameLocation.GetStartPosition());
|
||||
|
||||
function->location = ExpressionParserLocation(
|
||||
objectNameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
function->objectNameLocation = objectNameLocation;
|
||||
function->objectNameDotLocation = objectNameDotLocation;
|
||||
function->behaviorNameLocation = behaviorNameLocation;
|
||||
function->behaviorNameNamespaceSeparatorLocation =
|
||||
behaviorNameNamespaceSeparatorLocation;
|
||||
function->openingParenthesisLocation = openingParenthesisLocation;
|
||||
function->closingParenthesisLocation =
|
||||
parametersNode.closingParenthesisLocation;
|
||||
function->functionNameLocation = functionNameLocation;
|
||||
return std::move(function);
|
||||
} else {
|
||||
auto node = gd::make_unique<EmptyNode>(type);
|
||||
auto node = gd::make_unique<ObjectFunctionNameNode>(
|
||||
type, objectName, behaviorName, functionName);
|
||||
node->diagnostic = RaiseSyntaxError(
|
||||
_("An opening parenthesis was expected here to call a function."));
|
||||
|
||||
node->location = ExpressionParserLocation(
|
||||
objectNameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
node->objectNameLocation = objectNameLocation;
|
||||
node->objectNameDotLocation = objectNameDotLocation;
|
||||
node->objectFunctionOrBehaviorNameLocation = behaviorNameLocation;
|
||||
node->behaviorNameNamespaceSeparatorLocation =
|
||||
behaviorNameNamespaceSeparatorLocation;
|
||||
node->behaviorFunctionNameLocation = functionNameLocation;
|
||||
return std::move(node);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::unique_ptr<ExpressionNode>>,
|
||||
std::unique_ptr<gd::ExpressionParserError>>
|
||||
Parameters(std::vector<gd::ParameterMetadata> parameterMetadata,
|
||||
const gd::String &objectName = "",
|
||||
const gd::String &behaviorName = "") {
|
||||
// A temporary node that will be integrated into function nodes.
|
||||
struct ParametersNode {
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters;
|
||||
std::unique_ptr<gd::ExpressionParserError> diagnostic;
|
||||
ExpressionParserLocation closingParenthesisLocation;
|
||||
};
|
||||
|
||||
ParametersNode Parameters(
|
||||
std::vector<gd::ParameterMetadata> parameterMetadata,
|
||||
const gd::String &objectName = "",
|
||||
const gd::String &behaviorName = "") {
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters;
|
||||
|
||||
// By convention, object is always the first parameter, and behavior the
|
||||
@@ -437,11 +553,12 @@ class GD_CORE_API ExpressionParser2 {
|
||||
WrittenParametersFirstIndex(objectName, behaviorName);
|
||||
|
||||
while (!IsEndReached()) {
|
||||
SkipWhitespace();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
if (IsAnyChar(")")) {
|
||||
SkipChar();
|
||||
return std::make_pair(std::move(parameters), nullptr);
|
||||
if (CheckIfChar(IsClosingParenthesis)) {
|
||||
auto closingParenthesisLocation = SkipChar();
|
||||
return ParametersNode{
|
||||
std::move(parameters), nullptr, closingParenthesisLocation};
|
||||
} else {
|
||||
if (parameterIndex < parameterMetadata.size()) {
|
||||
const gd::String &type = parameterMetadata[parameterIndex].GetType();
|
||||
@@ -479,16 +596,18 @@ class GD_CORE_API ExpressionParser2 {
|
||||
GetCurrentPosition());
|
||||
}
|
||||
|
||||
SkipWhitespace();
|
||||
SkipIfIsAnyChar(PARAMETERS_SEPARATOR);
|
||||
SkipAllWhitespaces();
|
||||
SkipIfChar(IsParameterSeparator);
|
||||
parameterIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(
|
||||
ExpressionParserLocation invalidClosingParenthesisLocation;
|
||||
return ParametersNode{
|
||||
std::move(parameters),
|
||||
RaiseSyntaxError(_("The list of parameters is not terminated. Add a "
|
||||
"closing parenthesis to end the parameters.")));
|
||||
"closing parenthesis to end the parameters.")),
|
||||
invalidClosingParenthesisLocation};
|
||||
}
|
||||
///@}
|
||||
|
||||
@@ -497,7 +616,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
*/
|
||||
///@{
|
||||
std::unique_ptr<ExpressionParserDiagnostic> ValidateFunction(
|
||||
const gd::FunctionNode &function, size_t functionStartPosition);
|
||||
const gd::FunctionCallNode &function, size_t functionStartPosition);
|
||||
|
||||
std::unique_ptr<ExpressionParserDiagnostic> ValidateOperator(
|
||||
const gd::String &type, gd::String::value_type operatorChar) {
|
||||
@@ -525,7 +644,8 @@ class GD_CORE_API ExpressionParser2 {
|
||||
} 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."),
|
||||
_("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>(
|
||||
@@ -561,7 +681,8 @@ class GD_CORE_API ExpressionParser2 {
|
||||
} 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."),
|
||||
_("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>(
|
||||
@@ -579,55 +700,133 @@ class GD_CORE_API ExpressionParser2 {
|
||||
* Read tokens or characters
|
||||
*/
|
||||
///@{
|
||||
void SkipChar() { currentPosition++; }
|
||||
ExpressionParserLocation SkipChar() {
|
||||
size_t startPosition = currentPosition;
|
||||
return ExpressionParserLocation(startPosition, ++currentPosition);
|
||||
}
|
||||
|
||||
void SkipWhitespace() {
|
||||
void SkipAllWhitespaces() {
|
||||
while (currentPosition < expression.size() &&
|
||||
WHITESPACES.find(expression[currentPosition]) != gd::String::npos) {
|
||||
IsWhitespace(expression[currentPosition])) {
|
||||
currentPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
void SkipIfIsAnyChar(const gd::String &allowedCharacters) {
|
||||
if (IsAnyChar(allowedCharacters)) {
|
||||
void SkipIfChar(
|
||||
const std::function<bool(gd::String::value_type)> &predicate) {
|
||||
if (CheckIfChar(predicate)) {
|
||||
currentPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
void SkipNamespaceSeparator() {
|
||||
ExpressionParserLocation SkipNamespaceSeparator() {
|
||||
size_t startPosition = currentPosition;
|
||||
// Namespace separator is a special kind of delimiter as it is 2 characters
|
||||
// long
|
||||
if (IsNamespaceSeparator()) {
|
||||
currentPosition += NAMESPACE_SEPARATOR.size();
|
||||
}
|
||||
|
||||
return ExpressionParserLocation(startPosition, currentPosition);
|
||||
}
|
||||
|
||||
bool IsAnyChar(const gd::String &allowedCharacters) {
|
||||
if (currentPosition < expression.size() &&
|
||||
allowedCharacters.find(expression[currentPosition]) !=
|
||||
gd::String::npos) {
|
||||
return true;
|
||||
}
|
||||
bool CheckIfChar(
|
||||
const std::function<bool(gd::String::value_type)> &predicate) {
|
||||
if (currentPosition >= expression.size()) return false;
|
||||
gd::String::value_type character = expression[currentPosition];
|
||||
|
||||
return false;
|
||||
return predicate(character);
|
||||
}
|
||||
|
||||
bool IsIdentifierAllowedChar() {
|
||||
if (currentPosition < expression.size() &&
|
||||
PARAMETERS_SEPARATOR.find(expression[currentPosition]) ==
|
||||
gd::String::npos &&
|
||||
DOT.find(expression[currentPosition]) == gd::String::npos &&
|
||||
QUOTE.find(expression[currentPosition]) == gd::String::npos &&
|
||||
BRACKETS.find(expression[currentPosition]) == gd::String::npos &&
|
||||
EXPRESSION_OPERATORS.find(expression[currentPosition]) ==
|
||||
gd::String::npos &&
|
||||
TERM_OPERATORS.find(expression[currentPosition]) == gd::String::npos) {
|
||||
if (currentPosition >= expression.size()) return false;
|
||||
gd::String::value_type character = expression[currentPosition];
|
||||
|
||||
// Quickly compare if the character is a number or ASCII character.
|
||||
if ((character >= '0' && character <= '9') ||
|
||||
(character >= 'A' && character <= 'Z') ||
|
||||
(character >= 'a' && character <= 'z'))
|
||||
return true;
|
||||
|
||||
// Otherwise do the full check against separators forbidden in identifiers.
|
||||
if (!IsParameterSeparator(character) && !IsDot(character) &&
|
||||
!IsQuote(character) && !IsBracket(character) &&
|
||||
!IsExpressionOperator(character) && !IsTermOperator(character)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsWhitespace(gd::String::value_type character) {
|
||||
return character == ' ' || character == '\n' || character == '\r';
|
||||
}
|
||||
|
||||
static bool IsParameterSeparator(gd::String::value_type character) {
|
||||
return character == ',';
|
||||
}
|
||||
|
||||
static bool IsDot(gd::String::value_type character) {
|
||||
return character == '.';
|
||||
}
|
||||
|
||||
static bool IsQuote(gd::String::value_type character) {
|
||||
return character == '"';
|
||||
}
|
||||
|
||||
static bool IsBracket(gd::String::value_type character) {
|
||||
return character == '(' || character == ')' || character == '[' ||
|
||||
character == ']' || character == '{' || character == '}';
|
||||
}
|
||||
|
||||
static bool IsOpeningParenthesis(gd::String::value_type character) {
|
||||
return character == '(';
|
||||
}
|
||||
|
||||
static bool IsClosingParenthesis(gd::String::value_type character) {
|
||||
return character == ')';
|
||||
}
|
||||
|
||||
static bool IsOpeningSquareBracket(gd::String::value_type character) {
|
||||
return character == '[';
|
||||
}
|
||||
|
||||
static bool IsClosingSquareBracket(gd::String::value_type character) {
|
||||
return character == ']';
|
||||
}
|
||||
|
||||
static bool IsExpressionEndingChar(gd::String::value_type character) {
|
||||
return character == ',' || IsClosingParenthesis(character) ||
|
||||
IsClosingSquareBracket(character);
|
||||
}
|
||||
|
||||
static bool IsExpressionOperator(gd::String::value_type character) {
|
||||
return character == '+' || character == '-' || character == '<' ||
|
||||
character == '>' || character == '?' || character == '^' ||
|
||||
character == '=' || character == '\\' || character == ':' ||
|
||||
character == '!';
|
||||
}
|
||||
|
||||
static bool IsUnaryOperator(gd::String::value_type character) {
|
||||
return character == '+' || character == '-';
|
||||
}
|
||||
|
||||
static bool IsTermOperator(gd::String::value_type character) {
|
||||
return character == '/' || character == '*';
|
||||
}
|
||||
|
||||
static bool IsNumberFirstChar(gd::String::value_type character) {
|
||||
return character == '.' || (character >= '0' && character <= '9');
|
||||
}
|
||||
|
||||
static bool IsNonZeroDigit(gd::String::value_type character) {
|
||||
return (character >= '1' && character <= '9');
|
||||
}
|
||||
|
||||
static bool IsZeroDigit(gd::String::value_type character) {
|
||||
return character == '0';
|
||||
}
|
||||
|
||||
bool IsNamespaceSeparator() {
|
||||
// Namespace separator is a special kind of delimiter as it is 2 characters
|
||||
// long
|
||||
@@ -638,12 +837,20 @@ class GD_CORE_API ExpressionParser2 {
|
||||
|
||||
bool IsEndReached() { return currentPosition >= expression.size(); }
|
||||
|
||||
gd::String ReadIdentifierName() {
|
||||
// A temporary node used when reading an identifier
|
||||
struct IdentifierAndLocation {
|
||||
gd::String name;
|
||||
ExpressionParserLocation location;
|
||||
};
|
||||
|
||||
IdentifierAndLocation ReadIdentifierName() {
|
||||
gd::String name;
|
||||
size_t startPosition = currentPosition;
|
||||
while (currentPosition < expression.size() &&
|
||||
(IsIdentifierAllowedChar()
|
||||
// Allow whitespace in identifier name for compatibility
|
||||
|| expression[currentPosition] == ' ')) {
|
||||
||
|
||||
expression[currentPosition] == ' ')) {
|
||||
name += expression[currentPosition];
|
||||
currentPosition++;
|
||||
}
|
||||
@@ -651,12 +858,23 @@ class GD_CORE_API ExpressionParser2 {
|
||||
// Trim whitespace at the end (we allow them for compatibility inside
|
||||
// the name, but after the last character that is not whitespace, they
|
||||
// should be ignore again).
|
||||
size_t lastCharacterPos = name.find_last_not_of(WHITESPACES);
|
||||
if (!name.empty() && (lastCharacterPos + 1) < name.size()) {
|
||||
name.erase(lastCharacterPos + 1);
|
||||
if (!name.empty() && IsWhitespace(name[name.size() - 1])) {
|
||||
size_t lastCharacterPos = name.size() - 1;
|
||||
while (lastCharacterPos < name.size() &&
|
||||
IsWhitespace(name[lastCharacterPos])) {
|
||||
lastCharacterPos--;
|
||||
}
|
||||
if ((lastCharacterPos + 1) < name.size()) {
|
||||
name.erase(lastCharacterPos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
IdentifierAndLocation identifierAndLocation{
|
||||
name,
|
||||
// The location is ignoring the trailing whitespace (only whitespace
|
||||
// inside the identifier are allowed for compatibility).
|
||||
ExpressionParserLocation(startPosition, startPosition + name.size())};
|
||||
return identifierAndLocation;
|
||||
}
|
||||
|
||||
std::unique_ptr<TextNode> ReadText();
|
||||
@@ -664,24 +882,32 @@ class GD_CORE_API ExpressionParser2 {
|
||||
std::unique_ptr<NumberNode> ReadNumber();
|
||||
|
||||
std::unique_ptr<EmptyNode> ReadUntilWhitespace(gd::String type) {
|
||||
size_t startPosition = GetCurrentPosition();
|
||||
gd::String text;
|
||||
while (currentPosition < expression.size() &&
|
||||
WHITESPACES.find(expression[currentPosition]) == gd::String::npos) {
|
||||
!IsWhitespace(expression[currentPosition])) {
|
||||
text += expression[currentPosition];
|
||||
currentPosition++;
|
||||
}
|
||||
|
||||
return gd::make_unique<EmptyNode>(type, text);
|
||||
auto node = gd::make_unique<EmptyNode>(type, text);
|
||||
node->location =
|
||||
ExpressionParserLocation(startPosition, GetCurrentPosition());
|
||||
return node;
|
||||
}
|
||||
|
||||
std::unique_ptr<EmptyNode> ReadUntilEnd(gd::String type) {
|
||||
size_t startPosition = GetCurrentPosition();
|
||||
gd::String text;
|
||||
while (currentPosition < expression.size()) {
|
||||
text += expression[currentPosition];
|
||||
currentPosition++;
|
||||
}
|
||||
|
||||
return gd::make_unique<EmptyNode>(type, text);
|
||||
auto node = gd::make_unique<EmptyNode>(type, text);
|
||||
node->location =
|
||||
ExpressionParserLocation(startPosition, GetCurrentPosition());
|
||||
return node;
|
||||
}
|
||||
|
||||
size_t GetCurrentPosition() { return currentPosition; }
|
||||
@@ -746,15 +972,6 @@ class GD_CORE_API ExpressionParser2 {
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
|
||||
static gd::String NUMBER_FIRST_CHAR;
|
||||
static gd::String DOT;
|
||||
static gd::String PARAMETERS_SEPARATOR;
|
||||
static gd::String QUOTE;
|
||||
static gd::String BRACKETS;
|
||||
static gd::String EXPRESSION_OPERATORS;
|
||||
static gd::String TERM_OPERATORS;
|
||||
static gd::String UNARY_OPERATORS;
|
||||
static gd::String WHITESPACES;
|
||||
static gd::String NAMESPACE_SEPARATOR;
|
||||
};
|
||||
|
||||
|
@@ -20,6 +20,24 @@ class ExpressionMetadata;
|
||||
|
||||
namespace gd {
|
||||
|
||||
struct ExpressionParserLocation {
|
||||
ExpressionParserLocation() : isValid(false){};
|
||||
ExpressionParserLocation(size_t position)
|
||||
: isValid(true), startPosition(position), endPosition(position){};
|
||||
ExpressionParserLocation(size_t startPosition_, size_t endPosition_)
|
||||
: isValid(true),
|
||||
startPosition(startPosition_),
|
||||
endPosition(endPosition_){};
|
||||
size_t GetStartPosition() const { return startPosition; }
|
||||
size_t GetEndPosition() const { return endPosition; }
|
||||
bool IsValid() const { return isValid; }
|
||||
|
||||
private:
|
||||
bool isValid;
|
||||
size_t startPosition;
|
||||
size_t endPosition;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A diagnostic that can be attached to a gd::ExpressionNode.
|
||||
*/
|
||||
@@ -40,30 +58,25 @@ struct ExpressionParserError : public ExpressionParserDiagnostic {
|
||||
ExpressionParserError(const gd::String &type_,
|
||||
const gd::String &message_,
|
||||
size_t position_)
|
||||
: type(type_),
|
||||
message(message_),
|
||||
startPosition(position_),
|
||||
endPosition(position_){};
|
||||
: type(type_), message(message_), location(position_){};
|
||||
ExpressionParserError(const gd::String &type_,
|
||||
const gd::String &message_,
|
||||
size_t startPosition_,
|
||||
size_t endPosition_)
|
||||
: type(type_),
|
||||
message(message_),
|
||||
startPosition(startPosition_),
|
||||
endPosition(endPosition_){};
|
||||
location(startPosition_, endPosition_){};
|
||||
virtual ~ExpressionParserError(){};
|
||||
|
||||
bool IsError() override { return true; }
|
||||
const gd::String &GetMessage() override { return message; }
|
||||
size_t GetStartPosition() override { return startPosition; }
|
||||
size_t GetEndPosition() override { return endPosition; }
|
||||
size_t GetStartPosition() override { return location.GetStartPosition(); }
|
||||
size_t GetEndPosition() override { return location.GetEndPosition(); }
|
||||
|
||||
private:
|
||||
gd::String type;
|
||||
gd::String message;
|
||||
size_t startPosition;
|
||||
size_t endPosition;
|
||||
ExpressionParserLocation location;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -75,16 +88,26 @@ struct ExpressionNode {
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker){};
|
||||
|
||||
std::unique_ptr<ExpressionParserDiagnostic> diagnostic;
|
||||
ExpressionParserLocation location; ///< The location of the entire node. Some
|
||||
///nodes might have other locations stored
|
||||
///inside them. For example, a function
|
||||
///can store the position of the object
|
||||
///name, the dot, the function name,
|
||||
///etc...
|
||||
};
|
||||
|
||||
struct SubExpressionNode : public ExpressionNode {
|
||||
SubExpressionNode(std::unique_ptr<ExpressionNode> expression_)
|
||||
: expression(std::move(expression_)){};
|
||||
SubExpressionNode(const gd::String &type_,
|
||||
std::unique_ptr<ExpressionNode> expression_)
|
||||
: type(type_), expression(std::move(expression_)){};
|
||||
virtual ~SubExpressionNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitSubExpressionNode(*this);
|
||||
};
|
||||
|
||||
gd::String type; // "string", "number", type supported by
|
||||
// gd::ParameterMetadata::IsObject, types supported by
|
||||
// gd::ParameterMetadata::IsExpression or "unknown".
|
||||
std::unique_ptr<ExpressionNode> expression;
|
||||
};
|
||||
|
||||
@@ -92,6 +115,8 @@ struct SubExpressionNode : public ExpressionNode {
|
||||
* \brief An operator node. For example: "lhs + rhs".
|
||||
*/
|
||||
struct OperatorNode : public ExpressionNode {
|
||||
OperatorNode(const gd::String &type_, gd::String::value_type op_)
|
||||
: type(type_), op(op_){};
|
||||
virtual ~OperatorNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitOperatorNode(*this);
|
||||
@@ -99,6 +124,9 @@ struct OperatorNode : public ExpressionNode {
|
||||
|
||||
std::unique_ptr<ExpressionNode> leftHandSide;
|
||||
std::unique_ptr<ExpressionNode> rightHandSide;
|
||||
gd::String type; // "string", "number", type supported by
|
||||
// gd::ParameterMetadata::IsObject, types supported by
|
||||
// gd::ParameterMetadata::IsExpression or "unknown".
|
||||
gd::String::value_type op;
|
||||
};
|
||||
|
||||
@@ -106,13 +134,17 @@ struct OperatorNode : public ExpressionNode {
|
||||
* \brief A unary operator node. For example: "-2".
|
||||
*/
|
||||
struct UnaryOperatorNode : public ExpressionNode {
|
||||
UnaryOperatorNode(gd::String::value_type op_) : op(op_){};
|
||||
UnaryOperatorNode(const gd::String &type_, gd::String::value_type op_)
|
||||
: type(type_), op(op_){};
|
||||
virtual ~UnaryOperatorNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitUnaryOperatorNode(*this);
|
||||
};
|
||||
|
||||
std::unique_ptr<ExpressionNode> factor;
|
||||
gd::String type; // "string", "number", type supported by
|
||||
// gd::ParameterMetadata::IsObject, types supported by
|
||||
// gd::ParameterMetadata::IsExpression or "unknown".
|
||||
gd::String::value_type op;
|
||||
};
|
||||
|
||||
@@ -170,6 +202,8 @@ struct VariableNode : public ExpressionNode {
|
||||
|
||||
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
|
||||
child; // Can be nullptr if no accessor
|
||||
|
||||
ExpressionParserLocation nameLocation;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -185,6 +219,8 @@ struct VariableAccessorNode
|
||||
};
|
||||
|
||||
gd::String name;
|
||||
ExpressionParserLocation nameLocation;
|
||||
ExpressionParserLocation dotLocation;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -203,12 +239,14 @@ struct VariableBracketAccessorNode
|
||||
std::unique_ptr<ExpressionNode> expression;
|
||||
};
|
||||
|
||||
struct IdentifierOrFunctionOrEmptyNode : public ExpressionNode {};
|
||||
struct IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode
|
||||
: public ExpressionNode {};
|
||||
|
||||
/**
|
||||
* \brief An identifier node, usually representing an object.
|
||||
* \brief An identifier node, usually representing an object or a function name.
|
||||
*/
|
||||
struct IdentifierNode : public IdentifierOrFunctionOrEmptyNode {
|
||||
struct IdentifierNode
|
||||
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
IdentifierNode(const gd::String &identifierName_, const gd::String &type_)
|
||||
: identifierName(identifierName_), type(type_){};
|
||||
virtual ~IdentifierNode(){};
|
||||
@@ -220,64 +258,142 @@ struct IdentifierNode : public IdentifierOrFunctionOrEmptyNode {
|
||||
gd::String type;
|
||||
};
|
||||
|
||||
struct FunctionOrEmptyNode : public IdentifierOrFunctionOrEmptyNode {
|
||||
virtual ~FunctionOrEmptyNode(){};
|
||||
struct FunctionCallOrObjectFunctionNameOrEmptyNode
|
||||
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
virtual ~FunctionCallOrObjectFunctionNameOrEmptyNode(){};
|
||||
void Visit(ExpressionParser2NodeWorker &worker) override{};
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A function node. For example: "MyExtension::MyFunction(1, 2)".
|
||||
* \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".
|
||||
*/
|
||||
struct FunctionNode : public FunctionOrEmptyNode {
|
||||
FunctionNode(const gd::String &type_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
const gd::String &functionName_)
|
||||
struct ObjectFunctionNameNode
|
||||
: public FunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
ObjectFunctionNameNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
const gd::String &objectFunctionOrBehaviorName_)
|
||||
: type(type_),
|
||||
objectName(objectName_),
|
||||
objectFunctionOrBehaviorName(objectFunctionOrBehaviorName_) {}
|
||||
ObjectFunctionNameNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
const gd::String &behaviorName_,
|
||||
const gd::String &behaviorFunctionName_)
|
||||
: type(type_),
|
||||
objectName(objectName_),
|
||||
objectFunctionOrBehaviorName(behaviorName_),
|
||||
behaviorFunctionName(behaviorFunctionName_) {}
|
||||
virtual ~ObjectFunctionNameNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitObjectFunctionNameNode(*this);
|
||||
};
|
||||
|
||||
gd::String type; // This could be removed if the type ("string", "number",
|
||||
// type supported by gd::ParameterMetadata::IsObject, types
|
||||
// supported by gd::ParameterMetadata::IsExpression or
|
||||
// "unknown") was stored in ExpressionMetadata.
|
||||
gd::String objectName;
|
||||
gd::String objectFunctionOrBehaviorName; ///< Behavior name if
|
||||
///`behaviorFunctionName` is not
|
||||
///empty.
|
||||
gd::String behaviorFunctionName; ///< If empty, then
|
||||
///objectFunctionOrBehaviorName is filled
|
||||
///with the behavior name.
|
||||
|
||||
ExpressionParserLocation
|
||||
objectNameLocation; ///< Location of the object name.
|
||||
ExpressionParserLocation
|
||||
objectNameDotLocation; ///< Location of the "." after the object name.
|
||||
ExpressionParserLocation objectFunctionOrBehaviorNameLocation; ///< Location
|
||||
///of object
|
||||
///function
|
||||
///name or
|
||||
///behavior
|
||||
///name.
|
||||
ExpressionParserLocation
|
||||
behaviorNameNamespaceSeparatorLocation; ///< Location of the "::"
|
||||
///separator, if any.
|
||||
ExpressionParserLocation behaviorFunctionNameLocation; ///< Location of the
|
||||
///behavior function
|
||||
///name, if any.
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A function call node (either free function, object function or object
|
||||
* behavior function).
|
||||
* For example: "MyExtension::MyFunction(1, 2)", "MyObject.Function()" or
|
||||
* "MyObject.Physics::LinearVelocity()".
|
||||
*/
|
||||
struct FunctionCallNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
FunctionCallNode(const gd::String &type_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
const gd::String &functionName_)
|
||||
: type(type_),
|
||||
parameters(std::move(parameters_)),
|
||||
expressionMetadata(expressionMetadata_),
|
||||
functionName(functionName_){};
|
||||
FunctionNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
const gd::String &functionName_)
|
||||
FunctionCallNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
const gd::String &functionName_)
|
||||
: type(type_),
|
||||
objectName(objectName_),
|
||||
parameters(std::move(parameters_)),
|
||||
expressionMetadata(expressionMetadata_),
|
||||
functionName(functionName_){};
|
||||
FunctionNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
const gd::String &behaviorName_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
const gd::String &functionName_)
|
||||
FunctionCallNode(const gd::String &type_,
|
||||
const gd::String &objectName_,
|
||||
const gd::String &behaviorName_,
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
|
||||
const ExpressionMetadata &expressionMetadata_,
|
||||
const gd::String &functionName_)
|
||||
: type(type_),
|
||||
objectName(objectName_),
|
||||
behaviorName(behaviorName_),
|
||||
parameters(std::move(parameters_)),
|
||||
expressionMetadata(expressionMetadata_),
|
||||
functionName(functionName_){};
|
||||
virtual ~FunctionNode(){};
|
||||
virtual ~FunctionCallNode(){};
|
||||
virtual void Visit(ExpressionParser2NodeWorker &worker) {
|
||||
worker.OnVisitFunctionNode(*this);
|
||||
worker.OnVisitFunctionCallNode(*this);
|
||||
};
|
||||
|
||||
gd::String type; // This could be removed if the type ("string" or "number")
|
||||
// was stored in ExpressionMetadata.
|
||||
gd::String type; // This could be removed if the type ("string", "number",
|
||||
// type supported by gd::ParameterMetadata::IsObject, types
|
||||
// supported by gd::ParameterMetadata::IsExpression or
|
||||
// "unknown") was stored in ExpressionMetadata.
|
||||
gd::String objectName;
|
||||
gd::String behaviorName;
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters;
|
||||
const ExpressionMetadata &expressionMetadata;
|
||||
gd::String functionName;
|
||||
|
||||
ExpressionParserLocation
|
||||
functionNameLocation; ///< Location of the function name.
|
||||
ExpressionParserLocation
|
||||
objectNameLocation; ///< Location of the object name, if any.
|
||||
ExpressionParserLocation
|
||||
objectNameDotLocation; ///< Location of the "." after the object name.
|
||||
ExpressionParserLocation
|
||||
behaviorNameLocation; ///< Location of the behavior name, if any.
|
||||
ExpressionParserLocation
|
||||
behaviorNameNamespaceSeparatorLocation; ///< Location of the "::"
|
||||
///separator, if any.
|
||||
ExpressionParserLocation
|
||||
openingParenthesisLocation; ///< Location of the "(".
|
||||
ExpressionParserLocation
|
||||
closingParenthesisLocation; ///< Location of the ")".
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief An empty node, used when parsing failed/a syntax error was
|
||||
* encountered and any other node could not make sense.
|
||||
*/
|
||||
struct EmptyNode : public FunctionOrEmptyNode {
|
||||
struct EmptyNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
|
||||
EmptyNode(const gd::String &type_, const gd::String &text_ = "")
|
||||
: type(type_), text(text_){};
|
||||
virtual ~EmptyNode(){};
|
||||
@@ -285,10 +401,12 @@ struct EmptyNode : public FunctionOrEmptyNode {
|
||||
worker.OnVisitEmptyNode(*this);
|
||||
};
|
||||
|
||||
gd::String type;
|
||||
gd::String type; // "string", "number", type supported by
|
||||
// gd::ParameterMetadata::IsObject, types supported by
|
||||
// gd::ParameterMetadata::IsExpression or "unknown".
|
||||
gd::String text;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@@ -92,7 +92,15 @@ class GD_CORE_API ExpressionParser2NodePrinter
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
output += node.identifierName;
|
||||
}
|
||||
void OnVisitFunctionNode(FunctionNode& node) override {
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (!node.behaviorFunctionName.empty()) {
|
||||
output +=
|
||||
node.objectName + "." + node.objectFunctionOrBehaviorName + "::" + node.behaviorFunctionName;
|
||||
} else {
|
||||
output += node.objectName + "." + node.objectFunctionOrBehaviorName;
|
||||
}
|
||||
};
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (!node.behaviorName.empty()) {
|
||||
output +=
|
||||
node.objectName + "." + node.behaviorName + "::" + node.functionName;
|
||||
|
@@ -16,10 +16,11 @@ class TextNode;
|
||||
class VariableNode;
|
||||
class VariableAccessorNode;
|
||||
class VariableBracketAccessorNode;
|
||||
class IdentifierOrFunctionOrEmptyNode;
|
||||
class IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode;
|
||||
class IdentifierNode;
|
||||
class FunctionOrEmptyNode;
|
||||
class FunctionNode;
|
||||
class FunctionCallOrObjectFunctionNameOrEmptyNode;
|
||||
class ObjectFunctionNameNode;
|
||||
class FunctionCallNode;
|
||||
class EmptyNode;
|
||||
} // namespace gd
|
||||
|
||||
@@ -42,10 +43,11 @@ class GD_CORE_API ExpressionParser2NodeWorker {
|
||||
friend class VariableNode;
|
||||
friend class VariableAccessorNode;
|
||||
friend class VariableBracketAccessorNode;
|
||||
friend class IdentifierOrFunctionOrEmptyNode;
|
||||
friend class IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode;
|
||||
friend class IdentifierNode;
|
||||
friend class FunctionOrEmptyNode;
|
||||
friend class FunctionNode;
|
||||
friend class FunctionCallOrObjectFunctionNameOrEmptyNode;
|
||||
friend class ObjectFunctionNameNode;
|
||||
friend class FunctionCallNode;
|
||||
friend class EmptyNode;
|
||||
|
||||
public:
|
||||
@@ -62,10 +64,11 @@ class GD_CORE_API ExpressionParser2NodeWorker {
|
||||
virtual void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) = 0;
|
||||
virtual void OnVisitIdentifierNode(IdentifierNode& node) = 0;
|
||||
virtual void OnVisitFunctionNode(FunctionNode& node) = 0;
|
||||
virtual void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) = 0;
|
||||
virtual void OnVisitFunctionCallNode(FunctionCallNode& node) = 0;
|
||||
virtual void OnVisitEmptyNode(EmptyNode& node) = 0;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Events/Parsers/VariableParser.h"
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Layout;
|
||||
}
|
||||
namespace gd {
|
||||
class Project;
|
||||
}
|
||||
namespace gd {
|
||||
class Platform;
|
||||
}
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
VariableParser::~VariableParser() {}
|
||||
|
||||
bool VariableParser::Parse(VariableParserCallbacks& callbacks_) {
|
||||
callbacks = &callbacks_;
|
||||
rootVariableParsed = false;
|
||||
firstErrorStr.clear();
|
||||
firstErrorPos = 0;
|
||||
currentPositionIt = expression.begin();
|
||||
currentTokenType = TS_INVALID;
|
||||
currentToken.clear();
|
||||
S();
|
||||
|
||||
return firstErrorStr == "";
|
||||
}
|
||||
|
||||
void VariableParser::ReadToken() {
|
||||
currentTokenType = TS_INVALID;
|
||||
currentToken.clear();
|
||||
while (currentPositionIt != expression.end()) {
|
||||
char32_t currentChar = *currentPositionIt;
|
||||
if (currentChar == U'[' || currentChar == U']' || currentChar == U'.') {
|
||||
if (currentTokenType == TS_VARNAME)
|
||||
return; // We've parsed a variable name.
|
||||
}
|
||||
|
||||
if (currentChar == U'[') {
|
||||
currentTokenType = TS_OPENING_BRACKET;
|
||||
currentToken.clear();
|
||||
++currentPositionIt;
|
||||
return;
|
||||
} else if (currentChar == U']') {
|
||||
currentTokenType = TS_CLOSING_BRACKET;
|
||||
currentToken.clear();
|
||||
++currentPositionIt;
|
||||
return;
|
||||
} else if (currentChar == U'.') {
|
||||
currentTokenType = TS_PERIOD;
|
||||
currentToken.clear();
|
||||
++currentPositionIt;
|
||||
return;
|
||||
}
|
||||
|
||||
currentTokenType = TS_VARNAME; // We're parsing a variable name.
|
||||
currentToken.push_back(currentChar);
|
||||
++currentPositionIt;
|
||||
}
|
||||
|
||||
// Can be reached if we are at the end of the expression. In this case,
|
||||
// currentTokenType will be either TS_VARNAME or TS_INVALID.
|
||||
}
|
||||
|
||||
void VariableParser::S() {
|
||||
ReadToken();
|
||||
if (currentTokenType != TS_VARNAME) {
|
||||
firstErrorStr = _("Expecting a variable name.");
|
||||
firstErrorPos = std::distance<gd::String::const_iterator>(
|
||||
expression.begin(), currentPositionIt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rootVariableParsed) {
|
||||
rootVariableParsed = true;
|
||||
if (callbacks) callbacks->OnRootVariable(currentToken);
|
||||
} else if (callbacks)
|
||||
callbacks->OnChildVariable(currentToken);
|
||||
|
||||
X();
|
||||
}
|
||||
|
||||
void VariableParser::X() {
|
||||
ReadToken();
|
||||
if (currentTokenType == TS_INVALID)
|
||||
return; // Ended parsing.
|
||||
else if (currentTokenType == TS_PERIOD)
|
||||
S();
|
||||
else if (currentTokenType == TS_OPENING_BRACKET) {
|
||||
gd::String strExpr = SkipStringExpression();
|
||||
|
||||
ReadToken();
|
||||
if (currentTokenType != TS_CLOSING_BRACKET) {
|
||||
firstErrorStr = _("Expecting ]");
|
||||
firstErrorPos = std::distance<gd::String::const_iterator>(
|
||||
expression.begin(), currentPositionIt);
|
||||
return;
|
||||
}
|
||||
if (callbacks) callbacks->OnChildSubscript(strExpr);
|
||||
X();
|
||||
}
|
||||
}
|
||||
|
||||
gd::String VariableParser::SkipStringExpression() {
|
||||
gd::String stringExpression;
|
||||
bool insideStringLiteral = false;
|
||||
bool lastCharacterWasBackslash = false;
|
||||
unsigned int nestedBracket = 0;
|
||||
while (currentPositionIt != expression.end()) {
|
||||
char32_t currentChar = *currentPositionIt;
|
||||
if (currentChar == U'\"') {
|
||||
if (!insideStringLiteral)
|
||||
insideStringLiteral = true;
|
||||
else if (!lastCharacterWasBackslash)
|
||||
insideStringLiteral = false;
|
||||
} else if (currentChar == U'[' && !insideStringLiteral) {
|
||||
nestedBracket++;
|
||||
} else if (currentChar == U']' && !insideStringLiteral) {
|
||||
if (nestedBracket == 0)
|
||||
return stringExpression; // Found the end of the string litteral.
|
||||
nestedBracket--;
|
||||
}
|
||||
|
||||
lastCharacterWasBackslash = currentChar == U'\\';
|
||||
stringExpression.push_back(currentChar);
|
||||
++currentPositionIt;
|
||||
}
|
||||
|
||||
// End of the expression reached (so expression is invalid by the way)
|
||||
return stringExpression;
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_VARIABLEPARSER_H
|
||||
#define GDCORE_VARIABLEPARSER_H
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Layout;
|
||||
}
|
||||
namespace gd {
|
||||
class Project;
|
||||
}
|
||||
namespace gd {
|
||||
class Platform;
|
||||
}
|
||||
namespace gd {
|
||||
class VariableParserCallbacks;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/** \brief Parse a variable expression.
|
||||
*
|
||||
* Parse an variable expression ( like
|
||||
myVariable.child["subchild"+ToString(i)].subsubchild ),
|
||||
* calling callbacks when a token is reached.
|
||||
*
|
||||
* Usage example:
|
||||
\code
|
||||
//...
|
||||
|
||||
//VariableCodeGenerationCallbacks is a class inheriting from
|
||||
gd::VariableParserCallbacks VariableCodeGenerationCallbacks callbacks(output,
|
||||
*this, context, VariableCodeGenerationCallbacks::PROJECT_VARIABLE);
|
||||
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter <<
|
||||
endl;
|
||||
\endcode
|
||||
*
|
||||
* Here is the parsed grammar: <br>
|
||||
* S -> VarName X <br>
|
||||
* X -> e | . S | [StringExpression] X <br>
|
||||
*
|
||||
* where e = nothing (end of expression), StringExpression = A valid string
|
||||
expression and
|
||||
* S is the start.
|
||||
*
|
||||
* \see gd::VariableParserCallbacks
|
||||
*/
|
||||
class GD_CORE_API VariableParser {
|
||||
public:
|
||||
/**
|
||||
* \brief Default constructor
|
||||
* \param expressionPlainString The string representing the expression to be
|
||||
* parsed.
|
||||
*/
|
||||
VariableParser(const gd::String& expressionPlainString_)
|
||||
: currentPositionIt(), expression(expressionPlainString_){};
|
||||
virtual ~VariableParser();
|
||||
|
||||
/**
|
||||
* Parse the expression, calling each callback when necessary.
|
||||
* \param callbacks The callbacks to be called.
|
||||
* \return true if expression was correctly parsed.
|
||||
* \see gd::VariableParserCallbacks
|
||||
*/
|
||||
bool Parse(VariableParserCallbacks& callbacks);
|
||||
|
||||
/**
|
||||
* \brief Return the description of the error that was found
|
||||
*/
|
||||
const gd::String& GetFirstError() { return firstErrorStr; }
|
||||
|
||||
/**
|
||||
* \brief Return the position of the error that was found
|
||||
* \return The position, or gd::String::npos if no error is found
|
||||
*/
|
||||
size_t GetFirstErrorPosition() { return firstErrorPos; }
|
||||
|
||||
gd::String firstErrorStr;
|
||||
size_t firstErrorPos;
|
||||
|
||||
private:
|
||||
void S();
|
||||
void X();
|
||||
|
||||
/**
|
||||
* \brief Skip the string expression, starting from the current position.
|
||||
* \return The string expression skipped. currentPosition is now put on the
|
||||
* closing bracket.
|
||||
*/
|
||||
gd::String SkipStringExpression();
|
||||
|
||||
void ReadToken();
|
||||
|
||||
enum TokenType {
|
||||
TS_PERIOD,
|
||||
TS_OPENING_BRACKET,
|
||||
TS_CLOSING_BRACKET,
|
||||
TS_VARNAME,
|
||||
TS_INVALID
|
||||
};
|
||||
|
||||
TokenType currentTokenType;
|
||||
gd::String currentToken;
|
||||
gd::String::const_iterator currentPositionIt;
|
||||
gd::String expression;
|
||||
|
||||
VariableParserCallbacks* callbacks;
|
||||
bool rootVariableParsed;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Callbacks called by VariableParser when parsing a variable expression.
|
||||
*/
|
||||
class GD_CORE_API VariableParserCallbacks {
|
||||
public:
|
||||
/**
|
||||
* \brief Called when the first variable has been parsed. ( varName1 in
|
||||
* varName1.child for example. ) \param variableName The variable name.
|
||||
*/
|
||||
virtual void OnRootVariable(gd::String variableName) = 0;
|
||||
|
||||
/**
|
||||
* \brief Called when accessing the child of a structure variable. ( child in
|
||||
* varName1.child for example. ) \param variableName The child variable name.
|
||||
*/
|
||||
virtual void OnChildVariable(gd::String variableName) = 0;
|
||||
|
||||
/**
|
||||
* \brief Called when accessing the child of a structure variable using a
|
||||
* string expression in square brackets. ( "subscript" in
|
||||
* varName1["subscript"] for example. )
|
||||
*
|
||||
* \param variableName The expression used to access the child variable.
|
||||
*/
|
||||
virtual void OnChildSubscript(gd::String stringExpression) = 0;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDEXPRESSIONPARSER_H
|
@@ -8,10 +8,15 @@
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
EventsCodeNameMangler *EventsCodeNameMangler::_singleton = NULL;
|
||||
EventsCodeNameMangler *EventsCodeNameMangler::_singleton = nullptr;
|
||||
|
||||
gd::String EventsCodeNameMangler::GetMangledObjectsListName(
|
||||
const gd::String& EventsCodeNameMangler::GetMangledObjectsListName(
|
||||
const gd::String &originalObjectName) {
|
||||
auto it = mangledObjectNames.find(originalObjectName);
|
||||
if (it != mangledObjectNames.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
gd::String partiallyMangledName = originalObjectName;
|
||||
static const gd::String allowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
@@ -27,11 +32,17 @@ gd::String EventsCodeNameMangler::GetMangledObjectsListName(
|
||||
}
|
||||
}
|
||||
|
||||
return "GD" + partiallyMangledName + "Objects";
|
||||
mangledObjectNames[originalObjectName] = "GD" + partiallyMangledName + "Objects";
|
||||
return mangledObjectNames[originalObjectName];
|
||||
}
|
||||
|
||||
gd::String EventsCodeNameMangler::GetExternalEventsFunctionMangledName(
|
||||
const gd::String& EventsCodeNameMangler::GetExternalEventsFunctionMangledName(
|
||||
const gd::String &externalEventsName) {
|
||||
auto it = mangledExternalEventsNames.find(externalEventsName);
|
||||
if (it != mangledExternalEventsNames.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
gd::String partiallyMangledName = externalEventsName;
|
||||
static const gd::String allowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
@@ -47,23 +58,24 @@ gd::String EventsCodeNameMangler::GetExternalEventsFunctionMangledName(
|
||||
}
|
||||
}
|
||||
|
||||
return "GDExternalEvents" + partiallyMangledName;
|
||||
mangledExternalEventsNames[externalEventsName] = "GDExternalEvents" + partiallyMangledName;
|
||||
return mangledExternalEventsNames[externalEventsName];
|
||||
}
|
||||
|
||||
gd::String ManObjListName(const gd::String &objectName) {
|
||||
const gd::String& ManObjListName(const gd::String &objectName) {
|
||||
return EventsCodeNameMangler::Get()->GetMangledObjectsListName(objectName);
|
||||
}
|
||||
|
||||
EventsCodeNameMangler *EventsCodeNameMangler::Get() {
|
||||
if (NULL == _singleton) _singleton = new EventsCodeNameMangler;
|
||||
if (nullptr == _singleton) _singleton = new EventsCodeNameMangler;
|
||||
|
||||
return (static_cast<EventsCodeNameMangler *>(_singleton));
|
||||
}
|
||||
|
||||
void EventsCodeNameMangler::DestroySingleton() {
|
||||
if (NULL != _singleton) {
|
||||
if (nullptr != _singleton) {
|
||||
delete _singleton;
|
||||
_singleton = NULL;
|
||||
_singleton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -6,26 +6,34 @@
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#ifndef EVENTSCODENAMEMANGLER_H
|
||||
#define EVENTSCODENAMEMANGLER_H
|
||||
#include <unordered_map>
|
||||
#include "GDCore/String.h"
|
||||
|
||||
/**
|
||||
* Manage name mangling, so as to ensure all names used in code are valid.
|
||||
* \brief Mangle object names, so as to ensure all names used in code are valid.
|
||||
*
|
||||
* \see ManObjListName
|
||||
*/
|
||||
class GD_CORE_API EventsCodeNameMangler {
|
||||
public:
|
||||
/**
|
||||
* Get the mangled name from a name : All characters that are not 0-9, a-z,
|
||||
* Get the mangled name from a name: All characters that are not 0-9, a-z,
|
||||
* A-Z or _ are replaced by "_"+AsciiCodeOfTheCharacter.
|
||||
*
|
||||
* The mangled name is memoized as this is intensively used during project
|
||||
* export and events code generation.
|
||||
*/
|
||||
gd::String GetMangledObjectsListName(const gd::String &originalObjectName);
|
||||
const gd::String &GetMangledObjectsListName(
|
||||
const gd::String &originalObjectName);
|
||||
|
||||
/**
|
||||
* Get the mangled function name to be used to call external events named \a
|
||||
* externalEventsName.
|
||||
*
|
||||
* The mangled name is memoized as this is intensively used during project
|
||||
* export and events code generation.
|
||||
*/
|
||||
gd::String GetExternalEventsFunctionMangledName(
|
||||
const gd::String &GetExternalEventsFunctionMangledName(
|
||||
const gd::String &externalEventsName);
|
||||
|
||||
static EventsCodeNameMangler *Get();
|
||||
@@ -35,14 +43,22 @@ class GD_CORE_API EventsCodeNameMangler {
|
||||
EventsCodeNameMangler(){};
|
||||
virtual ~EventsCodeNameMangler(){};
|
||||
static EventsCodeNameMangler *_singleton;
|
||||
|
||||
std::unordered_map<gd::String, gd::String>
|
||||
mangledObjectNames; ///< Memoized results of mangling for objects
|
||||
std::unordered_map<gd::String, gd::String>
|
||||
mangledExternalEventsNames; ///< Memoized results of mangling for
|
||||
/// external events
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut to
|
||||
* EventsCodeNameMangler::Get()->GetMangledObjectsListName(objectName). \see
|
||||
* EventsCodeNameMangler \return Mangled object name
|
||||
* Shortcut for
|
||||
* `EventsCodeNameMangler::Get()->GetMangledObjectsListName(objectName)`.
|
||||
*
|
||||
* \see EventsCodeNameMangler
|
||||
* \return Mangled object name
|
||||
*/
|
||||
gd::String GD_CORE_API ManObjListName(const gd::String &objectName);
|
||||
const gd::String &GD_CORE_API ManObjListName(const gd::String &objectName);
|
||||
|
||||
#endif // EVENTSCODENAMEMANGLER_H
|
||||
#endif
|
||||
|
@@ -13,11 +13,12 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("BuiltinObject",
|
||||
_("Base object"),
|
||||
_("Base object"),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"BuiltinObject",
|
||||
_("Features for all objects"),
|
||||
_("Common features that can be used for all objects in GDevelop."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/base_object/events");
|
||||
|
||||
gd::ObjectMetadata& obj = extension.AddObject<gd::Object>(
|
||||
@@ -72,14 +73,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.UseStandardOperatorParameters("number")
|
||||
.MarkAsSimple();
|
||||
|
||||
obj.AddAction(
|
||||
"MettreXY",
|
||||
_("Position of an object"),
|
||||
_("Change the position of an object."),
|
||||
_("Change the position of _PARAM0_: _PARAM1_ _PARAM2_ (x axis), _PARAM3_ _PARAM4_ (y axis)"),
|
||||
_("Position"),
|
||||
"res/actions/position24.png",
|
||||
"res/actions/position.png")
|
||||
obj.AddAction("MettreXY",
|
||||
_("Position of an object"),
|
||||
_("Change the position of an object."),
|
||||
_("Change the position of _PARAM0_: _PARAM1_ _PARAM2_ (x "
|
||||
"axis), _PARAM3_ _PARAM4_ (y axis)"),
|
||||
_("Position"),
|
||||
"res/actions/position24.png",
|
||||
"res/actions/position.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("operator", _("Modification's sign"))
|
||||
@@ -294,14 +295,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("objectvar", _("Variable"))
|
||||
.UseStandardOperatorParameters("number");
|
||||
|
||||
obj.AddAction(
|
||||
"ModVarObjetTxt",
|
||||
_("Modify the text of a variable of an object"),
|
||||
_("Modify the text of a variable of an object"),
|
||||
_("the text of variable _PARAM1_"),
|
||||
_("Variables"),
|
||||
"res/actions/var24.png",
|
||||
"res/actions/var.png")
|
||||
obj.AddAction("ModVarObjetTxt",
|
||||
_("Modify the text of a variable of an object"),
|
||||
_("Modify the text of a variable of an object"),
|
||||
_("the text of variable _PARAM1_"),
|
||||
_("Variables"),
|
||||
"res/actions/var24.png",
|
||||
"res/actions/var.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectvar", _("Variable"))
|
||||
@@ -473,14 +473,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("objectvar", _("Variable"))
|
||||
.UseStandardRelationalOperatorParameters("number");
|
||||
|
||||
obj.AddCondition(
|
||||
"VarObjetTxt",
|
||||
_("Text of an object's variable"),
|
||||
_("Compare the text of a variable of an object."),
|
||||
_("the text of variable _PARAM1_"),
|
||||
_("Variables"),
|
||||
"res/conditions/var24.png",
|
||||
"res/conditions/var.png")
|
||||
obj.AddCondition("VarObjetTxt",
|
||||
_("Text of an object's variable"),
|
||||
_("Compare the text of a variable of an object."),
|
||||
_("the text of variable _PARAM1_"),
|
||||
_("Variables"),
|
||||
"res/conditions/var24.png",
|
||||
"res/conditions/var.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectvar", _("Variable"))
|
||||
@@ -634,6 +633,23 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("expression", _("Y position of the point"))
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddCondition("SourisSurObjet",
|
||||
_("The cursor/touch is on an object"),
|
||||
_("Test if the cursor is over an object, or if the object "
|
||||
"is being touched."),
|
||||
_("The cursor/touch is on _PARAM0_"),
|
||||
_("Mouse and touch"),
|
||||
"res/conditions/surObjet24.png",
|
||||
"res/conditions/surObjet.png")
|
||||
|
||||
.AddParameter("objectList", _("Object"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("yesorno", _("Accurate test (yes by default)"), "", true)
|
||||
.SetDefaultValue("yes")
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsSimple();
|
||||
|
||||
obj.AddCondition(
|
||||
"ObjectTimer",
|
||||
_("Value of a timer"),
|
||||
|
@@ -24,7 +24,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.AddCondition("CameraX",
|
||||
_("Camera center X position"),
|
||||
_("Compare the X position of the center of a camera."),
|
||||
_("the x position of camera _PARAM4_ (layer: _PARAM3_)"),
|
||||
_("the X position of camera _PARAM4_ (layer: _PARAM3_)"),
|
||||
_("Layers and cameras"),
|
||||
"res/conditions/camera24.png",
|
||||
"res/conditions/camera.png")
|
||||
@@ -57,7 +57,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
"CameraX",
|
||||
_("Camera center X position"),
|
||||
_("Change the X position of the center of the specified camera."),
|
||||
_("the x position of camera _PARAM4_ (layer: _PARAM3_)"),
|
||||
_("the X position of camera _PARAM4_ (layer: _PARAM3_)"),
|
||||
_("Layers and cameras"),
|
||||
"res/conditions/camera24.png",
|
||||
"res/conditions/camera.png")
|
||||
@@ -74,7 +74,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
"CameraY",
|
||||
_("Camera center Y position"),
|
||||
_("Change the Y position of the center of the specified camera."),
|
||||
_("the y position of camera _PARAM4_ (layer: _PARAM3_)"),
|
||||
_("the Y position of camera _PARAM4_ (layer: _PARAM3_)"),
|
||||
_("Layers and cameras"),
|
||||
"res/conditions/camera24.png",
|
||||
"res/conditions/camera.png")
|
||||
|
@@ -21,23 +21,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.SetExtensionHelpPath("/all-features/mouse-touch");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
extension
|
||||
.AddCondition("SourisSurObjet",
|
||||
_("The cursor/touch is on an object"),
|
||||
_("Test if the cursor is over an object, or if the object "
|
||||
"is being touched."),
|
||||
_("The cursor/touch is on _PARAM0_"),
|
||||
_("Mouse and touch"),
|
||||
"res/conditions/surObjet24.png",
|
||||
"res/conditions/surObjet.png")
|
||||
|
||||
.AddParameter("objectList", _("Object"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("yesorno", _("Accurate test (yes by default)"), "", true)
|
||||
.SetDefaultValue("yes")
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"IsMouseWheelScrollingUp",
|
||||
|
@@ -380,7 +380,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
obj.AddAction("ChangeBlendMode",
|
||||
_("Blend mode"),
|
||||
_("Change the number of the blend mode of an object.\nThe "
|
||||
"default blend mode is 0 (Alpha)."),
|
||||
"default blend mode is 0 (Normal)."),
|
||||
_("Change Blend mode of _PARAM0_ to _PARAM1_"),
|
||||
_("Effects"),
|
||||
"res/actions/color24.png",
|
||||
@@ -388,7 +388,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
|
||||
.AddParameter("object", _("Object"), "Sprite")
|
||||
.AddParameter("expression",
|
||||
_("Mode (0 : Alpha, 1 : Add, 2 : Multiply, 3 : None)"))
|
||||
_("Mode (0: Normal, 1: Add, 2: Multiply, 3: Screen)"))
|
||||
.MarkAsSimple();
|
||||
|
||||
obj.AddAction("FlipX",
|
||||
|
@@ -121,16 +121,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
|
||||
extension
|
||||
.AddExpression("TimeDelta",
|
||||
_("Time elapsed since the last image"),
|
||||
_("Time elapsed since the last image"),
|
||||
_("Time elapsed since the last frame"),
|
||||
_("Time elapsed since the last frame rendered on screen"),
|
||||
_("Time"),
|
||||
"res/actions/time.png")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddExpression("TempsFrame",
|
||||
_("Time elapsed since the last image"),
|
||||
_("Time elapsed since the last image"),
|
||||
_("Time elapsed since the last frame"),
|
||||
_("Time elapsed since the last frame rendered on screen"),
|
||||
_("Time"),
|
||||
"res/actions/time.png")
|
||||
.SetHidden()
|
||||
@@ -138,8 +138,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
|
||||
extension
|
||||
.AddExpression("ElapsedTime",
|
||||
_("Time elapsed since the last image"),
|
||||
_("Time elapsed since the last image"),
|
||||
_("Time elapsed since the last frame"),
|
||||
_("Time elapsed since the last frame rendered on screen"),
|
||||
_("Time"),
|
||||
"res/actions/time.png")
|
||||
.SetHidden()
|
||||
|
@@ -14,19 +14,19 @@ using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
Platform::Platform() {}
|
||||
Platform::Platform(): enableExtensionLoadingLogs(true) {}
|
||||
|
||||
Platform::~Platform() {}
|
||||
|
||||
bool Platform::AddExtension(std::shared_ptr<gd::PlatformExtension> extension) {
|
||||
if (!extension) return false;
|
||||
|
||||
std::cout << "Loading " << extension->GetName() << "...";
|
||||
if (enableExtensionLoadingLogs) std::cout << "Loading " << extension->GetName() << "...";
|
||||
if (IsExtensionLoaded(extension->GetName())) {
|
||||
std::cout << " (replacing existing extension)";
|
||||
if (enableExtensionLoadingLogs) std::cout << " (replacing existing extension)";
|
||||
RemoveExtension(extension->GetName());
|
||||
}
|
||||
std::cout << std::endl;
|
||||
if (enableExtensionLoadingLogs) std::cout << std::endl;
|
||||
|
||||
extensionsLoaded.push_back(extension);
|
||||
|
||||
|
@@ -156,6 +156,12 @@ class GD_CORE_API Platform {
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \brief Activate or disable the logs on the standard output when
|
||||
* loading an extension.
|
||||
*/
|
||||
void EnableExtensionLoadingLogs(bool enable) { enableExtensionLoadingLogs = enable; };
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE is about to shut down: Take this opportunity for
|
||||
* erasing for example any temporary file.
|
||||
@@ -174,6 +180,7 @@ class GD_CORE_API Platform {
|
||||
extensionsLoaded; ///< Extensions of the platform
|
||||
std::map<gd::String, CreateFunPtr>
|
||||
creationFunctionTable; ///< Creation functions for objects
|
||||
bool enableExtensionLoadingLogs;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -63,7 +63,16 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
context.AddObjectName(node.identifierName);
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionNode(FunctionNode& node) override {
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (!node.objectName.empty()) {
|
||||
context.AddObjectName(node.objectName);
|
||||
|
||||
if (!node.behaviorFunctionName.empty()) {
|
||||
context.AddBehaviorName(node.objectName, node.objectFunctionOrBehaviorName);
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (!node.objectName.empty()) {
|
||||
context.AddObjectName(node.objectName);
|
||||
|
||||
|
@@ -35,7 +35,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
virtual ~ExpressionObjectRenamer(){};
|
||||
|
||||
static bool Rename(gd::ExpressionNode & node, const gd::String& objectName, const gd::String& objectNewName) {
|
||||
if (ExpressionValidator::HasNoErrors(node)) {
|
||||
if (ExpressionValidator::HasNoErrors(node)) {
|
||||
ExpressionObjectRenamer renamer(objectName, objectNewName);
|
||||
node.Visit(renamer);
|
||||
|
||||
@@ -77,7 +77,13 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
node.identifierName = objectNewName;
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionNode(FunctionNode& node) override {
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (node.objectName == objectName) {
|
||||
hasDoneRenaming = true;
|
||||
node.objectName = objectNewName;
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (node.objectName == objectName) {
|
||||
hasDoneRenaming = true;
|
||||
node.objectName = objectNewName;
|
||||
@@ -107,7 +113,7 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
virtual ~ExpressionObjectFinder(){};
|
||||
|
||||
static bool CheckIfHasObject(gd::ExpressionNode & node, const gd::String & objectName) {
|
||||
if (ExpressionValidator::HasNoErrors(node)) {
|
||||
if (ExpressionValidator::HasNoErrors(node)) {
|
||||
ExpressionObjectFinder finder(objectName);
|
||||
node.Visit(finder);
|
||||
|
||||
@@ -148,7 +154,12 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
hasObject = true;
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionNode(FunctionNode& node) override {
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (node.objectName == objectName) {
|
||||
hasObject = true;
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (node.objectName == objectName) {
|
||||
hasObject = true;
|
||||
}
|
||||
@@ -184,7 +195,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("number", actions[aId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
actions[aId].SetParameter(pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -194,7 +205,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("string", actions[aId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
actions[aId].SetParameter(pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -237,7 +248,7 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("number", conditions[cId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
conditions[cId].SetParameter(pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -247,7 +258,7 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("string", conditions[cId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
conditions[cId].SetParameter(pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -268,6 +279,43 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
return somethingModified;
|
||||
}
|
||||
|
||||
bool EventsRefactorer::RenameObjectInEventParameters(
|
||||
const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::Expression& expression,
|
||||
gd::ParameterMetadata parameterMetadata,
|
||||
gd::String oldName,
|
||||
gd::String newName) {
|
||||
bool somethingModified = false;
|
||||
|
||||
if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType()) &&
|
||||
expression.GetPlainString() == oldName)
|
||||
expression = gd::Expression(newName);
|
||||
// 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());
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*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());
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
|
||||
expression = ExpressionParser2NodePrinter::PrintNode(*node);
|
||||
}
|
||||
}
|
||||
|
||||
return somethingModified;
|
||||
}
|
||||
|
||||
void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
@@ -289,6 +337,15 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
|
||||
platform, project, layout, *actionsVectors[j], oldName, newName);
|
||||
}
|
||||
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata>> expressionsWithMetadata =
|
||||
events[i].GetAllExpressionsWithMetadata();
|
||||
for (std::size_t j = 0; j < expressionsWithMetadata.size(); ++j) {
|
||||
gd::Expression* expression = expressionsWithMetadata[j].first;
|
||||
gd::ParameterMetadata parameterMetadata = expressionsWithMetadata[j].second;
|
||||
bool somethingModified = RenameObjectInEventParameters(
|
||||
platform, project, layout, *expression, parameterMetadata, oldName, newName);
|
||||
}
|
||||
|
||||
if (events[i].CanHaveSubEvents())
|
||||
RenameObjectInEvents(platform,
|
||||
project,
|
||||
@@ -323,7 +380,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("number", actions[aId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
@@ -334,7 +391,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("string", actions[aId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
@@ -384,7 +441,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
"number", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("number", conditions[cId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
@@ -395,7 +452,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
"string", instrInfos.parameters[pNb].type)) {
|
||||
gd::ExpressionParser2 parser(platform, project, layout);
|
||||
auto node = parser.ParseExpression("string", conditions[cId].GetParameter(pNb).GetPlainString());
|
||||
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
@@ -595,7 +652,8 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
|
||||
gd::String search,
|
||||
bool matchCase,
|
||||
bool inConditions,
|
||||
bool inActions) {
|
||||
bool inActions,
|
||||
bool inEventStrings) {
|
||||
vector<EventsSearchResult> results;
|
||||
|
||||
for (std::size_t i = 0; i < events.size(); ++i) {
|
||||
@@ -631,6 +689,16 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
|
||||
}
|
||||
}
|
||||
|
||||
if (inEventStrings) {
|
||||
if (!eventAddedInResults &&
|
||||
SearchStringInEvent(project, layout, events[i], search, matchCase)) {
|
||||
results.push_back(EventsSearchResult(
|
||||
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
|
||||
&events,
|
||||
i));
|
||||
}
|
||||
}
|
||||
|
||||
if (events[i].CanHaveSubEvents()) {
|
||||
vector<EventsSearchResult> subResults =
|
||||
SearchInEvents(project,
|
||||
@@ -639,7 +707,8 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
|
||||
search,
|
||||
matchCase,
|
||||
inConditions,
|
||||
inActions);
|
||||
inActions,
|
||||
inEventStrings);
|
||||
std::copy(
|
||||
subResults.begin(), subResults.end(), std::back_inserter(results));
|
||||
}
|
||||
@@ -711,6 +780,22 @@ bool EventsRefactorer::SearchStringInConditions(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EventsRefactorer::SearchStringInEvent(gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::BaseEvent& event,
|
||||
gd::String search,
|
||||
bool matchCase) {
|
||||
for (gd::String str : event.GetAllSearchableStrings()) {
|
||||
if (matchCase) {
|
||||
if (str.find(search) != gd::String::npos) return true;
|
||||
} else {
|
||||
if (str.FindCaseInsensitive(search) != gd::String::npos) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EventsSearchResult::EventsSearchResult(std::weak_ptr<gd::BaseEvent> event_,
|
||||
gd::EventsList* eventsList_,
|
||||
std::size_t positionInList_)
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class EventsList;
|
||||
@@ -103,7 +104,8 @@ class GD_CORE_API EventsRefactorer {
|
||||
gd::String search,
|
||||
bool matchCase,
|
||||
bool inConditions,
|
||||
bool inAction);
|
||||
bool inActions,
|
||||
bool inEventStrings);
|
||||
|
||||
/**
|
||||
* Replace all occurrences of a gd::String in events
|
||||
@@ -145,6 +147,20 @@ class GD_CORE_API EventsRefactorer {
|
||||
gd::InstructionsList& instructions,
|
||||
gd::String oldName,
|
||||
gd::String newName);
|
||||
/**
|
||||
* Replace all occurrences of an object name by another name in an expression
|
||||
* with the specified metadata
|
||||
* ( include : objects or objects in math/text expressions ).
|
||||
*
|
||||
* \return true if something was modified.
|
||||
*/
|
||||
static bool RenameObjectInEventParameters(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::Expression& expression,
|
||||
gd::ParameterMetadata parameterMetadata,
|
||||
gd::String oldName,
|
||||
gd::String newName);
|
||||
|
||||
/**
|
||||
* Remove all conditions of the list using an object
|
||||
@@ -202,6 +218,11 @@ class GD_CORE_API EventsRefactorer {
|
||||
gd::InstructionsList& conditions,
|
||||
gd::String search,
|
||||
bool matchCase);
|
||||
static bool SearchStringInEvent(gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::BaseEvent& events,
|
||||
gd::String search,
|
||||
bool matchCase);
|
||||
|
||||
EventsRefactorer(){};
|
||||
};
|
||||
|
@@ -63,7 +63,8 @@ class GD_CORE_API ExpressionParameterSearcher
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {}
|
||||
void OnVisitFunctionNode(FunctionNode& node) override {
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
bool considerFunction = objectName.empty() || node.objectName == objectName;
|
||||
for (size_t i = 0; i < node.parameters.size() &&
|
||||
i < node.expressionMetadata.parameters.size();
|
||||
|
366
Core/GDCore/IDE/Events/ExpressionCompletionFinder.h
Normal file
366
Core/GDCore/IDE/Events/ExpressionCompletionFinder.h
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H
|
||||
#define GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Describe completions to be shown to the user.
|
||||
*
|
||||
* The IDE is responsible for actually *searching* and showing the completions -
|
||||
* this is only describing what must be listed.
|
||||
*/
|
||||
struct ExpressionCompletionDescription {
|
||||
public:
|
||||
/**
|
||||
* The different kind of completions that can be described.
|
||||
*/
|
||||
enum CompletionKind {
|
||||
Object,
|
||||
Behavior,
|
||||
Expression,
|
||||
Variable,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Create a completion for an object with the given prefix
|
||||
*/
|
||||
static ExpressionCompletionDescription ForObject(const gd::String& type_,
|
||||
const gd::String& prefix_) {
|
||||
return ExpressionCompletionDescription(Object, type_, prefix_);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create a completion for a behavior with the given prefix of
|
||||
* the specified object
|
||||
*/
|
||||
static ExpressionCompletionDescription ForBehavior(
|
||||
const gd::String& prefix_, const gd::String& objectName_) {
|
||||
return ExpressionCompletionDescription(Behavior, "", prefix_, objectName_);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create a completion for a variable with the given prefix
|
||||
*/
|
||||
static ExpressionCompletionDescription ForVariable(
|
||||
const gd::String& type_, const gd::String& prefix_) {
|
||||
return ExpressionCompletionDescription(Variable, type_, prefix_);
|
||||
}
|
||||
/**
|
||||
* \brief Create a completion for an expression (free, object or behavior
|
||||
* expression) with the given prefix
|
||||
*/
|
||||
static ExpressionCompletionDescription ForExpression(
|
||||
const gd::String& type_,
|
||||
const gd::String& prefix_,
|
||||
const gd::String& objectName_ = "",
|
||||
const gd::String& behaviorName_ = "") {
|
||||
return ExpressionCompletionDescription(
|
||||
Expression, type_, prefix_, objectName_, behaviorName_);
|
||||
}
|
||||
|
||||
/** Check if two description of completions are equal */
|
||||
bool operator==(const ExpressionCompletionDescription& other) const {
|
||||
return completionKind == other.completionKind && type == other.type &&
|
||||
prefix == other.prefix && objectName == other.objectName &&
|
||||
behaviorName == other.behaviorName;
|
||||
};
|
||||
|
||||
/** \brief Return the kind of the completion */
|
||||
CompletionKind GetCompletionKind() const { return completionKind; }
|
||||
|
||||
/**
|
||||
* \brief Return the type of the completion (same type as types supported in
|
||||
* expressions)
|
||||
* (in other words, for expression this is the type of what must be returned).
|
||||
*/
|
||||
const gd::String& GetType() const { return type; }
|
||||
|
||||
/**
|
||||
* \brief Return the prefix currently entered and that must be completed.
|
||||
*/
|
||||
const gd::String& GetPrefix() const { return prefix; }
|
||||
|
||||
/**
|
||||
* \brief Return the object name, if completing an object expression or a
|
||||
* behavior.
|
||||
*/
|
||||
const gd::String& GetObjectName() const { return objectName; }
|
||||
|
||||
/**
|
||||
* \brief Return the behavior name, if completing an object behavior
|
||||
* expression.
|
||||
*
|
||||
* \warning If completing a behavior, the behavior (partial) name is returned
|
||||
* by `GetPrefix`.
|
||||
*/
|
||||
const gd::String& GetBehaviorName() const { return behaviorName; }
|
||||
|
||||
/**
|
||||
* \brief Set if the completion description is exact, i.e: it's not used
|
||||
* to complete anything. Rather, it should display information about what is
|
||||
* described by the completion.
|
||||
*/
|
||||
ExpressionCompletionDescription& SetIsExact(bool isExact_) {
|
||||
isExact = isExact_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if the completion description is exact, i.e: it's not
|
||||
* used to complete anything. Rather, it should display information
|
||||
* about what is described by the completion.
|
||||
*/
|
||||
bool IsExact() const { return isExact; }
|
||||
|
||||
/** Default constructor, only to be used by Emscripten bindings. */
|
||||
ExpressionCompletionDescription() : completionKind(Object){};
|
||||
|
||||
private:
|
||||
ExpressionCompletionDescription(CompletionKind completionKind_,
|
||||
const gd::String& type_,
|
||||
const gd::String& prefix_,
|
||||
const gd::String& objectName_ = "",
|
||||
const gd::String& behaviorName_ = "")
|
||||
: completionKind(completionKind_),
|
||||
type(type_),
|
||||
prefix(prefix_),
|
||||
objectName(objectName_),
|
||||
behaviorName(behaviorName_),
|
||||
isExact(false) {}
|
||||
|
||||
CompletionKind completionKind;
|
||||
gd::String type;
|
||||
gd::String prefix;
|
||||
gd::String objectName;
|
||||
gd::String behaviorName;
|
||||
bool isExact;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Turn an ExpressionCompletionDescription to a string.
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
ExpressionCompletionDescription const& value) {
|
||||
os << "{ " << value.GetCompletionKind() << ", " << value.GetType() << ", "
|
||||
<< value.GetPrefix() << ", " << value.GetObjectName() << ", "
|
||||
<< value.GetBehaviorName() << ", "
|
||||
<< (value.IsExact() ? "exact" : "non-exact") << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the list of completion descriptions for an expression node.
|
||||
*
|
||||
* \see gd::ExpressionCompletionDescription
|
||||
*/
|
||||
class GD_CORE_API ExpressionCompletionFinder
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
/**
|
||||
* \brief Given the expression, find the node at the specified location
|
||||
* and returns completions for it.
|
||||
*/
|
||||
static std::vector<ExpressionCompletionDescription>
|
||||
GetCompletionDescriptionsFor(gd::ExpressionNode& node,
|
||||
size_t searchedPosition) {
|
||||
gd::ExpressionNode* nodeAtLocation =
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(node,
|
||||
searchedPosition);
|
||||
|
||||
if (nodeAtLocation == nullptr) {
|
||||
std::vector<ExpressionCompletionDescription> emptyCompletions;
|
||||
return emptyCompletions;
|
||||
}
|
||||
|
||||
gd::ExpressionCompletionFinder autocompletionProvider(searchedPosition);
|
||||
nodeAtLocation->Visit(autocompletionProvider);
|
||||
return autocompletionProvider.GetCompletionDescriptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the completions found for the visited node.
|
||||
*/
|
||||
const std::vector<ExpressionCompletionDescription>&
|
||||
GetCompletionDescriptions() {
|
||||
return completions;
|
||||
};
|
||||
|
||||
virtual ~ExpressionCompletionFinder(){};
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForObject(node.type, ""));
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpression(node.type, ""));
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForObject(node.type, ""));
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpression(node.type, ""));
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForObject(node.type, ""));
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpression(node.type, ""));
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override {
|
||||
// No completions
|
||||
}
|
||||
void OnVisitTextNode(TextNode& node) override {
|
||||
// No completions
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForVariable(node.type, node.name));
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
// No completions
|
||||
}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
// No completions
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
if (gd::ParameterMetadata::IsObject(node.type)) {
|
||||
// Only show completions of objects if an object is required
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type, node.identifierName));
|
||||
} else {
|
||||
// Show completions for expressions and objects otherwise.
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type, node.identifierName));
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type, node.identifierName));
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
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, node.objectName));
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForBehavior(
|
||||
node.objectFunctionOrBehaviorName, node.objectName));
|
||||
} else if (IsCaretOn(node.behaviorNameNamespaceSeparatorLocation) ||
|
||||
IsCaretOn(node.behaviorFunctionNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type,
|
||||
node.behaviorFunctionName,
|
||||
node.objectName,
|
||||
node.objectFunctionOrBehaviorName));
|
||||
}
|
||||
} else {
|
||||
// Object function or behavior name
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type, node.objectName));
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForBehavior(
|
||||
node.objectFunctionOrBehaviorName, node.objectName));
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type, node.objectFunctionOrBehaviorName, node.objectName));
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
bool isCaretOnParenthesis = IsCaretOn(node.openingParenthesisLocation) ||
|
||||
IsCaretOn(node.closingParenthesisLocation);
|
||||
|
||||
if (!node.behaviorName.empty()) {
|
||||
// Behavior function
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type, node.objectName));
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.behaviorNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForBehavior(
|
||||
node.behaviorName, node.objectName));
|
||||
} else {
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpression(node.type,
|
||||
node.functionName,
|
||||
node.objectName,
|
||||
node.behaviorName)
|
||||
.SetIsExact(isCaretOnParenthesis));
|
||||
}
|
||||
} else if (!node.objectName.empty()) {
|
||||
// Object function
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForObject(
|
||||
node.type, node.objectName));
|
||||
} else {
|
||||
// Add completions for behaviors, because we could imagine that the user
|
||||
// wants to move from an object function to a behavior function, and so
|
||||
// need behavior completions. Do this unless we're on the parenthesis
|
||||
// (at which point we're only showing informative message about the
|
||||
// function).
|
||||
if (!isCaretOnParenthesis) {
|
||||
completions.push_back(ExpressionCompletionDescription::ForBehavior(
|
||||
node.functionName, node.objectName));
|
||||
}
|
||||
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type, node.functionName, node.objectName)
|
||||
.SetIsExact(isCaretOnParenthesis));
|
||||
}
|
||||
} else {
|
||||
// Free function
|
||||
completions.push_back(ExpressionCompletionDescription::ForExpression(
|
||||
node.type, node.functionName)
|
||||
.SetIsExact(isCaretOnParenthesis));
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForObject(node.type, node.text));
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpression(node.type, node.text));
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsCaretOn(const ExpressionParserLocation& location,
|
||||
bool inclusive = false) {
|
||||
if (!location.IsValid()) return false;
|
||||
|
||||
return (location.GetStartPosition() <= searchedPosition &&
|
||||
((!inclusive && searchedPosition < location.GetEndPosition()) ||
|
||||
(inclusive && searchedPosition <= location.GetEndPosition())));
|
||||
}
|
||||
|
||||
ExpressionCompletionFinder(size_t searchedPosition_)
|
||||
: searchedPosition(searchedPosition_){};
|
||||
|
||||
std::vector<ExpressionCompletionDescription> completions;
|
||||
size_t searchedPosition;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H
|
126
Core/GDCore/IDE/Events/ExpressionNodeLocationFinder.h
Normal file
126
Core/GDCore/IDE/Events/ExpressionNodeLocationFinder.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EXPRESSIONNODELOCATIONFINDER_H
|
||||
#define GDCORE_EXPRESSIONNODELOCATIONFINDER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Find the deepest node at the specified location in an expression.
|
||||
*
|
||||
* \see gd::ExpressionParser2
|
||||
*/
|
||||
class GD_CORE_API ExpressionNodeLocationFinder
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
/**
|
||||
* \brief Initialize the finder to search at the specified position.
|
||||
*/
|
||||
ExpressionNodeLocationFinder(size_t searchedPosition_)
|
||||
: searchedPosition(searchedPosition_), foundNode(nullptr){};
|
||||
virtual ~ExpressionNodeLocationFinder(){};
|
||||
|
||||
/**
|
||||
* \brief Helper function to find the deepest node at the search position, if
|
||||
* any.
|
||||
*/
|
||||
static ExpressionNode* GetNodeAtPosition(gd::ExpressionNode& node,
|
||||
size_t searchedPosition) {
|
||||
gd::ExpressionNodeLocationFinder finder(searchedPosition);
|
||||
node.Visit(finder);
|
||||
return finder.GetNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the deepest node found at the search position, if any.
|
||||
*/
|
||||
ExpressionNode* GetNode() { return foundNode; };
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
CheckSearchPositionInNode(node);
|
||||
node.expression->Visit(*this);
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
if (CheckSearchPositionInNode(node)) {
|
||||
node.leftHandSide->Visit(*this);
|
||||
node.rightHandSide->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
CheckSearchPositionInNode(node);
|
||||
node.factor->Visit(*this);
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override {
|
||||
CheckSearchPositionInNode(node);
|
||||
}
|
||||
void OnVisitTextNode(TextNode& node) override {
|
||||
CheckSearchPositionInNode(node);
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
if (CheckSearchPositionInNode(node)) {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
if (CheckSearchPositionInNode(node)) {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
if (CheckSearchPositionInNode(node)) {
|
||||
node.expression->Visit(*this);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
CheckSearchPositionInNode(node);
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
CheckSearchPositionInNode(node);
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
CheckSearchPositionInNode(node);
|
||||
for (auto& parameter : node.parameters) {
|
||||
parameter->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
CheckSearchPositionInNode(node, /*inclusive=*/true);
|
||||
}
|
||||
|
||||
private:
|
||||
bool CheckSearchPositionInNode(ExpressionNode& node, bool inclusive = false) {
|
||||
if (node.location.GetStartPosition() <= searchedPosition &&
|
||||
((!inclusive && searchedPosition < node.location.GetEndPosition()) ||
|
||||
(inclusive && searchedPosition <= node.location.GetEndPosition()))) {
|
||||
foundNode = &node;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t searchedPosition;
|
||||
ExpressionNode* foundNode;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EXPRESSIONNODELOCATIONFINDER_H
|
@@ -83,7 +83,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
ReportAnyError(node);
|
||||
}
|
||||
void OnVisitFunctionNode(FunctionNode& node) override {
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
ReportAnyError(node);
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
ReportAnyError(node);
|
||||
for (auto& parameter : node.parameters) {
|
||||
parameter->Visit(*this);
|
||||
|
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "GDCore/IDE/Events/ExpressionsCorrectnessTesting.h"
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
CallbacksForExpressionCorrectnessTesting::
|
||||
CallbacksForExpressionCorrectnessTesting(const gd::ObjectsContainer& project_,
|
||||
const gd::ObjectsContainer& layout_)
|
||||
: project(project_), layout(layout_) {}
|
||||
|
||||
bool CallbacksForExpressionCorrectnessTesting::OnSubMathExpression(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::Expression& expression) {
|
||||
CallbacksForExpressionCorrectnessTesting callbacks(project, layout);
|
||||
|
||||
gd::ExpressionParser parser(expression.GetPlainString());
|
||||
if (!parser.ParseMathExpression(platform, project, layout, callbacks)) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
firstErrorStr = callbacks.GetFirstError();
|
||||
firstErrorPos = callbacks.GetFirstErrorPosition();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallbacksForExpressionCorrectnessTesting::OnSubTextExpression(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::Expression& expression) {
|
||||
CallbacksForExpressionCorrectnessTesting callbacks(project, layout);
|
||||
|
||||
gd::ExpressionParser parser(expression.GetPlainString());
|
||||
if (!parser.ParseStringExpression(platform, project, layout, callbacks)) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
firstErrorStr = callbacks.GetFirstError();
|
||||
firstErrorPos = callbacks.GetFirstErrorPosition();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef GDCORE_EXPRESSIONSCORRECTNESSTESTING_H
|
||||
#define GDCORE_EXPRESSIONSCORRECTNESSTESTING_H
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Events/Parsers/ExpressionParser.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class ExpressionMetadata;
|
||||
class Expression;
|
||||
class Project;
|
||||
class Layout;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
// TODO: Replace and remove (ExpressionValidator)
|
||||
|
||||
/**
|
||||
* \brief Parser callbacks used to check expressions correctness
|
||||
*
|
||||
* Usage example:
|
||||
* \code
|
||||
* gd::CallbacksForExpressionCorrectnessTesting callbacks(game, scene);
|
||||
* gd::ExpressionParser expressionParser(expression);
|
||||
* if ( !expressionParser.ParseMathExpression(game, scene, callbacks) )
|
||||
* //Expression is not valid
|
||||
* else
|
||||
* //Expression is correct
|
||||
* \endcode
|
||||
*
|
||||
* \see gd::ExpressionParser
|
||||
* \see gd::ParserCallbacks
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API CallbacksForExpressionCorrectnessTesting
|
||||
: public gd::ParserCallbacks {
|
||||
public:
|
||||
CallbacksForExpressionCorrectnessTesting(const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout);
|
||||
virtual ~CallbacksForExpressionCorrectnessTesting(){};
|
||||
|
||||
void OnConstantToken(gd::String text){};
|
||||
void OnStaticFunction(gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo){};
|
||||
void OnObjectFunction(gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo){};
|
||||
void OnObjectBehaviorFunction(gd::String functionName,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const gd::ExpressionMetadata& expressionInfo){};
|
||||
bool OnSubMathExpression(const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::Expression& expression);
|
||||
bool OnSubTextExpression(const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::Expression& expression);
|
||||
|
||||
private:
|
||||
const gd::ObjectsContainer& project;
|
||||
const gd::ObjectsContainer& layout;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EXPRESSIONSCORRECTNESSTESTING_H
|
@@ -74,7 +74,8 @@ class GD_CORE_API ExpressionParameterMover
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {}
|
||||
void OnVisitFunctionNode(FunctionNode& node) override {
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
auto moveParameter =
|
||||
[this](std::vector<std::unique_ptr<gd::ExpressionNode>>& parameters) {
|
||||
if (oldIndex >= parameters.size() || newIndex >= parameters.size())
|
||||
@@ -87,7 +88,8 @@ class GD_CORE_API ExpressionParameterMover
|
||||
};
|
||||
|
||||
if (node.functionName == functionName) {
|
||||
if (!objectType.empty() && !node.objectName.empty()) {
|
||||
if (behaviorType.empty() && !objectType.empty() &&
|
||||
!node.objectName.empty()) {
|
||||
// Move parameter of an object function
|
||||
const gd::String& thisObjectType = gd::GetTypeOfObject(
|
||||
globalObjectsContainer, objectsContainer, node.objectName);
|
||||
@@ -103,7 +105,7 @@ class GD_CORE_API ExpressionParameterMover
|
||||
moveParameter(node.parameters);
|
||||
hasDoneMoving = true;
|
||||
}
|
||||
} else {
|
||||
} else if (behaviorType.empty() && objectType.empty()) {
|
||||
// Move parameter of a free function
|
||||
moveParameter(node.parameters);
|
||||
hasDoneMoving = true;
|
||||
@@ -119,10 +121,12 @@ class GD_CORE_API ExpressionParameterMover
|
||||
bool hasDoneMoving;
|
||||
const gd::ObjectsContainer& globalObjectsContainer;
|
||||
const gd::ObjectsContainer& objectsContainer;
|
||||
const gd::String& behaviorType; // The behavior type for which the expression
|
||||
// must be replaced (optional)
|
||||
const gd::String& objectType; // The object type for which the expression
|
||||
// must be replaced (optional)
|
||||
const gd::String& behaviorType; // The behavior type of the function which
|
||||
// must have a parameter moved (optional).
|
||||
const gd::String& objectType; // The object type of the function which
|
||||
// must have a parameter moved (optional). If
|
||||
// `behaviorType` is not empty, it takes
|
||||
// precedence over `objectType`.
|
||||
const gd::String& functionName;
|
||||
std::size_t oldIndex;
|
||||
std::size_t newIndex;
|
||||
|
@@ -71,13 +71,41 @@ class GD_CORE_API ExpressionFunctionRenamer
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {}
|
||||
void OnVisitFunctionNode(FunctionNode& node) override {
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (!node.behaviorFunctionName.empty()) {
|
||||
// Behavior function name
|
||||
if (!behaviorType.empty() &&
|
||||
node.behaviorFunctionName == oldFunctionName) {
|
||||
const gd::String& thisBehaviorType =
|
||||
gd::GetTypeOfBehavior(globalObjectsContainer,
|
||||
objectsContainer,
|
||||
node.objectFunctionOrBehaviorName);
|
||||
if (thisBehaviorType == behaviorType) {
|
||||
node.behaviorFunctionName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Object function name
|
||||
if (behaviorType.empty() && !objectType.empty() &&
|
||||
node.objectFunctionOrBehaviorName == oldFunctionName) {
|
||||
const gd::String& thisObjectType = gd::GetTypeOfObject(
|
||||
globalObjectsContainer, objectsContainer, node.objectName);
|
||||
if (thisObjectType == objectType) {
|
||||
node.objectFunctionOrBehaviorName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (node.functionName == oldFunctionName) {
|
||||
if (!objectType.empty() && !node.objectName.empty()) {
|
||||
if (behaviorType.empty() && !objectType.empty() &&
|
||||
!node.objectName.empty()) {
|
||||
// Replace an object function
|
||||
const gd::String& thisObjectType = gd::GetTypeOfObject(
|
||||
globalObjectsContainer, objectsContainer, node.objectName);
|
||||
if (thisObjectType == behaviorType) {
|
||||
if (thisObjectType == objectType) {
|
||||
node.functionName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
}
|
||||
@@ -89,7 +117,7 @@ class GD_CORE_API ExpressionFunctionRenamer
|
||||
node.functionName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
}
|
||||
} else {
|
||||
} else if (behaviorType.empty() && objectType.empty()) {
|
||||
// Replace a free function
|
||||
node.functionName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
@@ -106,9 +134,11 @@ class GD_CORE_API ExpressionFunctionRenamer
|
||||
const gd::ObjectsContainer& globalObjectsContainer;
|
||||
const gd::ObjectsContainer& objectsContainer;
|
||||
const gd::String& behaviorType; // The behavior type for which the expression
|
||||
// must be replaced (optional)
|
||||
// must be replaced (optional).
|
||||
const gd::String& objectType; // The object type for which the expression
|
||||
// must be replaced (optional)
|
||||
// must be replaced (optional). If
|
||||
// `behaviorType` is not empty, it takes
|
||||
// precedence over `objectType`.
|
||||
const gd::String& oldFunctionName;
|
||||
const gd::String& newFunctionName;
|
||||
};
|
||||
|
@@ -4,6 +4,7 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "ProjectStripper.h"
|
||||
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/ExternalLayout.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
@@ -20,61 +21,8 @@ void GD_CORE_API ProjectStripper::StripProjectForExport(gd::Project& project) {
|
||||
project.GetLayout(i).GetObjectGroups().Clear();
|
||||
project.GetLayout(i).GetEvents().Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void GD_CORE_API ProjectStripper::StripProjectForLayoutEdition(
|
||||
gd::Project& project, const gd::String& layoutName) {
|
||||
while (project.GetExternalEventsCount() > 0)
|
||||
project.RemoveExternalEvents(project.GetExternalEvents(0).GetName());
|
||||
|
||||
for (unsigned int i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
auto& layout = project.GetLayout(i);
|
||||
if (layoutName == layout.GetName()) continue;
|
||||
|
||||
project.GetLayout(i).GetEvents().Clear();
|
||||
project.GetLayout(i).GetInitialInstances().Clear();
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < project.GetExternalEventsCount(); ++i) {
|
||||
project.GetExternalEvents(i).GetEvents().Clear();
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < project.GetExternalLayoutsCount(); ++i) {
|
||||
project.GetExternalLayout(i).GetInitialInstances().Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void GD_CORE_API ProjectStripper::StripProjectForExternalLayoutEdition(
|
||||
gd::Project& project, const gd::String& externalLayoutName) {
|
||||
while (project.GetExternalEventsCount() > 0)
|
||||
project.RemoveExternalEvents(project.GetExternalEvents(0).GetName());
|
||||
|
||||
gd::String associatedLayoutName;
|
||||
if (project.HasExternalLayoutNamed(externalLayoutName)) {
|
||||
associatedLayoutName =
|
||||
project.GetExternalLayout(externalLayoutName).GetAssociatedLayout();
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
auto& layout = project.GetLayout(i);
|
||||
if (!associatedLayoutName.empty() &&
|
||||
associatedLayoutName == layout.GetName())
|
||||
continue;
|
||||
|
||||
project.GetLayout(i).GetEvents().Clear();
|
||||
project.GetLayout(i).GetInitialInstances().Clear();
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < project.GetExternalEventsCount(); ++i) {
|
||||
project.GetExternalEvents(i).GetEvents().Clear();
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < project.GetExternalLayoutsCount(); ++i) {
|
||||
auto& externalLayout = project.GetExternalLayout(i);
|
||||
if (externalLayoutName == externalLayout.GetName()) continue;
|
||||
|
||||
externalLayout.GetInitialInstances().Clear();
|
||||
}
|
||||
project.ClearEventsFunctionsExtensions();
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -28,23 +28,6 @@ class GD_CORE_API ProjectStripper {
|
||||
*/
|
||||
static void StripProjectForExport(gd::Project& project);
|
||||
|
||||
/**
|
||||
* \brief Strip project to keep only the full content of the specified
|
||||
* layout. The content of other layouts, external events and external layouts
|
||||
* is removed.
|
||||
*/
|
||||
static void StripProjectForLayoutEdition(gd::Project& project,
|
||||
const gd::String& layoutName);
|
||||
|
||||
/**
|
||||
* \brief Strip project to keep only the full content of the specified
|
||||
* external layout and the associated layout.
|
||||
* The content of other layouts, external events and external layouts is
|
||||
* removed.
|
||||
*/
|
||||
static void StripProjectForExternalLayoutEdition(
|
||||
gd::Project& project, const gd::String& externalLayoutName);
|
||||
|
||||
private:
|
||||
ProjectStripper(){};
|
||||
virtual ~ProjectStripper(){};
|
||||
|
@@ -10,34 +10,49 @@
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String SceneNameMangler::GetMangledSceneName(gd::String sceneName) {
|
||||
static const gd::String allowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
static const gd::String allowedExceptFirst = "0123456789";
|
||||
SceneNameMangler *SceneNameMangler::_singleton = nullptr;
|
||||
|
||||
std::size_t i = 0;
|
||||
for (auto it = sceneName.begin(); it != sceneName.end(); ++it) {
|
||||
char32_t character = *it;
|
||||
if (allowedCharacters.find(character) == gd::String::npos &&
|
||||
(allowedExceptFirst.find(character) == gd::String::npos ||
|
||||
i == 0)) // Also disallow some characters to be in first position
|
||||
{
|
||||
// Replace the character by an underscore and its unicode codepoint (in
|
||||
// base 10)
|
||||
auto it2 = it;
|
||||
++it2;
|
||||
sceneName.replace(it, it2, "_" + gd::String::From(character));
|
||||
|
||||
// The iterator it may have been invalidated:
|
||||
// re-assign it with a new iterator pointing to the same position.
|
||||
it = sceneName.begin();
|
||||
std::advance(it, i);
|
||||
}
|
||||
|
||||
++i;
|
||||
const gd::String &SceneNameMangler::GetMangledSceneName(
|
||||
const gd::String &sceneName) {
|
||||
auto it = mangledSceneNames.find(sceneName);
|
||||
if (it != mangledSceneNames.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return sceneName;
|
||||
gd::String partiallyMangledName = sceneName;
|
||||
static const gd::String alwaysAllowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
static const gd::String allowedExceptFirstCharacters = "0123456789";
|
||||
|
||||
for (size_t i = 0; i < partiallyMangledName.size();
|
||||
++i) // Replace all unallowed letter by an underscore and the unicode
|
||||
// code point of the letter
|
||||
{
|
||||
if (alwaysAllowedCharacters.find_first_of(
|
||||
std::u32string(1, partiallyMangledName[i])) == gd::String::npos &&
|
||||
(i == 0 ||
|
||||
allowedExceptFirstCharacters.find(
|
||||
std::u32string(1, partiallyMangledName[i])) == gd::String::npos)) {
|
||||
char32_t unallowedChar = partiallyMangledName[i];
|
||||
partiallyMangledName.replace(i, 1, "_" + gd::String::From(unallowedChar));
|
||||
}
|
||||
}
|
||||
|
||||
mangledSceneNames[sceneName] = partiallyMangledName;
|
||||
return mangledSceneNames[sceneName];
|
||||
}
|
||||
|
||||
SceneNameMangler *SceneNameMangler::Get() {
|
||||
if (nullptr == _singleton) _singleton = new SceneNameMangler;
|
||||
|
||||
return (static_cast<SceneNameMangler *>(_singleton));
|
||||
}
|
||||
|
||||
void SceneNameMangler::DestroySingleton() {
|
||||
if (nullptr != _singleton) {
|
||||
delete _singleton;
|
||||
_singleton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -6,12 +6,14 @@
|
||||
|
||||
#ifndef SCENENAMEMANGLER_H
|
||||
#define SCENENAMEMANGLER_H
|
||||
#include <unordered_map>
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Used to mangle the name of a scene
|
||||
* \brief Mangle the name of a scene, so that it can be used in code or file
|
||||
* names.
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
@@ -21,12 +23,22 @@ class GD_CORE_API SceneNameMangler {
|
||||
* \brief Mangle the name of a scene, replacing all characters that are not
|
||||
* 0-9, a-z or A-Z by "_"+UnicodeCodePointOfTheCharacter. The first character
|
||||
* must be a letter, otherwise it is also replaced in the same manner.
|
||||
*
|
||||
* The mangled name is memoized as this is intensively used during project
|
||||
* export and events code generation.
|
||||
*/
|
||||
static gd::String GetMangledSceneName(gd::String sceneName);
|
||||
const gd::String& GetMangledSceneName(const gd::String& sceneName);
|
||||
|
||||
static SceneNameMangler* Get();
|
||||
static void DestroySingleton();
|
||||
|
||||
private:
|
||||
SceneNameMangler(){};
|
||||
virtual ~SceneNameMangler(){};
|
||||
static SceneNameMangler* _singleton;
|
||||
|
||||
std::unordered_map<gd::String, gd::String>
|
||||
mangledSceneNames; ///< Memoized results of mangling
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -470,7 +470,17 @@ void WholeProjectRefactorer::RenameBehaviorProperty(
|
||||
auto& properties = eventsBasedBehavior.GetPropertyDescriptors();
|
||||
if (!properties.Has(oldPropertyName)) return;
|
||||
|
||||
const auto& property = properties.Get(oldPropertyName);
|
||||
// Order is important: we first rename the expressions then the instructions,
|
||||
// to avoid being unable to fetch the metadata (the types of parameters) of
|
||||
// instructions after they are renamed.
|
||||
gd::ExpressionsRenamer expressionRenamer =
|
||||
gd::ExpressionsRenamer(project.GetCurrentPlatform());
|
||||
expressionRenamer.SetReplacedBehaviorExpression(
|
||||
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
|
||||
eventsBasedBehavior.GetName()),
|
||||
EventsBasedBehavior::GetPropertyExpressionName(oldPropertyName),
|
||||
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
|
||||
ExposeProjectEvents(project, expressionRenamer);
|
||||
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
@@ -495,15 +505,6 @@ void WholeProjectRefactorer::RenameBehaviorProperty(
|
||||
eventsBasedBehavior.GetName(),
|
||||
EventsBasedBehavior::GetPropertyConditionName(newPropertyName)));
|
||||
ExposeProjectEvents(project, conditionRenamer);
|
||||
|
||||
gd::ExpressionsRenamer expressionRenamer =
|
||||
gd::ExpressionsRenamer(project.GetCurrentPlatform());
|
||||
expressionRenamer.SetReplacedBehaviorExpression(
|
||||
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
|
||||
eventsBasedBehavior.GetName()),
|
||||
EventsBasedBehavior::GetPropertyExpressionName(oldPropertyName),
|
||||
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
|
||||
ExposeProjectEvents(project, expressionRenamer);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
|
@@ -50,7 +50,10 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
gd::ArbitraryEventsWorkerWithContext& worker);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after an events function extension is renamed
|
||||
* \brief Refactor the project **before** an events function extension is renamed.
|
||||
*
|
||||
* \warning Do the renaming of the specified extension after calling this.
|
||||
* This is because the extension is expected to have its old name for the refactoring.
|
||||
*/
|
||||
static void RenameEventsFunctionsExtension(
|
||||
gd::Project& project,
|
||||
@@ -59,7 +62,10 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
const gd::String& newName);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after an events function is renamed
|
||||
* \brief Refactor the project **before** an events function is renamed.
|
||||
*
|
||||
* \warning Do the renaming of the specified function after calling this.
|
||||
* This is because the function is expected to have its old name for the refactoring.
|
||||
*/
|
||||
static void RenameEventsFunction(
|
||||
gd::Project& project,
|
||||
@@ -68,8 +74,11 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
const gd::String& newFunctionName);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after an events function of a behavior is
|
||||
* \brief Refactor the project **before** an events function of a behavior is
|
||||
* renamed.
|
||||
*
|
||||
* \warning Do the renaming of the specified function after calling this.
|
||||
* This is because the function is expected to have its old name for the refactoring.
|
||||
*/
|
||||
static void RenameBehaviorEventsFunction(
|
||||
gd::Project& project,
|
||||
@@ -79,8 +88,11 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
const gd::String& newFunctionName);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after an events function parameter
|
||||
* was moved.
|
||||
* \brief Refactor the project **before** an events function parameter
|
||||
* is moved.
|
||||
*
|
||||
* \warning Do the move of the specified function parameters after calling this.
|
||||
* This is because the function is expected to be in its old state for the refactoring.
|
||||
*/
|
||||
static void MoveEventsFunctionParameter(
|
||||
gd::Project& project,
|
||||
@@ -90,8 +102,11 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
std::size_t newIndex);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after the parmaeter of an events function of a
|
||||
* behavior was moved.
|
||||
* \brief Refactor the project **before** the parameter of an events function of a
|
||||
* behavior is moved.
|
||||
*
|
||||
* \warning Do the move of the specified function parameters after calling this.
|
||||
* This is because the function is expected to be in its old state for the refactoring.
|
||||
*/
|
||||
static void MoveBehaviorEventsFunctionParameter(
|
||||
gd::Project& project,
|
||||
@@ -102,8 +117,11 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
std::size_t newIndex);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after a property of a behavior is
|
||||
* \brief Refactor the project **before** a property of a behavior is
|
||||
* renamed.
|
||||
*
|
||||
* \warning Do the renaming of the specified property after calling this.
|
||||
* This is because the property is expected to have its old name for the refactoring.
|
||||
*/
|
||||
static void RenameBehaviorProperty(
|
||||
gd::Project& project,
|
||||
@@ -113,7 +131,10 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
const gd::String& newPropertyName);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after a behavior is renamed.
|
||||
* \brief Refactor the project **before** a behavior is renamed.
|
||||
*
|
||||
* \warning Do the renaming of the specified behavior after calling this.
|
||||
* This is because the behavior is expected to have its old name for the refactoring.
|
||||
*/
|
||||
static void RenameEventsBasedBehavior(
|
||||
gd::Project& project,
|
||||
@@ -160,7 +181,7 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
bool isObjectGroup);
|
||||
|
||||
/**
|
||||
* \brief Refactor the events function after an object or group is renamed
|
||||
* \brief Refactor the events function after an object or group is removed
|
||||
*
|
||||
* This will update the events of the function and groups.
|
||||
*/
|
||||
|
@@ -70,6 +70,19 @@ void EventsFunctionsExtension::UnserializeFrom(
|
||||
"eventsBasedBehavior", project, element.GetChild("eventsBasedBehaviors"));
|
||||
}
|
||||
|
||||
bool EventsFunctionsExtension::IsExtensionLifecycleEventsFunction(
|
||||
const gd::String& eventsFunctionName) {
|
||||
// The list of all supported lifecycle events function names.
|
||||
// If adding a new one, code generator(s) must be updated.
|
||||
return eventsFunctionName == "onFirstSceneLoaded" ||
|
||||
eventsFunctionName == "onSceneLoaded" ||
|
||||
eventsFunctionName == "onScenePreEvents" ||
|
||||
eventsFunctionName == "onScenePostEvents" ||
|
||||
eventsFunctionName == "onScenePaused" ||
|
||||
eventsFunctionName == "onSceneResumed" ||
|
||||
eventsFunctionName == "onSceneUnloading";
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif
|
||||
|
@@ -8,8 +8,8 @@
|
||||
#define GDCORE_EVENTSFUNCTIONEXTENSION_H
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Project/EventsFunctionsContainer.h"
|
||||
#include "GDCore/Project/EventsBasedBehavior.h"
|
||||
#include "GDCore/Project/EventsFunctionsContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/SerializableWithNameList.h"
|
||||
namespace gd {
|
||||
@@ -59,7 +59,8 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
}
|
||||
|
||||
const gd::String& GetShortDescription() const { return shortDescription; };
|
||||
EventsFunctionsExtension& SetShortDescription(const gd::String& shortDescription_) {
|
||||
EventsFunctionsExtension& SetShortDescription(
|
||||
const gd::String& shortDescription_) {
|
||||
shortDescription = shortDescription_;
|
||||
return *this;
|
||||
}
|
||||
@@ -97,15 +98,15 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
/**
|
||||
* \brief Return a reference to the list of the events based behaviors.
|
||||
*/
|
||||
SerializableWithNameList<EventsBasedBehavior>& GetEventsBasedBehaviors() {
|
||||
gd::SerializableWithNameList<EventsBasedBehavior>& GetEventsBasedBehaviors() {
|
||||
return eventsBasedBehaviors;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return a const reference to the list of the events based behaviors.
|
||||
*/
|
||||
const SerializableWithNameList<EventsBasedBehavior>& GetEventsBasedBehaviors()
|
||||
const {
|
||||
const gd::SerializableWithNameList<EventsBasedBehavior>&
|
||||
GetEventsBasedBehaviors() const {
|
||||
return eventsBasedBehaviors;
|
||||
}
|
||||
|
||||
@@ -124,6 +125,12 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
const gd::SerializerElement& element);
|
||||
///@}
|
||||
|
||||
/** \name Lifecycle event functions
|
||||
*/
|
||||
///@{
|
||||
static bool IsExtensionLifecycleEventsFunction(const gd::String& eventsFunctionName);
|
||||
///@}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialize object using another object. Used by copy-ctor and assign-op.
|
||||
@@ -139,7 +146,7 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
gd::String fullName;
|
||||
gd::String tags;
|
||||
gd::String author;
|
||||
SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
|
||||
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -63,7 +63,7 @@ Layout::Layout()
|
||||
|
||||
void Layout::SetName(const gd::String& name_) {
|
||||
name = name_;
|
||||
mangledName = gd::SceneNameMangler::GetMangledSceneName(name);
|
||||
mangledName = gd::SceneNameMangler::Get()->GetMangledSceneName(name);
|
||||
};
|
||||
|
||||
bool Layout::HasBehaviorSharedData(const gd::String& behaviorName) {
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "Project.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cctype>
|
||||
#include <SFML/System/Utf.hpp>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
@@ -520,6 +521,9 @@ void Project::RemoveEventsFunctionsExtension(const gd::String& name) {
|
||||
|
||||
eventsFunctionsExtensions.erase(eventsFunctionExtension);
|
||||
}
|
||||
void Project::ClearEventsFunctionsExtensions() {
|
||||
eventsFunctionsExtensions.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
@@ -945,18 +949,16 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Project::ValidateObjectName(const gd::String& name) {
|
||||
bool Project::ValidateName(const gd::String& name) {
|
||||
if (name.empty()) return false;
|
||||
|
||||
if (isdigit(name[0])) return false;
|
||||
|
||||
gd::String allowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||
return !(name.find_first_not_of(allowedCharacters) != gd::String::npos);
|
||||
}
|
||||
|
||||
gd::String Project::GetBadObjectNameWarning() {
|
||||
return _("Please use only letters, digits\nand underscores ( _ ).");
|
||||
}
|
||||
|
||||
void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
|
||||
// See also gd::WholeProjectRefactorer::ExposeProjectEvents for a method that
|
||||
// traverse the whole project (this time for events) and ExposeProjectEffects
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#define GDCORE_PROJECT_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Project/LoadingScreen.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
@@ -460,22 +461,27 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
std::size_t GetLayoutsCount() const;
|
||||
|
||||
/**
|
||||
* \brief \brief Adds a new empty layout called "name" at the specified
|
||||
* \brief Add a new empty layout called "name" at the specified
|
||||
* position in the layout list.
|
||||
*/
|
||||
gd::Layout& InsertNewLayout(const gd::String& name, std::size_t position);
|
||||
|
||||
/**
|
||||
* \brief \brief Adds a new layout constructed from the layout passed as
|
||||
* parameter. \note No pointer or reference must be kept on the layout passed
|
||||
* as parameter. \param layout The layout that must be copied and inserted
|
||||
* into the project \param position Insertion position. Even if the position
|
||||
* \brief Add a new layout constructed from the layout passed as
|
||||
* parameter.
|
||||
* \param layout The layout that must be copied and inserted
|
||||
* into the project
|
||||
* \param position Insertion position. Even if the position
|
||||
* is invalid, the layout must be inserted at the end of the layout list.
|
||||
*
|
||||
* \note No pointer or reference must be kept on the layout passed
|
||||
* as parameter.
|
||||
*
|
||||
*/
|
||||
gd::Layout& InsertLayout(const Layout& layout, std::size_t position);
|
||||
|
||||
/**
|
||||
* Must delete layout named "name".
|
||||
* \brief Delete layout named "name".
|
||||
*/
|
||||
void RemoveLayout(const gd::String& name);
|
||||
|
||||
@@ -592,7 +598,7 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
std::size_t position);
|
||||
|
||||
/**
|
||||
* Must delete external events named "name".
|
||||
* \brief Delete external events named "name".
|
||||
*/
|
||||
void RemoveExternalEvents(const gd::String& name);
|
||||
#endif
|
||||
@@ -673,7 +679,7 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
std::size_t position);
|
||||
|
||||
/**
|
||||
* Must delete external layout named "name".
|
||||
* \brief Delete external layout named "name".
|
||||
*/
|
||||
void RemoveExternalLayout(const gd::String& name);
|
||||
|
||||
@@ -694,37 +700,37 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
///@{
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* Return true if events functions extension called "name" exists.
|
||||
* \brief Check if events functions extension called "name" exists.
|
||||
*/
|
||||
bool HasEventsFunctionsExtensionNamed(const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* Return a reference to the events functions extension called "name".
|
||||
* \brief Return a reference to the events functions extension called "name".
|
||||
*/
|
||||
EventsFunctionsExtension& GetEventsFunctionsExtension(const gd::String& name);
|
||||
|
||||
/**
|
||||
* Return a reference to the events functions extension called "name".
|
||||
* \brief Return a reference to the events functions extension called "name".
|
||||
*/
|
||||
const EventsFunctionsExtension& GetEventsFunctionsExtension(
|
||||
const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* Return a reference to the events functions extension at position "index" in
|
||||
* the list
|
||||
* \brief Return a reference to the events functions extension at position
|
||||
* "index" in the list
|
||||
*/
|
||||
EventsFunctionsExtension& GetEventsFunctionsExtension(std::size_t index);
|
||||
|
||||
/**
|
||||
* Return a reference to the events functions extension at position "index" in
|
||||
* the list
|
||||
* \brief Return a reference to the events functions extension at position
|
||||
* "index" in the list
|
||||
*/
|
||||
const EventsFunctionsExtension& GetEventsFunctionsExtension(
|
||||
std::size_t index) const;
|
||||
|
||||
/**
|
||||
* Return the position of the events functions extension called "name" in the
|
||||
* list
|
||||
* \brief Return the position of the events functions extension called "name"
|
||||
* in the list.
|
||||
*/
|
||||
std::size_t GetEventsFunctionsExtensionPosition(const gd::String& name) const;
|
||||
|
||||
@@ -736,7 +742,7 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
void SwapEventsFunctionsExtensions(std::size_t first, std::size_t second);
|
||||
|
||||
/**
|
||||
* Return the number of events functions extension.
|
||||
* \brief Returns the number of events functions extension.
|
||||
*/
|
||||
std::size_t GetEventsFunctionsExtensionsCount() const;
|
||||
|
||||
@@ -759,9 +765,14 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
std::size_t position);
|
||||
|
||||
/**
|
||||
* Must delete the events functions extension named "name".
|
||||
* \brief Delete the events functions extension named "name".
|
||||
*/
|
||||
void RemoveEventsFunctionsExtension(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Remove all the events functions extensions.
|
||||
*/
|
||||
void ClearEventsFunctionsExtensions();
|
||||
#endif
|
||||
///@}
|
||||
|
||||
@@ -843,21 +854,10 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Return true if \a objectName can be used as name for an object.
|
||||
*
|
||||
* Default implementation check if objectName is only composed of a-z,A-Z,0-9
|
||||
* or _ characters an if does not conflict with an expression.
|
||||
* Return true if \a name is valid (can be used safely for an object,
|
||||
* behavior, events function name, etc...).
|
||||
*/
|
||||
static bool ValidateObjectName(const gd::String& objectName);
|
||||
|
||||
/**
|
||||
* Return a message that will be displayed when an invalid object name has
|
||||
* been entered.
|
||||
*
|
||||
* \note This message will be displayed by the IDE into a tooltip.
|
||||
*/
|
||||
static gd::String GetBadObjectNameWarning();
|
||||
|
||||
static bool ValidateName(const gd::String& name);
|
||||
///@}
|
||||
|
||||
/** \name External source files
|
||||
|
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
|
||||
#include "GDCore/Project/Variable.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/TinyXml/tinyxml.h"
|
||||
@@ -19,9 +21,7 @@ namespace gd {
|
||||
*/
|
||||
double Variable::GetValue() const {
|
||||
if (!isNumber) {
|
||||
stringstream ss;
|
||||
ss << str;
|
||||
ss >> value;
|
||||
value = str.To<double>();
|
||||
isNumber = true;
|
||||
}
|
||||
|
||||
@@ -30,9 +30,7 @@ double Variable::GetValue() const {
|
||||
|
||||
const gd::String& Variable::GetString() const {
|
||||
if (isNumber) {
|
||||
stringstream s;
|
||||
s << (value);
|
||||
str = s.str();
|
||||
str = gd::String::From(value);
|
||||
isNumber = false;
|
||||
}
|
||||
|
||||
@@ -76,6 +74,7 @@ const Variable& Variable::GetChild(const gd::String& name) const {
|
||||
void Variable::RemoveChild(const gd::String& name) {
|
||||
if (!isStructure) return;
|
||||
children.erase(name);
|
||||
isStructure = !children.empty();
|
||||
}
|
||||
|
||||
bool Variable::RenameChild(const gd::String& oldName,
|
||||
@@ -190,6 +189,7 @@ void Variable::RemoveRecursively(const gd::Variable& variableToRemove) {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
isStructure = !children.empty();
|
||||
}
|
||||
|
||||
Variable::Variable(const Variable& other)
|
||||
|
@@ -95,6 +95,14 @@ void Serializer::FromXML(SerializerElement& element,
|
||||
}
|
||||
#endif
|
||||
|
||||
gd::String Serializer::ToEscapedXMLString(const gd::String& str) {
|
||||
return str.FindAndReplace("&", "&")
|
||||
.FindAndReplace("'", "'")
|
||||
.FindAndReplace("\"", """)
|
||||
.FindAndReplace("<", "<")
|
||||
.FindAndReplace(">", ">");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
|
@@ -27,14 +27,26 @@ class GD_CORE_API Serializer {
|
||||
static void FromXML(SerializerElement& element,
|
||||
const TiXmlElement* xmlElement);
|
||||
#endif
|
||||
/**
|
||||
* \brief Escape a string for inclusion in a XML tag
|
||||
*/
|
||||
static gd::String ToEscapedXMLString(const gd::String& str);
|
||||
///@}
|
||||
|
||||
/** \name JSON serialization.
|
||||
* Serialize a SerializerElement from/to JSON.
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize a gd::SerializerElement to a JSON string.
|
||||
*/
|
||||
static gd::String ToJSON(const SerializerElement& element);
|
||||
|
||||
static SerializerElement FromJSON(const std::string& json);
|
||||
|
||||
/**
|
||||
* \brief Parse a JSON string and returns a gd::SerializerElement for it.
|
||||
*/
|
||||
static SerializerElement FromJSON(const gd::String& json) {
|
||||
return FromJSON(json.ToUTF8());
|
||||
}
|
||||
|
@@ -25,11 +25,11 @@ gd::String GetTranslation(const char* str) { // TODO: Inline?
|
||||
// }
|
||||
ensureCache.prepare();
|
||||
|
||||
var translatedStr = getTranslation(Pointer_stringify($0));
|
||||
var translatedStr = getTranslation(UTF8ToString($0));
|
||||
return ensureString(translatedStr);
|
||||
},
|
||||
str);
|
||||
return gd::String(translatedStr); // TODO: Is copying necessary?
|
||||
}
|
||||
} // namespace gd
|
||||
#endif
|
||||
#endif
|
||||
|
@@ -14,6 +14,9 @@
|
||||
|
||||
void SetupProjectWithDummyPlatform(gd::Project &project,
|
||||
gd::Platform &platform) {
|
||||
// Don't show extension loading logs for tests (too verbose).
|
||||
platform.EnableExtensionLoadingLogs(false);
|
||||
|
||||
std::shared_ptr<gd::PlatformExtension> baseObjectExtension =
|
||||
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
|
||||
baseObjectExtension->SetExtensionInformation(
|
||||
|
307
Core/tests/ExpressionCompletionFinder.cpp
Normal file
307
Core/tests/ExpressionCompletionFinder.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/IDE/Events/ExpressionCompletionFinder.h"
|
||||
#include "DummyPlatform.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto& layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
|
||||
auto getCompletionsFor = [&](
|
||||
const gd::String& type, const gd::String& expression, size_t location) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
REQUIRE(node != nullptr);
|
||||
return gd::ExpressionCompletionFinder::GetCompletionDescriptionsFor(
|
||||
*node, location);
|
||||
};
|
||||
|
||||
const std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedEmptyCompletions;
|
||||
|
||||
SECTION("Identifier") {
|
||||
SECTION("Object or expression completions when type is string") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string", "My"),
|
||||
gd::ExpressionCompletionDescription::ForExpression("string", "My")};
|
||||
REQUIRE(getCompletionsFor("string", "My", 0) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "My", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "My", 2) == expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object or expression completions when type is number") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("number", "My"),
|
||||
gd::ExpressionCompletionDescription::ForExpression("number", "My")};
|
||||
REQUIRE(getCompletionsFor("number", "My", 0) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "My", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "My", 2) == expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object when type is an object") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("object", "My")};
|
||||
REQUIRE(getCompletionsFor("object", "My", 0) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("object", "My", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("object", "My", 2) == expectedEmptyCompletions);
|
||||
}
|
||||
|
||||
SECTION("Object when type is an object (alternate type)") {
|
||||
// Also test alternate types also considered as objects (but that can
|
||||
// result in different code generation):
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("objectPtr", "My")};
|
||||
REQUIRE(getCompletionsFor("objectPtr", "My", 0) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("objectPtr", "My", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("objectPtr", "My", 2) ==
|
||||
expectedEmptyCompletions);
|
||||
}
|
||||
}
|
||||
SECTION("Operator (number)") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("number", ""),
|
||||
gd::ExpressionCompletionDescription::ForExpression("number", "")};
|
||||
REQUIRE(getCompletionsFor("number", "1 + ", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "1 + ", 2) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "1 + ", 3) == expectedCompletions);
|
||||
}
|
||||
SECTION("Operator (string)") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string", ""),
|
||||
gd::ExpressionCompletionDescription::ForExpression("string", "")};
|
||||
REQUIRE(getCompletionsFor("string", "\"a\" + ", 3) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "\"a\" + ", 4) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "\"a\" + ", 5) == expectedCompletions);
|
||||
}
|
||||
|
||||
SECTION("Free function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression("string",
|
||||
"Function")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedExactCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression("string",
|
||||
"Function")
|
||||
.SetIsExact(true)};
|
||||
REQUIRE(getCompletionsFor("string", "Function(", 0) ==
|
||||
expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "Function(", 1) ==
|
||||
expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "Function(", 7) ==
|
||||
expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "Function(", 8) ==
|
||||
expectedExactCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "Function(", 9) ==
|
||||
expectedEmptyCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "Function()", 9) ==
|
||||
expectedExactCompletions);
|
||||
}
|
||||
SECTION("Unknown function, test with arguments") {
|
||||
REQUIRE(getCompletionsFor("string", "Function(1", 9) ==
|
||||
expectedEmptyCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "Function(\"", 9) ==
|
||||
expectedEmptyCompletions);
|
||||
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("unknown", "a"),
|
||||
gd::ExpressionCompletionDescription::ForExpression("unknown", "a")};
|
||||
REQUIRE(getCompletionsFor("string", "Function(a", 9) ==
|
||||
expectedCompletions);
|
||||
}
|
||||
SECTION("Function with a Variable as argument") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForVariable("scenevar",
|
||||
"myVar")};
|
||||
REQUIRE(getCompletionsFor("number",
|
||||
"MyExtension::GetVariableAsNumber(myVar",
|
||||
33) == expectedCompletions);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Partial object or behavior function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string",
|
||||
"MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorOrFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior("Func",
|
||||
"MyObject"),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", "MyObject")};
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func", 7) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func", 8) ==
|
||||
expectedBehaviorOrFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func", 9) ==
|
||||
expectedBehaviorOrFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func", 12) ==
|
||||
expectedBehaviorOrFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func", 13) ==
|
||||
expectedEmptyCompletions);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Object function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string",
|
||||
"MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorOrFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior("Func",
|
||||
"MyObject"),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", "MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedExactFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", "MyObject")
|
||||
.SetIsExact(true)};
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 7) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 8) ==
|
||||
expectedBehaviorOrFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 9) ==
|
||||
expectedBehaviorOrFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 12) ==
|
||||
expectedBehaviorOrFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 13) ==
|
||||
expectedExactFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 14) ==
|
||||
expectedEmptyCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func()", 14) ==
|
||||
expectedExactFunctionCompletions);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Partial behavior function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string",
|
||||
"MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior("MyBehavior",
|
||||
"MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", "MyObject", "MyBehavior")};
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 7) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 8) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 9) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 18) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 19) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 20) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 21) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 23) ==
|
||||
expectedFunctionCompletions);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string",
|
||||
"MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior("MyBehavior",
|
||||
"MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "", "MyObject", "MyBehavior")};
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 7) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 8) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 9) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 18) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 19) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 20) ==
|
||||
expectedFunctionCompletions);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Behavior function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string",
|
||||
"MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior("MyBehavior",
|
||||
"MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", "MyObject", "MyBehavior")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedExactFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", "MyObject", "MyBehavior")
|
||||
.SetIsExact(true)};
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 7) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 8) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 9) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 18) ==
|
||||
expectedBehaviorCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 19) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 20) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 21) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 22) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 23) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 24) ==
|
||||
expectedFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 25) ==
|
||||
expectedExactFunctionCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 26) ==
|
||||
expectedEmptyCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func()", 26) ==
|
||||
expectedExactFunctionCompletions);
|
||||
}
|
||||
}
|
||||
}
|
382
Core/tests/ExpressionNodeLocationFinder.cpp
Normal file
382
Core/tests/ExpressionNodeLocationFinder.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
|
||||
#include "DummyPlatform.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
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);
|
||||
REQUIRE(node != nullptr);
|
||||
return dynamic_cast<TNode*>(
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(
|
||||
*node, searchPosition)) != nullptr;
|
||||
}
|
||||
|
||||
bool CheckNoNodeAtLocation(gd::ExpressionParser2& parser,
|
||||
const gd::String& type,
|
||||
const gd::String& expression,
|
||||
size_t searchPosition) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
REQUIRE(node != nullptr);
|
||||
return gd::ExpressionNodeLocationFinder::GetNodeAtPosition(
|
||||
*node, searchPosition) == nullptr;
|
||||
}
|
||||
|
||||
TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto& layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
|
||||
SECTION("Empty expressions") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
|
||||
parser, "string", "", 0) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", "", 1) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", " ", 0) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
|
||||
parser, "string", " ", 1) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", " ", 2) ==
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid text") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello world\"", 0) == true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello world\"", 1) == true);
|
||||
}
|
||||
SECTION("Test 3") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello world\"", 12) == true);
|
||||
}
|
||||
SECTION("Test 4") {
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"Hello world\"", 13) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Test 5") {
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"Hello world\"", 99) ==
|
||||
true);
|
||||
}
|
||||
}
|
||||
SECTION("Valid text operators") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" + \"World\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "string", "\"Hello \" + \"World\"", 8) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" + \"World\"", 15) == true);
|
||||
}
|
||||
}
|
||||
SECTION("Invalid texts") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "string", "\"", 0) ==
|
||||
true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "string", "\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "string", "\"a", 1) ==
|
||||
true);
|
||||
}
|
||||
|
||||
SECTION("Invalid parenthesis") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::SubExpressionNode>(
|
||||
parser, "string", "((\"hello\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "((\"hello\"", 2) == true);
|
||||
}
|
||||
|
||||
SECTION("Invalid text operators") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" - \"World\"", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" - \"World\"", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "string", "\"Hello \" / \"World\"", 8) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser, "string", "\"Hello \" * \"World\"", 15) == true);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid unary operators") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "-123", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "+123", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "-123", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "-123", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "-123", 3) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "number", "-123", 4) == true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "-+-123", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "-+-123", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::UnaryOperatorNode>(
|
||||
parser, "number", "-+-123", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "-+-123", 3) == true);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid number operators") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 ! 34", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 ! 34", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 ! 34", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 ! 34", 3) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 ! 34", 4) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 ! 34", 5) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 ! 34", 6) == true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "1 / /2", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "1 / /2", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "1 / /2", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "1 / /2", 3) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
|
||||
parser, "number", "1 / /2", 4) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::EmptyNode>(
|
||||
parser, "number", "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);
|
||||
}
|
||||
|
||||
SECTION("Valid objects") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "HelloWorld1", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "HelloWorld1", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "HelloWorld1", 10) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "object", "HelloWorld1", 11) == true);
|
||||
}
|
||||
SECTION("Invalid objects") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "a+b", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "object", "a+b", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
|
||||
parser, "object", "a+b", 2) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "object", "a+b", 3) == true);
|
||||
}
|
||||
SECTION("Valid function calls") {
|
||||
SECTION("Test 1") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 0) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 1) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 2) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 3) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 4) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 5) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 27) ==
|
||||
true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 28) ==
|
||||
true);
|
||||
REQUIRE(CheckNoNodeAtLocation(
|
||||
parser, "number", "12 + MyExtension::GetNumber()", 29) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
33) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
34) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
35) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
36) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
37) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
38) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
39) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
50) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
51) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(
|
||||
parser,
|
||||
"number",
|
||||
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
|
||||
52) == true);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid function calls") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 2) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 10) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "Idontexist(12)", 11) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
|
||||
parser, "number", "Idontexist(12)", 12) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(12)", 13) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "number", "Idontexist(12)", 14) ==
|
||||
true);
|
||||
}
|
||||
|
||||
SECTION("Unterminated function calls") {
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(", 0) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(", 1) == true);
|
||||
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(
|
||||
parser, "number", "Idontexist(", 10) == true);
|
||||
REQUIRE(CheckNoNodeAtLocation(parser, "number", "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) ==
|
||||
true);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
auto node = parser.ParseExpression("scenevar", "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");
|
||||
|
||||
auto child1Node =
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 4);
|
||||
REQUIRE(dynamic_cast<gd::VariableAccessorNode*>(child1Node) != nullptr);
|
||||
REQUIRE(dynamic_cast<gd::VariableAccessorNode&>(*child1Node).name ==
|
||||
"Child1");
|
||||
}
|
||||
SECTION("Test 3") {
|
||||
auto node = parser.ParseExpression(
|
||||
"scenevar", "myVariable[ \"My named children\" ].grandChild");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
auto myVariableNode =
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 0);
|
||||
REQUIRE(dynamic_cast<gd::VariableNode*>(myVariableNode) != nullptr);
|
||||
REQUIRE(dynamic_cast<gd::VariableNode&>(*myVariableNode).name ==
|
||||
"myVariable");
|
||||
|
||||
auto variableBracketAccessorNode =
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 10);
|
||||
REQUIRE(dynamic_cast<gd::VariableBracketAccessorNode*>(
|
||||
variableBracketAccessorNode) != nullptr);
|
||||
|
||||
auto textNode =
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 15);
|
||||
REQUIRE(dynamic_cast<gd::TextNode*>(textNode) != nullptr);
|
||||
|
||||
auto grandChildNode =
|
||||
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 40);
|
||||
REQUIRE(dynamic_cast<gd::VariableAccessorNode*>(grandChildNode) !=
|
||||
nullptr);
|
||||
REQUIRE(dynamic_cast<gd::VariableAccessorNode&>(*grandChildNode).name ==
|
||||
"grandChild");
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,6 +21,51 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
|
||||
SECTION("Empty expression") {
|
||||
{
|
||||
auto node = parser.ParseExpression("string", "");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
REQUIRE(emptyNode.type == "string");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
REQUIRE(emptyNode.type == "number");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("object", "");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
REQUIRE(emptyNode.type == "object");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("string", " ");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
REQUIRE(emptyNode.type == "string");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number", " ");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
REQUIRE(emptyNode.type == "number");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("object", " ");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
REQUIRE(emptyNode.type == "object");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid texts") {
|
||||
{
|
||||
auto node = parser.ParseExpression("string", "\"hello world\"");
|
||||
@@ -50,7 +95,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(textNode.text == "\\");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("string", "\"hello \\\\\\\"world\\\"\"");
|
||||
auto node =
|
||||
parser.ParseExpression("string", "\"hello \\\\\\\"world\\\"\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &textNode = dynamic_cast<gd::TextNode &>(*node);
|
||||
REQUIRE(textNode.text == "hello \\\"world\"");
|
||||
@@ -280,12 +326,42 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("valid operators") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "123 + 456");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(operatorNode.type == "number");
|
||||
auto &leftNumberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*operatorNode.leftHandSide);
|
||||
REQUIRE(leftNumberNode.number == "123");
|
||||
auto &rightNumberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*operatorNode.rightHandSide);
|
||||
REQUIRE(rightNumberNode.number == "456");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("string", "\"abc\" + \"def\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(operatorNode.type == "string");
|
||||
auto &leftTextNode =
|
||||
dynamic_cast<gd::TextNode &>(*operatorNode.leftHandSide);
|
||||
REQUIRE(leftTextNode.text == "abc");
|
||||
auto &rightTextNode =
|
||||
dynamic_cast<gd::TextNode &>(*operatorNode.rightHandSide);
|
||||
REQUIRE(rightTextNode.text == "def");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("valid unary operators") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "-123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(unaryOperatorNode.type == "number");
|
||||
auto &numberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*unaryOperatorNode.factor);
|
||||
REQUIRE(numberNode.number == "123");
|
||||
@@ -295,6 +371,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
REQUIRE(unaryOperatorNode.op == '+');
|
||||
REQUIRE(unaryOperatorNode.type == "number");
|
||||
auto &numberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*unaryOperatorNode.factor);
|
||||
REQUIRE(numberNode.number == "123");
|
||||
@@ -304,6 +381,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(unaryOperatorNode.type == "number");
|
||||
auto &numberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*unaryOperatorNode.factor);
|
||||
REQUIRE(numberNode.number == "123.2");
|
||||
@@ -520,6 +598,26 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(validator.GetErrors().size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto node = parser.ParseExpression("object", "Hello World 1");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &identifierNode = dynamic_cast<gd::IdentifierNode &>(*node);
|
||||
REQUIRE(identifierNode.identifierName == "Hello World 1");
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetErrors().size() == 0);
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("object", "Hello World 1 ");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &identifierNode = dynamic_cast<gd::IdentifierNode &>(*node);
|
||||
REQUIRE(identifierNode.identifierName == "Hello World 1");
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetErrors().size() == 0);
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("object", "Hello World 1 ");
|
||||
REQUIRE(node != nullptr);
|
||||
@@ -550,9 +648,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetErrors().size() == 1);
|
||||
REQUIRE(
|
||||
validator.GetErrors()[0]->GetMessage() ==
|
||||
"Operators (+, -, /, *) can't be used with an object name. Remove the operator.");
|
||||
REQUIRE(validator.GetErrors()[0]->GetMessage() ==
|
||||
"Operators (+, -, /, *) can't be used with an object name. "
|
||||
"Remove the operator.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +658,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "MyExtension::GetNumber()");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "MyExtension::GetNumber");
|
||||
REQUIRE(functionNode.objectName == "");
|
||||
REQUIRE(functionNode.behaviorName == "");
|
||||
@@ -573,7 +671,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "MyExtension::GetNumberWith2Params(12, \"hello world\")");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "MyExtension::GetNumberWith2Params");
|
||||
REQUIRE(functionNode.objectName == "");
|
||||
REQUIRE(functionNode.behaviorName == "");
|
||||
@@ -586,7 +684,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "MyExtension::GetNumberWith3Params(12, \"hello world\")");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
@@ -597,7 +695,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
"number",
|
||||
"MyExtension::GetNumberWith3Params(12, \"hello world\", 34)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
@@ -607,7 +705,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
auto node =
|
||||
parser.ParseExpression("number", "MySpriteObject.GetObjectNumber()");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "GetObjectNumber");
|
||||
REQUIRE(functionNode.objectName == "MySpriteObject");
|
||||
REQUIRE(functionNode.behaviorName == "");
|
||||
@@ -620,7 +718,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "WhateverObject.WhateverBehavior::WhateverFunction()");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "WhateverFunction");
|
||||
REQUIRE(functionNode.objectName == "WhateverObject");
|
||||
REQUIRE(functionNode.behaviorName == "WhateverBehavior");
|
||||
@@ -630,7 +728,58 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
"number",
|
||||
"WhateverObject.WhateverBehavior::WhateverFunction(1, \"2\", three)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "WhateverFunction");
|
||||
REQUIRE(functionNode.objectName == "WhateverObject");
|
||||
REQUIRE(functionNode.behaviorName == "WhateverBehavior");
|
||||
REQUIRE(functionNode.parameters.size() == 3);
|
||||
auto &numberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*functionNode.parameters[0]);
|
||||
auto &textNode =
|
||||
dynamic_cast<gd::TextNode &>(*functionNode.parameters[1]);
|
||||
auto &identifierNode =
|
||||
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[2]);
|
||||
|
||||
REQUIRE(numberNode.number == "1");
|
||||
REQUIRE(textNode.text == "2");
|
||||
REQUIRE(identifierNode.identifierName == "three");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid function calls (whitespaces)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("number", "MyExtension::GetNumber ()");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "MyExtension::GetNumber");
|
||||
REQUIRE(functionNode.objectName == "");
|
||||
REQUIRE(functionNode.behaviorName == "");
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetErrors().size() == 0);
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "MySpriteObject . GetObjectNumber ()");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "GetObjectNumber");
|
||||
REQUIRE(functionNode.objectName == "MySpriteObject");
|
||||
REQUIRE(functionNode.behaviorName == "");
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetErrors().size() == 0);
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number",
|
||||
"WhateverObject . WhateverBehavior "
|
||||
":: WhateverFunction ( 1 , \"2\" "
|
||||
" , three )");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "WhateverFunction");
|
||||
REQUIRE(functionNode.objectName == "WhateverObject");
|
||||
REQUIRE(functionNode.behaviorName == "WhateverBehavior");
|
||||
@@ -653,7 +802,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "MyExtension::GetNumberWith3Params(12, \"hello world\",)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
@@ -665,7 +814,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "MyExtension::MouseX(,)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionNode &>(*node);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
@@ -673,6 +822,40 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid object function name") {
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyFunc");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyFunc");
|
||||
}
|
||||
|
||||
SECTION("Valid object behavior name") {
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::MyFunc");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.behaviorFunctionName == "MyFunc");
|
||||
}
|
||||
|
||||
SECTION("Unfinished object function name") {
|
||||
auto node = parser.ParseExpression("string", "MyObject.");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "");
|
||||
}
|
||||
|
||||
SECTION("Unfinished object behavior name") {
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.behaviorFunctionName == "");
|
||||
}
|
||||
|
||||
SECTION("Invalid function calls") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "Idontexist(12)");
|
||||
@@ -746,6 +929,45 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(validator.GetErrors()[0]->GetEndPosition() == 33);
|
||||
}
|
||||
}
|
||||
SECTION("Invalid free function call, finishing with namespace separator") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "MyExtension::(12)");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetErrors().size() == 2);
|
||||
REQUIRE(validator.GetErrors()[0]->GetMessage() ==
|
||||
"Cannot find an expression with this name: MyExtension::\nDouble "
|
||||
"check that you've not made any typo in the name.");
|
||||
REQUIRE(validator.GetErrors()[0]->GetStartPosition() == 0);
|
||||
REQUIRE(validator.GetErrors()[0]->GetEndPosition() == 17);
|
||||
REQUIRE(validator.GetErrors()[1]->GetMessage() ==
|
||||
"This parameter was not expected by this expression. Remove it "
|
||||
"or verify that you've entered the proper expression name.");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid behavior function call, finishing with namespace separator") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "MyObject.MyBehavior::(12)");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetErrors().size() == 2);
|
||||
|
||||
// TODO: The error message could be improved
|
||||
REQUIRE(validator.GetErrors()[0]->GetMessage() ==
|
||||
"Cannot find an expression with this name: \nDouble "
|
||||
"check that you've not made any typo in the name.");
|
||||
REQUIRE(validator.GetErrors()[0]->GetStartPosition() == 0);
|
||||
REQUIRE(validator.GetErrors()[0]->GetEndPosition() == 25);
|
||||
REQUIRE(validator.GetErrors()[1]->GetMessage() ==
|
||||
"This parameter was not expected by this expression. Remove it "
|
||||
"or verify that you've entered the proper expression name.");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid variables") {
|
||||
{
|
||||
@@ -795,7 +1017,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
|
||||
{
|
||||
auto node = parser.ParseExpression(
|
||||
"scenevar", "myVariable[ \"My named children\" ].grandChild");
|
||||
"scenevar", "myVariable[ \"My named children\" ] . grandChild");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &variableNode = dynamic_cast<gd::VariableNode &>(*node);
|
||||
REQUIRE(variableNode.name == "myVariable");
|
||||
@@ -853,4 +1075,425 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
"\fe'f\fwe'\te'w\f'reg[pto43o]"));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Location") {
|
||||
SECTION("Single node locations") {
|
||||
{
|
||||
auto node = parser.ParseExpression("string", "\"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &textNode = dynamic_cast<gd::TextNode &>(*node);
|
||||
REQUIRE(textNode.text == "hello world");
|
||||
REQUIRE(textNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(textNode.location.GetEndPosition() == 13);
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "3.14159");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &numberNode = dynamic_cast<gd::NumberNode &>(*node);
|
||||
REQUIRE(numberNode.number == "3.14159");
|
||||
REQUIRE(numberNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(numberNode.location.GetEndPosition() == 7);
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "345 + 678");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
REQUIRE(operatorNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(operatorNode.location.GetEndPosition() == 10);
|
||||
REQUIRE(operatorNode.leftHandSide != nullptr);
|
||||
REQUIRE(operatorNode.rightHandSide != nullptr);
|
||||
REQUIRE(operatorNode.leftHandSide->location.GetStartPosition() == 0);
|
||||
REQUIRE(operatorNode.leftHandSide->location.GetEndPosition() == 3);
|
||||
REQUIRE(operatorNode.rightHandSide->location.GetStartPosition() == 7);
|
||||
REQUIRE(operatorNode.rightHandSide->location.GetEndPosition() == 10);
|
||||
}
|
||||
}
|
||||
SECTION("Variable locations (simple variable name)") {
|
||||
auto node = parser.ParseExpression("scenevar", "MyVariable");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &variableNode = dynamic_cast<gd::VariableNode &>(*node);
|
||||
REQUIRE(variableNode.name == "MyVariable");
|
||||
REQUIRE(variableNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(variableNode.location.GetEndPosition() == 10);
|
||||
REQUIRE(variableNode.nameLocation.GetStartPosition() == 0);
|
||||
REQUIRE(variableNode.nameLocation.GetEndPosition() == 10);
|
||||
}
|
||||
SECTION("Variable locations (with child)") {
|
||||
auto node = parser.ParseExpression("scenevar", "MyVariable.MyChild");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &variableNode = dynamic_cast<gd::VariableNode &>(*node);
|
||||
REQUIRE(variableNode.name == "MyVariable");
|
||||
REQUIRE(variableNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(variableNode.location.GetEndPosition() == 18);
|
||||
REQUIRE(variableNode.nameLocation.GetStartPosition() == 0);
|
||||
REQUIRE(variableNode.nameLocation.GetEndPosition() == 10);
|
||||
REQUIRE(variableNode.child != nullptr);
|
||||
auto &childVariableAccessorNode =
|
||||
dynamic_cast<gd::VariableAccessorNode &>(*variableNode.child);
|
||||
REQUIRE(childVariableAccessorNode.location.GetStartPosition() == 10);
|
||||
REQUIRE(childVariableAccessorNode.location.GetEndPosition() == 18);
|
||||
REQUIRE(childVariableAccessorNode.dotLocation.GetStartPosition() == 10);
|
||||
REQUIRE(childVariableAccessorNode.dotLocation.GetEndPosition() == 11);
|
||||
REQUIRE(childVariableAccessorNode.nameLocation.GetStartPosition() == 11);
|
||||
REQUIRE(childVariableAccessorNode.nameLocation.GetEndPosition() == 18);
|
||||
}
|
||||
SECTION("Free function locations") {
|
||||
auto node =
|
||||
parser.ParseExpression("number", "WhateverFunction(1, \"2\", three)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "WhateverFunction");
|
||||
REQUIRE(functionNode.parameters.size() == 3);
|
||||
auto &numberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*functionNode.parameters[0]);
|
||||
auto &textNode =
|
||||
dynamic_cast<gd::TextNode &>(*functionNode.parameters[1]);
|
||||
auto &identifierNode =
|
||||
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[2]);
|
||||
|
||||
REQUIRE(numberNode.number == "1");
|
||||
REQUIRE(textNode.text == "2");
|
||||
REQUIRE(identifierNode.identifierName == "three");
|
||||
|
||||
REQUIRE(functionNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.location.GetEndPosition() == 31);
|
||||
REQUIRE(functionNode.objectNameLocation.IsValid() == false);
|
||||
REQUIRE(functionNode.objectNameDotLocation.IsValid() == false);
|
||||
REQUIRE(functionNode.behaviorNameLocation.IsValid() == false);
|
||||
REQUIRE(functionNode.behaviorNameNamespaceSeparatorLocation.IsValid() ==
|
||||
false);
|
||||
REQUIRE(functionNode.functionNameLocation.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.functionNameLocation.GetEndPosition() == 16);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetStartPosition() == 16);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetEndPosition() == 17);
|
||||
REQUIRE(numberNode.location.GetStartPosition() == 17);
|
||||
REQUIRE(numberNode.location.GetEndPosition() == 18);
|
||||
REQUIRE(textNode.location.GetStartPosition() == 20);
|
||||
REQUIRE(textNode.location.GetEndPosition() == 23);
|
||||
REQUIRE(identifierNode.location.GetStartPosition() == 25);
|
||||
REQUIRE(identifierNode.location.GetEndPosition() == 30);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetStartPosition() == 30);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetEndPosition() == 31);
|
||||
}
|
||||
SECTION("Free function locations (with whitespaces)") {
|
||||
auto node = parser.ParseExpression("number",
|
||||
"WhateverFunction (1, \"2\", three)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "WhateverFunction");
|
||||
REQUIRE(functionNode.parameters.size() == 3);
|
||||
|
||||
REQUIRE(functionNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.location.GetEndPosition() == 33);
|
||||
REQUIRE(functionNode.objectNameLocation.IsValid() == false);
|
||||
REQUIRE(functionNode.objectNameDotLocation.IsValid() == false);
|
||||
REQUIRE(functionNode.behaviorNameLocation.IsValid() == false);
|
||||
REQUIRE(functionNode.behaviorNameNamespaceSeparatorLocation.IsValid() ==
|
||||
false);
|
||||
REQUIRE(functionNode.functionNameLocation.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.functionNameLocation.GetEndPosition() == 16);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetStartPosition() == 18);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetEndPosition() == 19);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetStartPosition() == 32);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetEndPosition() == 33);
|
||||
}
|
||||
SECTION("Object function locations") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "WhateverObject.WhateverFunction(1, \"2\", three)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "WhateverFunction");
|
||||
REQUIRE(functionNode.objectName == "WhateverObject");
|
||||
REQUIRE(functionNode.parameters.size() == 3);
|
||||
auto &numberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*functionNode.parameters[0]);
|
||||
auto &textNode =
|
||||
dynamic_cast<gd::TextNode &>(*functionNode.parameters[1]);
|
||||
auto &identifierNode =
|
||||
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[2]);
|
||||
|
||||
REQUIRE(numberNode.number == "1");
|
||||
REQUIRE(textNode.text == "2");
|
||||
REQUIRE(identifierNode.identifierName == "three");
|
||||
|
||||
REQUIRE(functionNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.location.GetEndPosition() == 46);
|
||||
REQUIRE(functionNode.objectNameLocation.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.objectNameLocation.GetEndPosition() == 14);
|
||||
REQUIRE(functionNode.objectNameDotLocation.GetStartPosition() == 14);
|
||||
REQUIRE(functionNode.objectNameDotLocation.GetEndPosition() == 15);
|
||||
REQUIRE(functionNode.behaviorNameLocation.IsValid() == false);
|
||||
REQUIRE(functionNode.behaviorNameNamespaceSeparatorLocation.IsValid() ==
|
||||
false);
|
||||
REQUIRE(functionNode.functionNameLocation.GetStartPosition() == 15);
|
||||
REQUIRE(functionNode.functionNameLocation.GetEndPosition() == 31);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetStartPosition() == 31);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetEndPosition() == 32);
|
||||
REQUIRE(numberNode.location.GetStartPosition() == 32);
|
||||
REQUIRE(numberNode.location.GetEndPosition() == 33);
|
||||
REQUIRE(textNode.location.GetStartPosition() == 35);
|
||||
REQUIRE(textNode.location.GetEndPosition() == 38);
|
||||
REQUIRE(identifierNode.location.GetStartPosition() == 40);
|
||||
REQUIRE(identifierNode.location.GetEndPosition() == 45);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetStartPosition() == 45);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetEndPosition() == 46);
|
||||
}
|
||||
SECTION("Object function name locations") {
|
||||
auto node =
|
||||
parser.ParseExpression("number", "WhateverObject.WhateverFunction");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionNameNode =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionNameNode.objectName == "WhateverObject");
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorName ==
|
||||
"WhateverFunction");
|
||||
|
||||
REQUIRE(objectFunctionNameNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(objectFunctionNameNode.location.GetEndPosition() == 31);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetStartPosition() ==
|
||||
0);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetEndPosition() == 14);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetStartPosition() ==
|
||||
14);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetEndPosition() ==
|
||||
15);
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionNameLocation.IsValid() ==
|
||||
false);
|
||||
REQUIRE(objectFunctionNameNode.behaviorNameNamespaceSeparatorLocation
|
||||
.IsValid() == false);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetStartPosition() == 15);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetEndPosition() == 31);
|
||||
}
|
||||
SECTION("Object function name locations (with whitespace)") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "WhateverObject . WhateverFunction ");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionNameNode =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionNameNode.objectName == "WhateverObject");
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorName ==
|
||||
"WhateverFunction");
|
||||
|
||||
REQUIRE(objectFunctionNameNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(objectFunctionNameNode.location.GetEndPosition() == 37);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetStartPosition() ==
|
||||
0);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetEndPosition() == 14);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetStartPosition() ==
|
||||
16);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetEndPosition() ==
|
||||
17);
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionNameLocation.IsValid() ==
|
||||
false);
|
||||
REQUIRE(objectFunctionNameNode.behaviorNameNamespaceSeparatorLocation
|
||||
.IsValid() == false);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetStartPosition() == 19);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetEndPosition() == 35);
|
||||
}
|
||||
SECTION("Object function locations (with whitespaces)") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "WhateverObject . WhateverFunction (1, \"2\", three)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "WhateverFunction");
|
||||
REQUIRE(functionNode.objectName == "WhateverObject");
|
||||
|
||||
REQUIRE(functionNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.location.GetEndPosition() == 49);
|
||||
REQUIRE(functionNode.objectNameLocation.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.objectNameLocation.GetEndPosition() == 14);
|
||||
REQUIRE(functionNode.objectNameDotLocation.GetStartPosition() == 15);
|
||||
REQUIRE(functionNode.objectNameDotLocation.GetEndPosition() == 16);
|
||||
REQUIRE(functionNode.functionNameLocation.GetStartPosition() == 17);
|
||||
REQUIRE(functionNode.functionNameLocation.GetEndPosition() == 33);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetStartPosition() == 34);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetEndPosition() == 35);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetStartPosition() == 48);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetEndPosition() == 49);
|
||||
}
|
||||
SECTION("Behavior function locations") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number",
|
||||
"WhateverObject.WhateverBehavior::WhateverFunction(1, \"2\", three)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
REQUIRE(functionNode.functionName == "WhateverFunction");
|
||||
REQUIRE(functionNode.objectName == "WhateverObject");
|
||||
REQUIRE(functionNode.behaviorName == "WhateverBehavior");
|
||||
REQUIRE(functionNode.parameters.size() == 3);
|
||||
auto &numberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*functionNode.parameters[0]);
|
||||
auto &textNode =
|
||||
dynamic_cast<gd::TextNode &>(*functionNode.parameters[1]);
|
||||
auto &identifierNode =
|
||||
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[2]);
|
||||
|
||||
REQUIRE(numberNode.number == "1");
|
||||
REQUIRE(textNode.text == "2");
|
||||
REQUIRE(identifierNode.identifierName == "three");
|
||||
|
||||
REQUIRE(functionNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.location.GetEndPosition() == 64);
|
||||
REQUIRE(functionNode.objectNameLocation.GetStartPosition() == 0);
|
||||
REQUIRE(functionNode.objectNameLocation.GetEndPosition() == 14);
|
||||
REQUIRE(functionNode.objectNameDotLocation.GetStartPosition() == 14);
|
||||
REQUIRE(functionNode.objectNameDotLocation.GetEndPosition() == 15);
|
||||
REQUIRE(functionNode.behaviorNameLocation.GetStartPosition() == 15);
|
||||
REQUIRE(functionNode.behaviorNameLocation.GetEndPosition() == 31);
|
||||
REQUIRE(functionNode.behaviorNameNamespaceSeparatorLocation
|
||||
.GetStartPosition() == 31);
|
||||
REQUIRE(functionNode.behaviorNameNamespaceSeparatorLocation
|
||||
.GetEndPosition() == 33);
|
||||
REQUIRE(functionNode.functionNameLocation.GetStartPosition() == 33);
|
||||
REQUIRE(functionNode.functionNameLocation.GetEndPosition() == 49);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetStartPosition() == 49);
|
||||
REQUIRE(functionNode.openingParenthesisLocation.GetEndPosition() == 50);
|
||||
REQUIRE(numberNode.location.GetStartPosition() == 50);
|
||||
REQUIRE(numberNode.location.GetEndPosition() == 51);
|
||||
REQUIRE(textNode.location.GetStartPosition() == 53);
|
||||
REQUIRE(textNode.location.GetEndPosition() == 56);
|
||||
REQUIRE(identifierNode.location.GetStartPosition() == 58);
|
||||
REQUIRE(identifierNode.location.GetEndPosition() == 63);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetStartPosition() == 63);
|
||||
REQUIRE(functionNode.closingParenthesisLocation.GetEndPosition() == 64);
|
||||
}
|
||||
SECTION("Behavior function name locations (with whitespace)") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "WhateverObject . WhateverFunction ");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionNameNode =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionNameNode.objectName == "WhateverObject");
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorName ==
|
||||
"WhateverFunction");
|
||||
|
||||
REQUIRE(objectFunctionNameNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(objectFunctionNameNode.location.GetEndPosition() == 37);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetStartPosition() ==
|
||||
0);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetEndPosition() == 14);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetStartPosition() ==
|
||||
16);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetEndPosition() ==
|
||||
17);
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionNameLocation.IsValid() ==
|
||||
false);
|
||||
REQUIRE(objectFunctionNameNode.behaviorNameNamespaceSeparatorLocation
|
||||
.IsValid() == false);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetStartPosition() == 19);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetEndPosition() == 35);
|
||||
}
|
||||
SECTION("Behavior function name locations") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "WhateverObject.WhateverBehavior::WhateverFunction");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionNameNode =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionNameNode.objectName == "WhateverObject");
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorName ==
|
||||
"WhateverBehavior");
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionName ==
|
||||
"WhateverFunction");
|
||||
|
||||
REQUIRE(objectFunctionNameNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(objectFunctionNameNode.location.GetEndPosition() == 49);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetStartPosition() ==
|
||||
0);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetEndPosition() == 14);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetStartPosition() ==
|
||||
14);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetEndPosition() ==
|
||||
15);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetStartPosition() == 15);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetEndPosition() == 31);
|
||||
REQUIRE(objectFunctionNameNode.behaviorNameNamespaceSeparatorLocation
|
||||
.GetStartPosition() == 31);
|
||||
REQUIRE(objectFunctionNameNode.behaviorNameNamespaceSeparatorLocation
|
||||
.GetEndPosition() == 33);
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionNameLocation
|
||||
.GetStartPosition() == 33);
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionNameLocation
|
||||
.GetEndPosition() == 49);
|
||||
}
|
||||
SECTION("Behavior function name locations (with whitespace)") {
|
||||
auto node = parser.ParseExpression(
|
||||
"number", "WhateverObject.WhateverBehavior :: WhateverFunction");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionNameNode =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionNameNode.objectName == "WhateverObject");
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorName ==
|
||||
"WhateverBehavior");
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionName ==
|
||||
"WhateverFunction");
|
||||
|
||||
REQUIRE(objectFunctionNameNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(objectFunctionNameNode.location.GetEndPosition() == 53);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetStartPosition() ==
|
||||
0);
|
||||
REQUIRE(objectFunctionNameNode.objectNameLocation.GetEndPosition() == 14);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetStartPosition() ==
|
||||
14);
|
||||
REQUIRE(objectFunctionNameNode.objectNameDotLocation.GetEndPosition() ==
|
||||
15);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetStartPosition() == 15);
|
||||
REQUIRE(objectFunctionNameNode.objectFunctionOrBehaviorNameLocation
|
||||
.GetEndPosition() == 31);
|
||||
REQUIRE(objectFunctionNameNode.behaviorNameNamespaceSeparatorLocation
|
||||
.GetStartPosition() == 33);
|
||||
REQUIRE(objectFunctionNameNode.behaviorNameNamespaceSeparatorLocation
|
||||
.GetEndPosition() == 35);
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionNameLocation
|
||||
.GetStartPosition() == 37);
|
||||
REQUIRE(objectFunctionNameNode.behaviorFunctionNameLocation
|
||||
.GetEndPosition() == 53);
|
||||
}
|
||||
SECTION("Invalid/partial expression locations") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "3.14159 + ");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
REQUIRE(operatorNode.leftHandSide != nullptr);
|
||||
REQUIRE(operatorNode.rightHandSide != nullptr);
|
||||
REQUIRE(operatorNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(operatorNode.location.GetEndPosition() == 10);
|
||||
|
||||
auto &numberNode =
|
||||
dynamic_cast<gd::NumberNode &>(*operatorNode.leftHandSide);
|
||||
auto &emptyNode =
|
||||
dynamic_cast<gd::EmptyNode &>(*operatorNode.rightHandSide);
|
||||
REQUIRE(numberNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(numberNode.location.GetEndPosition() == 7);
|
||||
REQUIRE(emptyNode.location.GetStartPosition() == 10);
|
||||
REQUIRE(emptyNode.location.GetEndPosition() == 10);
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "\"abcde\" + ");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
REQUIRE(operatorNode.leftHandSide != nullptr);
|
||||
REQUIRE(operatorNode.rightHandSide != nullptr);
|
||||
REQUIRE(operatorNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(operatorNode.location.GetEndPosition() == 10);
|
||||
|
||||
auto &textNode =
|
||||
dynamic_cast<gd::TextNode &>(*operatorNode.leftHandSide);
|
||||
auto &emptyNode =
|
||||
dynamic_cast<gd::EmptyNode &>(*operatorNode.rightHandSide);
|
||||
REQUIRE(textNode.location.GetStartPosition() == 0);
|
||||
REQUIRE(textNode.location.GetEndPosition() == 7);
|
||||
REQUIRE(emptyNode.location.GetStartPosition() == 10);
|
||||
REQUIRE(emptyNode.location.GetEndPosition() == 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
122
Core/tests/ExpressionParser2Benchmarks.cpp
Normal file
122
Core/tests/ExpressionParser2Benchmarks.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
#include "DummyPlatform.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
TEST_CASE("ExpressionParser2 - Benchmarks", "[common][events]") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
|
||||
gd::ExpressionParser2 parser(platform, project, layout1);
|
||||
|
||||
auto parseExpression = [&parser](const gd::String &expression) {
|
||||
auto parseExpressionWithType = [&parser,
|
||||
&expression](const gd::String &type) {
|
||||
auto node = parser.ParseExpression(type, expression);
|
||||
REQUIRE(node != nullptr);
|
||||
};
|
||||
|
||||
parseExpressionWithType("number");
|
||||
parseExpressionWithType("string");
|
||||
parseExpressionWithType("scenevar");
|
||||
parseExpressionWithType("globalvar");
|
||||
parseExpressionWithType("objectvar");
|
||||
parseExpressionWithType("object");
|
||||
parseExpressionWithType("objectPtr");
|
||||
parseExpressionWithType("unknown");
|
||||
};
|
||||
|
||||
auto doBenchmark = [](const gd::String &benchmarkName,
|
||||
const size_t runsCount,
|
||||
std::function<void()> func) {
|
||||
std::vector<long long> timesInMicroseconds;
|
||||
|
||||
for (size_t i = 0; i < runsCount; i++) {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
func();
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
|
||||
timesInMicroseconds.push_back(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start)
|
||||
.count());
|
||||
}
|
||||
|
||||
std::cout << benchmarkName << " benchmark (" << runsCount << " runs): "
|
||||
<< (float)std::accumulate(timesInMicroseconds.begin(),
|
||||
timesInMicroseconds.end(),
|
||||
0) /
|
||||
(float)runsCount
|
||||
<< " microseconds" << std::endl;
|
||||
};
|
||||
|
||||
SECTION("Parse long expression") {
|
||||
doBenchmark("Parse long expression", 10, [&]() {
|
||||
REQUIRE_NOTHROW(parseExpression(
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+"
|
||||
"MySpriteObject.X()+MySpriteObject.X()/"
|
||||
"cos(3.123456789)+MySpriteObject.X()+0"));
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Parse long expression") {
|
||||
doBenchmark("Long identifier", 100, [&]() {
|
||||
REQUIRE_NOTHROW(parseExpression(
|
||||
"MyLoooooongIdentifierThatNeverStoooooopsAndContinueAgainAndAgainAndA"
|
||||
"gainAndAgainAndAgainAndAgainAndAgainAndAgainAndAgainAndAgainAndAgain"
|
||||
"AndAgainAndAgainAndAgainAndAgainAndAgainAndAgainAndAgain"));
|
||||
});
|
||||
}
|
||||
}
|
@@ -176,6 +176,23 @@ TEST_CASE("ExpressionParser2NodePrinter", "[common][events]") {
|
||||
"Idontexist(12, 34, \"56\" + 2)");
|
||||
}
|
||||
|
||||
SECTION("Valid function name") {
|
||||
SECTION("Free function") {
|
||||
testPrinter("number", "MyExtension::GetNumber");
|
||||
testPrinter("number", "MyExtension::GetNumberWith2Params");
|
||||
testPrinter("number", "MyExtension::UnknownFunc");
|
||||
testPrinter("number", "UnknownFunc");
|
||||
}
|
||||
SECTION("Object function") {
|
||||
testPrinter("number", "a.b");
|
||||
testPrinter("number", "MySpriteObject.GetObjectNumber");
|
||||
testPrinter("number", "MySpriteObject.MyOtherFunc");
|
||||
}
|
||||
SECTION("Behavior function") {
|
||||
testPrinter("number", "MySpriteObject.MyBehavior::MyFunc");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid variables") {
|
||||
testPrinter("scenevar", "myVariable");
|
||||
testPrinter("scenevar", "myVariable.myChild");
|
||||
|
@@ -11,16 +11,31 @@
|
||||
|
||||
TEST_CASE("SceneNameMangler", "[common]") {
|
||||
SECTION("Basics") {
|
||||
REQUIRE(gd::SceneNameMangler::GetMangledSceneName(
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"TotallyCorrectSceneName") == u8"TotallyCorrectSceneName");
|
||||
REQUIRE(gd::SceneNameMangler::GetMangledSceneName(
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"TotallyCorrectSceneName") == u8"TotallyCorrectSceneName");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"Totally NOT CorrectSceneName") ==
|
||||
u8"Totally_32NOT_32CorrectSceneName");
|
||||
REQUIRE(gd::SceneNameMangler::GetMangledSceneName(u8"Nouvelle scène") ==
|
||||
u8"Nouvelle_32sc_232ne");
|
||||
REQUIRE(gd::SceneNameMangler::GetMangledSceneName(u8"A test Ԙ") ==
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"Totally NOT CorrectSceneName") ==
|
||||
u8"Totally_32NOT_32CorrectSceneName");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"Nouvelle scène") == u8"Nouvelle_32sc_232ne");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"Nouvelle scène") == u8"Nouvelle_32sc_232ne");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(u8"A test Ԙ") ==
|
||||
u8"A_32test_32_1304");
|
||||
REQUIRE(gd::SceneNameMangler::GetMangledSceneName(u8"1 Nouvelle scène") ==
|
||||
u8"_49_32Nouvelle_32sc_232ne");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(u8"A test Ԙ") ==
|
||||
u8"A_32test_32_1304");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"1 Nouvelle scène") == u8"_49_32Nouvelle_32sc_232ne");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"1 Nouvelle scène") == u8"_49_32Nouvelle_32sc_232ne");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"汉语") == u8"_27721_35821");
|
||||
REQUIRE(gd::SceneNameMangler::Get()->GetMangledSceneName(
|
||||
u8"汉语") == u8"_27721_35821");
|
||||
}
|
||||
}
|
||||
|
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
/**
|
||||
* @file Tests covering common features of GDevelop Core.
|
||||
*/
|
||||
#include "GDCore/Events/Parsers/VariableParser.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
namespace {
|
||||
class TestVariableParserCallbacks : public gd::VariableParserCallbacks {
|
||||
public:
|
||||
TestVariableParserCallbacks(gd::String &debugStr)
|
||||
: VariableParserCallbacks(), debugStr(debugStr) {}
|
||||
|
||||
virtual void OnRootVariable(gd::String variableName) {
|
||||
debugStr += "OnRootVariable(" + variableName + ")\n";
|
||||
}
|
||||
|
||||
virtual void OnChildVariable(gd::String variableName) {
|
||||
debugStr += "OnChildVariable(" + variableName + ")\n";
|
||||
}
|
||||
|
||||
virtual void OnChildSubscript(gd::String stringExpression) {
|
||||
debugStr += "OnChildSubscript(" + stringExpression + ")\n";
|
||||
}
|
||||
|
||||
private:
|
||||
gd::String &debugStr;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("VariableParser", "[common][events]") {
|
||||
SECTION("Basics") {
|
||||
{
|
||||
gd::String str;
|
||||
TestVariableParserCallbacks callbacks(str);
|
||||
gd::VariableParser parser("myVar");
|
||||
parser.Parse(callbacks);
|
||||
|
||||
REQUIRE(str == "OnRootVariable(myVar)\n");
|
||||
}
|
||||
{
|
||||
gd::String str;
|
||||
TestVariableParserCallbacks callbacks(str);
|
||||
gd::VariableParser parser("myVar.child1.child2");
|
||||
parser.Parse(callbacks);
|
||||
|
||||
REQUIRE(str ==
|
||||
"OnRootVariable(myVar)\nOnChildVariable(child1)\nOnChildVariable("
|
||||
"child2)\n");
|
||||
}
|
||||
{
|
||||
gd::String str;
|
||||
TestVariableParserCallbacks callbacks(str);
|
||||
gd::VariableParser parser("myVar[ToString(i) + \"é\"].child");
|
||||
parser.Parse(callbacks);
|
||||
|
||||
REQUIRE(str ==
|
||||
"OnRootVariable(myVar)\nOnChildSubscript(ToString(i) + "
|
||||
"\"é\")\nOnChildVariable(child)\n");
|
||||
}
|
||||
}
|
||||
}
|
@@ -25,6 +25,39 @@
|
||||
|
||||
namespace {
|
||||
|
||||
gd::StandardEvent &EnsureStandardEvent(gd::BaseEvent &baseEvent) {
|
||||
gd::StandardEvent *standardEvent =
|
||||
dynamic_cast<gd::StandardEvent *>(&baseEvent);
|
||||
INFO("The inspected event is "
|
||||
<< (standardEvent ? "a standard event" : "not a standard event"));
|
||||
REQUIRE(standardEvent != nullptr);
|
||||
|
||||
return *standardEvent;
|
||||
}
|
||||
|
||||
const gd::String &GetEventFirstActionFirstParameterString(
|
||||
gd::BaseEvent &event) {
|
||||
auto &actions = EnsureStandardEvent(event).GetActions();
|
||||
REQUIRE(actions.IsEmpty() == false);
|
||||
REQUIRE(actions.Get(0).GetParametersCount() != 0);
|
||||
|
||||
return actions.Get(0).GetParameter(0).GetPlainString();
|
||||
}
|
||||
|
||||
const gd::String &GetEventFirstConditionType(gd::BaseEvent &event) {
|
||||
auto &conditions = EnsureStandardEvent(event).GetConditions();
|
||||
REQUIRE(conditions.IsEmpty() == false);
|
||||
|
||||
return conditions.Get(0).GetType();
|
||||
}
|
||||
|
||||
const gd::String &GetEventFirstActionType(gd::BaseEvent &event) {
|
||||
auto &actions = EnsureStandardEvent(event).GetActions();
|
||||
REQUIRE(actions.IsEmpty() == false);
|
||||
|
||||
return actions.Get(0).GetType();
|
||||
}
|
||||
|
||||
gd::EventsFunctionsExtension &SetupProjectWithEventsFunctionExtension(
|
||||
gd::Project &project) {
|
||||
auto &eventsExtension =
|
||||
@@ -126,8 +159,7 @@ gd::EventsFunctionsExtension &SetupProjectWithEventsFunctionExtension(
|
||||
layout.GetEvents().InsertEvent(event);
|
||||
}
|
||||
|
||||
// Create an event in the layout referring to
|
||||
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunction
|
||||
// Create an event in the layout using "MyProperty" action
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
@@ -138,6 +170,33 @@ gd::EventsFunctionsExtension &SetupProjectWithEventsFunctionExtension(
|
||||
layout.GetEvents().InsertEvent(event);
|
||||
}
|
||||
|
||||
// Create an event in the layout using "MyProperty" condition
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType(
|
||||
"MyEventsExtension::MyEventsBasedBehavior::" +
|
||||
gd::EventsBasedBehavior::GetPropertyConditionName("MyProperty"));
|
||||
event.GetConditions().Insert(instruction);
|
||||
layout.GetEvents().InsertEvent(event);
|
||||
}
|
||||
|
||||
// Create an event in the layout using "MyProperty" expression
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType("MyExtension::DoSomething");
|
||||
instruction.SetParametersCount(1);
|
||||
instruction.SetParameter(
|
||||
0,
|
||||
gd::Expression(
|
||||
"ObjectWithMyBehavior.MyBehavior::" +
|
||||
gd::EventsBasedBehavior::GetPropertyExpressionName("MyProperty") +
|
||||
"()"));
|
||||
event.GetActions().Insert(instruction);
|
||||
layout.GetEvents().InsertEvent(event);
|
||||
}
|
||||
|
||||
// Create an event in ExternalEvents1 referring to
|
||||
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpression
|
||||
{
|
||||
@@ -148,11 +207,62 @@ gd::EventsFunctionsExtension &SetupProjectWithEventsFunctionExtension(
|
||||
instruction.SetParameter(
|
||||
0,
|
||||
gd::Expression("1 + "
|
||||
"ObjectWithMyBehavior.MyBehavior::"
|
||||
"MyBehaviorEventsFunctionExpression(123, 456, 789)"));
|
||||
event.GetActions().Insert(instruction);
|
||||
externalEvents.GetEvents().InsertEvent(event);
|
||||
}
|
||||
|
||||
// Create an event in ExternalEvents1 **wrongly** referring to
|
||||
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpression
|
||||
// (it's ill-named).
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType("MyExtension::DoSomething");
|
||||
instruction.SetParametersCount(1);
|
||||
instruction.SetParameter(
|
||||
0,
|
||||
gd::Expression("2 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"MyBehaviorEventsFunctionExpression(123, 456, 789)"));
|
||||
event.GetActions().Insert(instruction);
|
||||
externalEvents.GetEvents().InsertEvent(event);
|
||||
}
|
||||
|
||||
// Create an event in ExternalEvents1 referring to
|
||||
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpression
|
||||
// function name without calling the function.
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType("MyExtension::DoSomething");
|
||||
instruction.SetParametersCount(1);
|
||||
instruction.SetParameter(
|
||||
0,
|
||||
gd::Expression("3 + "
|
||||
"ObjectWithMyBehavior.MyBehavior::"
|
||||
"MyBehaviorEventsFunctionExpression"));
|
||||
event.GetActions().Insert(instruction);
|
||||
externalEvents.GetEvents().InsertEvent(event);
|
||||
}
|
||||
|
||||
// Create an event in ExternalEvents1 **wrongly** referring to
|
||||
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpression
|
||||
// function name without calling the function (it's ill-named).
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType("MyExtension::DoSomething");
|
||||
instruction.SetParametersCount(1);
|
||||
instruction.SetParameter(
|
||||
0,
|
||||
gd::Expression("4 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"MyBehaviorEventsFunctionExpression"));
|
||||
event.GetActions().Insert(instruction);
|
||||
externalEvents.GetEvents().InsertEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
return eventsExtension;
|
||||
@@ -477,23 +587,16 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
project, eventsExtension, "MyEventsExtension", "MyRenamedExtension");
|
||||
|
||||
// Check that events function calls in instructions have been renamed
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetType() == "MyRenamedExtension::MyEventsFunction");
|
||||
REQUIRE(GetEventFirstActionType(project.GetLayout("LayoutWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0)) ==
|
||||
"MyRenamedExtension::MyEventsFunction");
|
||||
|
||||
// Check that events function calls in expressions have been renamed
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetParameter(0)
|
||||
.GetPlainString() ==
|
||||
.GetEvent(0)) ==
|
||||
"1 + MyRenamedExtension::MyEventsFunctionExpression(123, 456)");
|
||||
|
||||
// Check that the type of the behavior was changed in the behaviors of
|
||||
@@ -506,43 +609,41 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
.GetBehavior("MyBehavior")
|
||||
.GetTypeName() == "MyRenamedExtension::MyEventsBasedBehavior");
|
||||
|
||||
// Check if events based behaviors functions have been renamed in
|
||||
// Check if events-based behavior methods have been renamed in
|
||||
// instructions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetType() ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior::"
|
||||
"MyBehaviorEventsFunction");
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0)) ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior::"
|
||||
"MyBehaviorEventsFunction");
|
||||
|
||||
// Check if events based behaviors properties have been renamed in
|
||||
// Check if events-based behaviors properties have been renamed in
|
||||
// instructions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(1))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetType() ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior::"
|
||||
"SetPropertyMyProperty");
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(1)) ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior::"
|
||||
"SetPropertyMyProperty");
|
||||
|
||||
// Check events based behaviors functions have *not* been renamed in
|
||||
// Check events-based behavior methods have *not* been renamed in
|
||||
// expressions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetParameter(0)
|
||||
.GetPlainString() ==
|
||||
.GetEvent(0)) ==
|
||||
"1 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"ObjectWithMyBehavior.MyBehavior::"
|
||||
"MyBehaviorEventsFunctionExpression(123, 456, 789)");
|
||||
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(2)) ==
|
||||
"3 + "
|
||||
"ObjectWithMyBehavior.MyBehavior::"
|
||||
"MyBehaviorEventsFunctionExpression");
|
||||
}
|
||||
SECTION("(Free) events function renamed") {
|
||||
gd::Project project;
|
||||
@@ -561,23 +662,16 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
"MyRenamedFunctionExpression");
|
||||
|
||||
// Check that events function calls in instructions have been renamed
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetType() == "MyEventsExtension::MyRenamedEventsFunction");
|
||||
REQUIRE(GetEventFirstActionType(project.GetLayout("LayoutWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0)) ==
|
||||
"MyEventsExtension::MyRenamedEventsFunction");
|
||||
|
||||
// Check that events function calls in expressions have been renamed
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetParameter(0)
|
||||
.GetPlainString() ==
|
||||
.GetEvent(0)) ==
|
||||
"1 + MyEventsExtension::MyRenamedFunctionExpression(123, 456)");
|
||||
}
|
||||
SECTION("(Free) events function parameter moved") {
|
||||
@@ -586,36 +680,27 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
|
||||
|
||||
gd::WholeProjectRefactorer::MoveEventsFunctionParameter(project,
|
||||
eventsExtension,
|
||||
"MyEventsFunction",
|
||||
0, 2);
|
||||
gd::WholeProjectRefactorer::MoveEventsFunctionParameter(
|
||||
project,
|
||||
eventsExtension,
|
||||
"MyEventsFunctionExpression",
|
||||
0, 1);
|
||||
project, eventsExtension, "MyEventsFunction", 0, 2);
|
||||
gd::WholeProjectRefactorer::MoveEventsFunctionParameter(
|
||||
project, eventsExtension, "MyEventsFunctionExpression", 0, 1);
|
||||
|
||||
// Check that events function calls in instructions have been updated
|
||||
auto& action = static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0);
|
||||
auto &action = static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0);
|
||||
REQUIRE(action.GetParameter(0).GetPlainString() == "Second parameter");
|
||||
REQUIRE(action.GetParameter(1).GetPlainString() == "Third parameter");
|
||||
REQUIRE(action.GetParameter(2).GetPlainString() == "First parameter");
|
||||
|
||||
// Check that events function calls in expressions have been updated
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithFreeFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetParameter(0)
|
||||
.GetPlainString() ==
|
||||
.GetEvent(0)) ==
|
||||
"1 + MyEventsExtension::MyEventsFunctionExpression(456, 123)");
|
||||
}
|
||||
SECTION("Events based Behavior type renamed") {
|
||||
@@ -644,42 +729,32 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
.GetTypeName() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior");
|
||||
|
||||
// Check if events based behaviors functions have been renamed in
|
||||
// Check if events-based behavior methods have been renamed in
|
||||
// instructions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetType() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
|
||||
"MyBehaviorEventsFunction");
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0)) ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
|
||||
"MyBehaviorEventsFunction");
|
||||
|
||||
// Check if events based behaviors properties have been renamed in
|
||||
// Check if events-based behaviors properties have been renamed in
|
||||
// instructions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(1))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetType() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
|
||||
"SetPropertyMyProperty");
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(1)) ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
|
||||
"SetPropertyMyProperty");
|
||||
|
||||
// Check events based behaviors functions have *not* been renamed in
|
||||
// Check events-based behavior methods have *not* been renamed in
|
||||
// expressions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetParameter(0)
|
||||
.GetPlainString() ==
|
||||
.GetEvent(0)) ==
|
||||
"1 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"ObjectWithMyBehavior.MyBehavior::"
|
||||
"MyBehaviorEventsFunctionExpression(123, 456, 789)");
|
||||
}
|
||||
SECTION("(Events based Behavior) events function renamed") {
|
||||
@@ -703,31 +778,54 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
"MyBehaviorEventsFunctionExpression",
|
||||
"MyRenamedBehaviorEventsFunctionExpression");
|
||||
|
||||
// Check if events based behaviors functions have been renamed in
|
||||
// Check if events-based behavior methods have been renamed in
|
||||
// instructions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetType() ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::"
|
||||
"MyRenamedBehaviorEventsFunction");
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0)) ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::"
|
||||
"MyRenamedBehaviorEventsFunction");
|
||||
|
||||
// Check events based behaviors functions have been renamed in
|
||||
// Check events-based behavior methods have been renamed in
|
||||
// expressions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetParameter(0)
|
||||
.GetPlainString() ==
|
||||
.GetEvent(0)) ==
|
||||
"1 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"ObjectWithMyBehavior.MyBehavior::"
|
||||
"MyRenamedBehaviorEventsFunctionExpression(123, 456, 789)");
|
||||
|
||||
// Check that a ill-named function that looks a bit like a behavior method
|
||||
// (but which is actually an object function) is *not* renamed.
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(1)) ==
|
||||
"2 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"MyBehaviorEventsFunctionExpression(123, 456, 789)");
|
||||
|
||||
// Check events based behaviors functions have been renamed in
|
||||
// expressions referring to the function with just its name
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(2)) ==
|
||||
"3 + "
|
||||
"ObjectWithMyBehavior.MyBehavior::"
|
||||
"MyRenamedBehaviorEventsFunctionExpression");
|
||||
|
||||
// Check that a ill-named function that looks a bit like a behavior method
|
||||
// (but which is actually an object function) is *not* renamed.
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(3)) ==
|
||||
"4 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"MyBehaviorEventsFunctionExpression");
|
||||
}
|
||||
SECTION("(Events based Behavior) events function parameter moved") {
|
||||
gd::Project project;
|
||||
@@ -742,39 +840,47 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
eventsExtension,
|
||||
eventsBasedBehavior,
|
||||
"MyBehaviorEventsFunction",
|
||||
0, 2);
|
||||
0,
|
||||
2);
|
||||
gd::WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
|
||||
project,
|
||||
eventsExtension,
|
||||
eventsBasedBehavior,
|
||||
"MyBehaviorEventsFunctionExpression",
|
||||
0, 2);
|
||||
0,
|
||||
2);
|
||||
|
||||
// Check if events based behaviors functions have been renamed in
|
||||
// Check if parameters of events-based behavior methods have been moved in
|
||||
// instructions
|
||||
auto& action = static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0);
|
||||
auto &action = static_cast<gd::StandardEvent &>(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0);
|
||||
REQUIRE(action.GetParameter(0).GetPlainString() == "Second parameter");
|
||||
REQUIRE(action.GetParameter(1).GetPlainString() == "Third parameter");
|
||||
REQUIRE(action.GetParameter(2).GetPlainString() == "First parameter");
|
||||
|
||||
// Check events based behaviors functions have been renamed in
|
||||
// Check parameters of events-based behavior methods have been moved in
|
||||
// expressions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(0))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetParameter(0)
|
||||
.GetPlainString() ==
|
||||
.GetEvent(0)) ==
|
||||
"1 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"ObjectWithMyBehavior.MyBehavior::"
|
||||
"MyBehaviorEventsFunctionExpression(456, 789, 123)");
|
||||
|
||||
// Check that a ill-named function that looks a bit like a behavior method
|
||||
// (but which is actually a free function) has its parameter *not* moved.
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetExternalEvents("ExternalEventsWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(1)) ==
|
||||
"2 + "
|
||||
"ObjectWithMyBehavior::MyBehavior."
|
||||
"MyBehaviorEventsFunctionExpression(123, 456, 789)");
|
||||
}
|
||||
SECTION("(Events based Behavior) property renamed") {
|
||||
gd::Project project;
|
||||
@@ -790,16 +896,26 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
"MyProperty",
|
||||
"MyRenamedProperty");
|
||||
|
||||
// Check if events based behaviors property has been renamed in
|
||||
// Check if events-based behaviors property has been renamed in
|
||||
// instructions
|
||||
REQUIRE(static_cast<gd::StandardEvent &>(
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(1)) ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::"
|
||||
"SetPropertyMyRenamedProperty");
|
||||
|
||||
REQUIRE(GetEventFirstConditionType(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(1))
|
||||
.GetActions()
|
||||
.Get(0)
|
||||
.GetType() ==
|
||||
.GetEvent(2)) ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::"
|
||||
"SetPropertyMyRenamedProperty");
|
||||
"PropertyMyRenamedProperty");
|
||||
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
project.GetLayout("LayoutWithBehaviorFunctions")
|
||||
.GetEvents()
|
||||
.GetEvent(3)) ==
|
||||
"ObjectWithMyBehavior.MyBehavior::PropertyMyRenamedProperty()");
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
#Clone SFML from its official directory using Git
|
||||
# Clone SFML from its official directory using Git. Only
|
||||
# do it if not already cloned to avoid triggering a whole
|
||||
# recompilation every time CMake is run.
|
||||
find_package(Git)
|
||||
if(GIT_FOUND)
|
||||
if(GIT_FOUND AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/SFML/readme.txt")
|
||||
message( "Cloning SFML in ExtLibs/SFML with Git..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} clone "https://www.github.com/SFML/SFML.git" SFML
|
||||
@@ -11,7 +13,7 @@ if(GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} reset --hard 2.4.1
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
|
||||
OUTPUT_QUIET)
|
||||
OUTPUT_QUIET)
|
||||
|
||||
message( "Applying the patches..." )
|
||||
file(GLOB SFML_PATCHES
|
||||
|
@@ -24,7 +24,7 @@ gdjs.AnchorRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
};
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.AnchorRuntimeBehavior.thisIsARuntimeBehaviorConstructor = "AnchorBehavior::AnchorBehavior";
|
||||
gdjs.registerBehavior("AnchorBehavior::AnchorBehavior", gdjs.AnchorRuntimeBehavior);
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.HorizontalAnchor = {
|
||||
NONE: 0,
|
||||
|
@@ -35,6 +35,8 @@ module.exports = {
|
||||
if (propertyName in objectContent) {
|
||||
if (typeof objectContent[propertyName] === 'boolean')
|
||||
objectContent[propertyName] = newValue === '1';
|
||||
else if (typeof objectContent[propertyName] === 'number')
|
||||
objectContent[propertyName] = parseFloat(newValue);
|
||||
else objectContent[propertyName] = newValue;
|
||||
return true;
|
||||
}
|
||||
@@ -67,7 +69,7 @@ module.exports = {
|
||||
|
||||
objectProperties.set(
|
||||
'fontSize',
|
||||
new gd.PropertyDescriptor(objectContent.fontSize)
|
||||
new gd.PropertyDescriptor(objectContent.fontSize.toString())
|
||||
.setType('number')
|
||||
.setLabel(_('Base size'))
|
||||
);
|
||||
@@ -85,7 +87,8 @@ module.exports = {
|
||||
objectProperties.set(
|
||||
'fontFamily',
|
||||
new gd.PropertyDescriptor(objectContent.fontFamily)
|
||||
.setType('string')
|
||||
.setType('resource')
|
||||
.addExtraInfo('font')
|
||||
.setLabel(_('Base font family'))
|
||||
);
|
||||
|
||||
@@ -110,7 +113,7 @@ module.exports = {
|
||||
text:
|
||||
'[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
|
||||
opacity: 255,
|
||||
fontSize: '20',
|
||||
fontSize: 20,
|
||||
visible: true,
|
||||
color: '#000000',
|
||||
fontFamily: 'Arial',
|
||||
@@ -409,7 +412,7 @@ module.exports = {
|
||||
objectsEditorService.registerEditorConfiguration(
|
||||
'BBText::BBText',
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||||
helpPagePath: '/objects/bbtext_object',
|
||||
helpPagePath: '/objects/bbtext',
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -515,11 +518,25 @@ module.exports = {
|
||||
.getValue();
|
||||
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
|
||||
|
||||
const fontFamily = this._associatedObject
|
||||
const fontResourceName = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('fontFamily')
|
||||
.getValue();
|
||||
this._pixiObject.textStyles.default.fontFamily = fontFamily;
|
||||
|
||||
if (this._fontResourceName !== fontResourceName) {
|
||||
this._fontResourceName = fontResourceName;
|
||||
|
||||
this._pixiResourcesLoader
|
||||
.loadFontFamily(this._project, fontResourceName)
|
||||
.then(fontFamily => {
|
||||
// Once the font is loaded, we can use the given fontFamily.
|
||||
this._pixiObject.textStyles.default.fontFamily = fontFamily;
|
||||
})
|
||||
.catch(err => {
|
||||
// Ignore errors
|
||||
console.warn('Unable to load font family', err);
|
||||
});
|
||||
}
|
||||
|
||||
const wordWrap = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
|
@@ -13,7 +13,10 @@ gdjs.BBTextRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene) {
|
||||
if (this._pixiObject === undefined) {
|
||||
this._pixiObject = new MultiStyleText(runtimeObject._text, {
|
||||
default: {
|
||||
fontFamily: runtimeObject._fontFamily,
|
||||
fontFamily: runtimeScene
|
||||
.getGame()
|
||||
.getFontManager()
|
||||
.getFontFamily(runtimeObject._fontFamily),
|
||||
fontSize: runtimeObject._fontSize + 'px',
|
||||
fill: runtimeObject._color,
|
||||
tagStyle: 'bbcode',
|
||||
@@ -73,6 +76,7 @@ gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateText = function() {
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateColor = function() {
|
||||
this._pixiObject.textStyles.default.fill = this._object._color;
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAlignment = function() {
|
||||
@@ -80,10 +84,12 @@ gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAlignment = function() {
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontFamily = function() {
|
||||
this._pixiObject.textStyles.default.fontFamily = this._object._fontFamily;
|
||||
this._pixiObject.textStyles.default.fontFamily = this._object._runtimeScene.getGame().getFontManager().getFontFamily(this._object._fontFamily);
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontSize = function() {
|
||||
this._pixiObject.textStyles.default.fontSize = this._object._fontSize + 'px';
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updatePosition = function() {
|
||||
|
@@ -25,7 +25,8 @@ gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
gdjs.RuntimeObject.call(this, runtimeScene, objectData);
|
||||
|
||||
/** @type {number} */
|
||||
this._opacity = objectData.content.opacity;
|
||||
this._opacity = parseFloat(objectData.content.opacity);
|
||||
// parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
|
||||
/** @type {boolean} */
|
||||
this._visible = objectData.content.visible;
|
||||
/** @type {string} */
|
||||
@@ -35,7 +36,8 @@ gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
/** @type {string} */
|
||||
this._fontFamily = objectData.content.fontFamily;
|
||||
/** @type {number} */
|
||||
this._fontSize = objectData.content.fontSize;
|
||||
this._fontSize = parseFloat(objectData.content.fontSize);
|
||||
// parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
|
||||
/** @type {boolean} */
|
||||
this._wordWrap = objectData.content.wordWrap;
|
||||
/** @type {number} */
|
||||
@@ -56,7 +58,7 @@ gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
gdjs.BBTextRuntimeObject.prototype = Object.create(
|
||||
gdjs.RuntimeObject.prototype
|
||||
);
|
||||
gdjs.BBTextRuntimeObject.thisIsARuntimeObjectConstructor = 'BBText::BBText';
|
||||
gdjs.registerObject('BBText::BBText', gdjs.BBTextRuntimeObject);
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
|
File diff suppressed because one or more lines are too long
@@ -50,13 +50,18 @@ function(gd_add_extension_target target_name source_files)
|
||||
IF(target_name STREQUAL "")
|
||||
MESSAGE(ERROR "You called gd_add_extension_target without specifying a target name")
|
||||
ENDIF()
|
||||
|
||||
|
||||
SET(platform_directory ${ARGV2})
|
||||
IF(NOT platform_directory)
|
||||
SET(platform_directory "CppPlatform")
|
||||
ENDIF()
|
||||
|
||||
add_library(${target_name} SHARED ${source_files})
|
||||
IF(EMSCRIPTEN)
|
||||
# Emscripten treats all libraries as static libraries
|
||||
add_library(${target_name} STATIC ${source_files})
|
||||
ELSE()
|
||||
add_library(${target_name} SHARED ${source_files})
|
||||
ENDIF()
|
||||
set_target_properties(${target_name} PROPERTIES PREFIX "")
|
||||
set_target_properties(${target_name} PROPERTIES COMPILE_DEFINITIONS "${${target_name}_extra_definitions}")
|
||||
set_target_properties(${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/${platform_directory}/Extensions")
|
||||
|
@@ -18,7 +18,7 @@ gdjs.DestroyOutsideRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
};
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.DestroyOutsideRuntimeBehavior.thisIsARuntimeBehaviorConstructor = "DestroyOutsideBehavior::DestroyOutside";
|
||||
gdjs.registerBehavior("DestroyOutsideBehavior::DestroyOutside", gdjs.DestroyOutsideRuntimeBehavior);
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
|
||||
|
@@ -45,7 +45,7 @@ module.exports = {
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DialogueTree/dialoguetools.js')
|
||||
.addIncludeFile('Extensions/DialogueTree/bondage.min.js')
|
||||
.addIncludeFile('Extensions/DialogueTree/bondage.js/dist/bondage.min.js')
|
||||
.setFunctionName('gdjs.dialogueTree.loadFromSceneVariable');
|
||||
|
||||
extension
|
||||
@@ -69,7 +69,7 @@ module.exports = {
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DialogueTree/dialoguetools.js')
|
||||
.addIncludeFile('Extensions/DialogueTree/bondage.min.js')
|
||||
.addIncludeFile('Extensions/DialogueTree/bondage.js/dist/bondage.min.js')
|
||||
.setFunctionName('gdjs.dialogueTree.loadFromJsonFile');
|
||||
|
||||
extension
|
||||
@@ -168,15 +168,14 @@ module.exports = {
|
||||
_(
|
||||
'Select option by number. Use this when the dialogue line is of type "options" and the player has pressed a button to change selected option.'
|
||||
),
|
||||
_('Select option by number'),
|
||||
_('Select option at index _PARAM0_'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('expression', _('Option index number'), '', false)
|
||||
.setDefaultValue('0')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.selectPreviousOption');
|
||||
.setFunctionName('gdjs.dialogueTree.selectOption');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
@@ -210,21 +209,55 @@ module.exports = {
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetVariable',
|
||||
_('Set dialogue state variable'),
|
||||
'SetStringVariable',
|
||||
_('Set dialogue state string variable'),
|
||||
_(
|
||||
'Set dialogue state variable. Use this to set a variable that the dialogue data is using.'
|
||||
'Set dialogue state string variable. Use this to set a variable that the dialogue data is using.'
|
||||
),
|
||||
_('Set dialogue state variable _PARAM0_ to _PARAM1_'),
|
||||
_('Set dialogue state string variable _PARAM0_ to _PARAM1_'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('State Variable Name'), '', false)
|
||||
.addParameter('expression', _('Variable Value'), '', false)
|
||||
.addParameter('string', _('Variable string value'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.setVariable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetNumberVariable',
|
||||
_('Set dialogue state number variable'),
|
||||
_(
|
||||
'Set dialogue state number variable. Use this to set a variable that the dialogue data is using.'
|
||||
),
|
||||
_('Set dialogue state number variable _PARAM0_ to _PARAM1_'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('State Variable Name'), '', false)
|
||||
.addParameter('expression', _('Variable number value'), '', true)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.setVariable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetBooleanVariable',
|
||||
_('Set dialogue state boolean variable'),
|
||||
_(
|
||||
'Set dialogue state boolean variable. Use this to set a variable that the dialogue data is using.'
|
||||
),
|
||||
_('Set dialogue state boolean variable _PARAM0_ to _PARAM1_'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('State Variable Name'), '', false)
|
||||
.addParameter('trueorfalse', _('Variable boolean value'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.setVariable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SaveState',
|
||||
@@ -257,6 +290,21 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.loadState');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'ClearState',
|
||||
_('Clear dialogue state'),
|
||||
_(
|
||||
'Clear dialogue state. This resets all dialogue state accumulated by the player choices. Useful when the player is starting a new game.'
|
||||
),
|
||||
_('Clear dialogue state'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.clearState');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'LineText',
|
||||
@@ -592,9 +640,9 @@ module.exports = {
|
||||
extension
|
||||
.addCondition(
|
||||
'WasBranchVisited',
|
||||
_('Branch title has been visited before'),
|
||||
_('Check if the current branch has been visited before'),
|
||||
_('Branch title _PARAM0_ has been visited before'),
|
||||
_('Branch title has been visited'),
|
||||
_('Check if a branch has been visited'),
|
||||
_('Branch title _PARAM0_ has been visited'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
@@ -605,12 +653,12 @@ module.exports = {
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'CompareDialogueStateVariable',
|
||||
_('Compare dialogue state variable'),
|
||||
'CompareDialogueStateStringVariable',
|
||||
_('Compare dialogue state string variable'),
|
||||
_(
|
||||
'Compare dialogue state variable. Use this to trigger game events via dialogue variables.'
|
||||
'Compare dialogue state string variable. Use this to trigger game events via dialogue variables.'
|
||||
),
|
||||
_('Dialogue state variable _PARAM0_ is equal to _PARAM1_'),
|
||||
_('Dialogue state string variable _PARAM0_ is equal to _PARAM1_'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
@@ -620,6 +668,40 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.compareVariable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'CompareDialogueStateNumberVariable',
|
||||
_('Compare dialogue state number variable'),
|
||||
_(
|
||||
'Compare dialogue state number variable. Use this to trigger game events via dialogue variables.'
|
||||
),
|
||||
_('Dialogue state number variable _PARAM0_ is equal to _PARAM1_'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('State variable'), '', false)
|
||||
.addParameter('expression', _('Equal to'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.compareVariable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'CompareDialogueStateBooleanVariable',
|
||||
_('Compare dialogue state boolean variable'),
|
||||
_(
|
||||
'Compare dialogue state variable. Use this to trigger game events via dialogue variables.'
|
||||
),
|
||||
_('Dialogue state boolean variable _PARAM0_ is equal to _PARAM1_'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('State variable'), '', false)
|
||||
.addParameter('trueorfalse', _('Equal to'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.compareVariable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'HasClippedTextScrollingCompleted',
|
||||
|
21
Extensions/DialogueTree/bondage.js/LICENSE.txt
Normal file
21
Extensions/DialogueTree/bondage.js/LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 j hayley
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
66
Extensions/DialogueTree/bondage.js/README.md
Normal file
66
Extensions/DialogueTree/bondage.js/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# bondage.js [](https://travis-ci.org/jhayley/bondage.js)
|
||||
[Yarn](https://github.com/InfiniteAmmoInc/Yarn) parser for Javascript, in the same vein as [YarnSpinner](https://github.com/thesecretlab/YarnSpinner).
|
||||
|
||||
# Usage
|
||||
|
||||
#### As a Web Tool
|
||||
|
||||
To run through your yarn files in your browser, go to http://hayley.zone/bondage.js, paste your yarn data in the field, then hit "compile".
|
||||
|
||||
#### As a Command Line Tool
|
||||
Installation: `npm install -g bondage`
|
||||
|
||||
Now you can use the `bondage` command to run through Yarn files from the command line. You can load one or multiple files at a time. If you load multiple files and a two nodes are encountered with the same name, the node will be overwritten.
|
||||
|
||||
**Examples**
|
||||
|
||||
* Running a single file from the default start node (named "Start"): `bondage run yarnfile.json`
|
||||
* Running a single file from the specified node name: `bondage run -s StartNode yarnfile.json`
|
||||
* Running multiple files from the specified node name: `bondage run -s StartNode yarnfile1.json yarnfile2.json ...`
|
||||
* See the compiled ast: `bondage compile --ast yarnfile.json`
|
||||
* See the tokenized input: `bondage compile --tokens yarnfile.json`
|
||||
|
||||
#### As a Library
|
||||
|
||||
**Web**
|
||||
|
||||
Include [dist/bondage.min.js](https://github.com/jhayley/bondage.js/blob/master/dist/bondage.min.js) somewhere in your html, and the `bondage` variable will be added to the global scope. You can then access everything in the example below (such as `bondage.Runner`) through that variable.
|
||||
|
||||
**Node**
|
||||
|
||||
Installation: `npm install bondage`
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const bondage = require('bondage');
|
||||
|
||||
const runner = new bondage.Runner();
|
||||
const yarnData = JSON.parse(fs.readFileSync('yarnFile.json'));
|
||||
|
||||
runner.load(yarnData);
|
||||
|
||||
// Loop over the dialogue from the node titled 'Start'
|
||||
for (const result of runner.run('Start')) {
|
||||
// Do something else with the result
|
||||
if (result instanceof bondage.TextResult) {
|
||||
console.log(result.text);
|
||||
} else if (result instanceof bondage.OptionsResult) {
|
||||
// This works for both links between nodes and shortcut options
|
||||
console.log(result.options);
|
||||
|
||||
// Select based on the option's index in the array (if you don't select an option, the dialog will continue past them)
|
||||
result.select(1);
|
||||
} else if (result instanceof bondage.CommandResult) {
|
||||
// If the text was inside <<here>>, it will get returned as a CommandResult string, which you can use in any way you want
|
||||
console.log(result.text);
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the dialogue manually from the node titled 'Start'
|
||||
const d = runner.run('Start')
|
||||
let result = d.next().value;
|
||||
let nextResult = d.next().value;
|
||||
// And so on
|
||||
```
|
||||
|
||||
For usage of the yarn format itself, please see the [YarnSpinner Documentation](https://github.com/thesecretlab/YarnSpinner/tree/master/Documentation), everything there should carry here too (if something does not match up, please open an issue).
|
File diff suppressed because one or more lines are too long
5
Extensions/DialogueTree/bondage.js/version.txt
Normal file
5
Extensions/DialogueTree/bondage.js/version.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
This extension is using bondage.js library to parse yarn syntax.
|
||||
https://github.com/hylyh/bondage.js
|
||||
|
||||
The current build used is built from commit 3c63e21
|
||||
|
@@ -101,7 +101,20 @@ gdjs.dialogueTree.isRunning = function() {
|
||||
gdjs.dialogueTree.scrollClippedText = function() {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning) return;
|
||||
|
||||
if (this.dialogueText) {
|
||||
// Autoscroll commands so the user doesnt have to press again
|
||||
if (
|
||||
gdjs.dialogueTree._isLineTypeCommand() &&
|
||||
this.dialogueDataType === 'text' &&
|
||||
this.dialogueBranchTitle === this.dialogueData.data.title &&
|
||||
this.lineNum === this.dialogueData.lineNum &&
|
||||
gdjs.dialogueTree.hasClippedScrollingCompleted()
|
||||
) {
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
return
|
||||
}
|
||||
|
||||
// Increment scrolling of clipped text
|
||||
if (this.dialogueText && this.dialogueDataType === 'text' && this.clipTextEnd < this.dialogueText.length) {
|
||||
this.clipTextEnd += 1;
|
||||
}
|
||||
};
|
||||
@@ -110,7 +123,7 @@ gdjs.dialogueTree.scrollClippedText = function() {
|
||||
* Scroll the clipped text to its end, so the entire text is printed. This can be useful in keeping the event sheet logic simpler, while supporting more variation.
|
||||
*/
|
||||
gdjs.dialogueTree.completeClippedTextScrolling = function() {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning || !this.dialogueText)
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning || !this.dialogueText || this.dialogueDataType !== 'text')
|
||||
return;
|
||||
this.clipTextEnd = this.dialogueText.length;
|
||||
};
|
||||
@@ -120,8 +133,11 @@ gdjs.dialogueTree.completeClippedTextScrolling = function() {
|
||||
* Useful to prevent the user from skipping to next line before the current one has been printed fully.
|
||||
*/
|
||||
gdjs.dialogueTree.hasClippedScrollingCompleted = function() {
|
||||
if (this.dialogueData && this.dialogueText.length) {
|
||||
return this.clipTextEnd >= this.dialogueText.length;
|
||||
if (!this.dialogueIsRunning || this.dialogueDataType === '') return false;
|
||||
|
||||
if (this.dialogueData && this.dialogueText.length > 0 && this.clipTextEnd >= this.dialogueText.length) {
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.warn('Scroll completed:', this.clipTextEnd,'/', this.dialogueText.length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -131,8 +147,8 @@ gdjs.dialogueTree.hasClippedScrollingCompleted = function() {
|
||||
* Used with the scrollClippedText to achieve a classic scrolling text, as well as any <<wait>> effects to pause scrolling.
|
||||
*/
|
||||
gdjs.dialogueTree.getClippedLineText = function() {
|
||||
return this.dialogueText.length
|
||||
? this.dialogueText.substring(0, this.clipTextEnd)
|
||||
return this.dialogueIsRunning && this.dialogueText.length
|
||||
? this.dialogueText.substring(0, this.clipTextEnd + 1)
|
||||
: '';
|
||||
};
|
||||
|
||||
@@ -141,8 +157,9 @@ gdjs.dialogueTree.getClippedLineText = function() {
|
||||
* Note that using this instead getClippedLineText will skip any <<wait>> commands entirely.
|
||||
*/
|
||||
gdjs.dialogueTree.getLineText = function() {
|
||||
this.completeClippedTextScrolling();
|
||||
return this.dialogueText.length ? this.dialogueText : '';
|
||||
return this.dialogueIsRunning && this.dialogueText.length
|
||||
? this.dialogueText
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,6 +177,7 @@ gdjs.dialogueTree.commandParametersCount = function() {
|
||||
* @param {number} paramIndex The index of the parameter to get.
|
||||
*/
|
||||
gdjs.dialogueTree.getCommandParameter = function(paramIndex) {
|
||||
if (paramIndex === -1 && this.commandParameters.length > 0) return this.commandParameters[0];
|
||||
if (
|
||||
this.commandParameters &&
|
||||
this.commandParameters.length >= paramIndex + 1
|
||||
@@ -177,23 +195,28 @@ gdjs.dialogueTree.getCommandParameter = function(paramIndex) {
|
||||
* @param {string} command The command you want to check for being called. Write it without the `<<>>`.
|
||||
*/
|
||||
gdjs.dialogueTree.isCommandCalled = function(command) {
|
||||
if (!this.dialogueIsRunning) return false;
|
||||
|
||||
var commandCalls = gdjs.dialogueTree.commandCalls;
|
||||
var clipTextEnd = gdjs.dialogueTree.clipTextEnd;
|
||||
var dialogueText = gdjs.dialogueTree.dialogueText;
|
||||
|
||||
if (this.pauseScrolling || !commandCalls) return false;
|
||||
return this.commandCalls.some(function(call, index) {
|
||||
if (clipTextEnd < call.time) return false;
|
||||
if (call.cmd === 'wait' && clipTextEnd !== dialogueText.length) {
|
||||
if (clipTextEnd !== 0 && clipTextEnd < call.time) return false;
|
||||
if (call.cmd === 'wait' && (clipTextEnd === 0 || clipTextEnd !== dialogueText.length)) {
|
||||
gdjs.dialogueTree.pauseScrolling = true;
|
||||
setTimeout(function() {
|
||||
gdjs.dialogueTree.pauseScrolling = false;
|
||||
commandCalls.splice(index, 1);
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('CMD:', call);
|
||||
}, parseInt(call.params[1], 10));
|
||||
}
|
||||
if (call.cmd === command) {
|
||||
gdjs.dialogueTree.commandParameters = call.params;
|
||||
commandCalls.splice(index, 1);
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('CMD:', call);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -225,7 +248,7 @@ gdjs.dialogueTree._cycledOptionIndex = function(optionIndex) {
|
||||
* @param {number} optionIndex The index of the option you want to get
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOption = function(optionIndex) {
|
||||
if (!this.options.length) return [];
|
||||
if (!this.dialogueIsRunning || !this.options.length) return [];
|
||||
optionIndex = gdjs.dialogueTree._normalizedOptionIndex(optionIndex);
|
||||
return this.options[optionIndex];
|
||||
};
|
||||
@@ -239,7 +262,7 @@ gdjs.dialogueTree.getLineOptionsText = function(
|
||||
optionSelectionCursor,
|
||||
addNewLine
|
||||
) {
|
||||
if (!this.options.length) return '';
|
||||
if (!this.dialogueIsRunning || !this.options.length) return '';
|
||||
var textResult = '';
|
||||
this.options.forEach(function(optionText, index) {
|
||||
if (index === gdjs.dialogueTree.selectedOption) {
|
||||
@@ -266,7 +289,7 @@ gdjs.dialogueTree.getLineOptionsTextVertical = function(optionSelectionCursor) {
|
||||
* @returns {number} The number of options
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOptionsCount = function() {
|
||||
if (this.options.length) {
|
||||
if (this.dialogueIsRunning && this.options.length) {
|
||||
return this.optionsCount;
|
||||
}
|
||||
return 0;
|
||||
@@ -278,6 +301,7 @@ gdjs.dialogueTree.getLineOptionsCount = function() {
|
||||
* This will advance the dialogue tree to the dialogue branch was selected by the player.
|
||||
*/
|
||||
gdjs.dialogueTree.confirmSelectOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (
|
||||
this.dialogueData.select &&
|
||||
!this.selectedOptionUpdated &&
|
||||
@@ -301,6 +325,7 @@ gdjs.dialogueTree.confirmSelectOption = function() {
|
||||
* Select next option during Options type line parsing. Hook this to your game input.
|
||||
*/
|
||||
gdjs.dialogueTree.selectNextOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption += 1;
|
||||
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
|
||||
@@ -314,6 +339,7 @@ gdjs.dialogueTree.selectNextOption = function() {
|
||||
* Select previous option during Options type line parsing. Hook this to your game input.
|
||||
*/
|
||||
gdjs.dialogueTree.selectPreviousOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption -= 1;
|
||||
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
|
||||
@@ -328,9 +354,10 @@ gdjs.dialogueTree.selectPreviousOption = function() {
|
||||
* @param {number} optionIndex The index of the option to select
|
||||
*/
|
||||
gdjs.dialogueTree.selectOption = function(optionIndex) {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption = gdjs.dialogueTree._normalizedOptionIndex(
|
||||
this.selectedOption
|
||||
optionIndex
|
||||
);
|
||||
this.selectedOptionUpdated = true;
|
||||
}
|
||||
@@ -341,6 +368,7 @@ gdjs.dialogueTree.selectOption = function(optionIndex) {
|
||||
* @returns {number} The index of the currently selected option
|
||||
*/
|
||||
gdjs.dialogueTree.getSelectedOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
return this.selectedOption;
|
||||
}
|
||||
@@ -374,16 +402,21 @@ gdjs.dialogueTree.hasSelectedOptionChanged = function() {
|
||||
* @param {string} type The type you want to check for ( one of the three above )
|
||||
*/
|
||||
gdjs.dialogueTree.isDialogueLineType = function(type) {
|
||||
if (
|
||||
this.commandCalls &&
|
||||
this.commandCalls.some(function(call) {
|
||||
return gdjs.dialogueTree.clipTextEnd > call.time && call.cmd === 'wait';
|
||||
})
|
||||
) {
|
||||
return !this.pauseScrolling;
|
||||
if (!this.dialogueIsRunning) return false;
|
||||
if (this.commandCalls && type === 'command') {
|
||||
if (
|
||||
this.commandCalls.some(function(call) {
|
||||
return gdjs.dialogueTree.clipTextEnd > call.time && call.cmd === 'wait';
|
||||
})
|
||||
) {
|
||||
return !this.pauseScrolling;
|
||||
}
|
||||
if (this.commandCalls.length > 0 && this.commandParameters.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.dialogueIsRunning ? this.dialogueDataType === type : false;
|
||||
return this.dialogueDataType === type;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -410,19 +443,27 @@ gdjs.dialogueTree.startFrom = function(startDialogueNode) {
|
||||
if (!this.hasDialogueBranch(startDialogueNode)) return;
|
||||
this.optionsCount = 0;
|
||||
this.options = [];
|
||||
this.dialogueBranchTitle = '';
|
||||
this.dialogueBranchBody = '';
|
||||
this.dialogueBranchTags = [];
|
||||
this.tagParameters = [];
|
||||
this.dialogue = this.runner.run(startDialogueNode);
|
||||
this.dialogueData = null;
|
||||
this.dialogueDataType = '';
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.commandCalls = [];
|
||||
this.commandParameters = [];
|
||||
this.pauseScrolling = false;
|
||||
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
this.dialogueBranchTags = this.dialogueData.data.tags;
|
||||
this.dialogueBranchTitle = this.dialogueData.data.title;
|
||||
this.dialogueBranchBody = this.dialogueData.data.body;
|
||||
this.lineNum = this.dialogueData.lineNum;
|
||||
if (gdjs.dialogueTree._isLineTypeText()){
|
||||
this.dialogueDataType = 'text';
|
||||
} else if (gdjs.dialogueTree._isLineTypeOptions()){
|
||||
this.dialogueDataType = 'options';
|
||||
} else {
|
||||
this.dialogueDataType = 'command';
|
||||
};
|
||||
|
||||
this.dialogueIsRunning = true;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
};
|
||||
@@ -452,32 +493,37 @@ gdjs.dialogueTree.goToNextDialogueLine = function() {
|
||||
this.selectedOption = -1;
|
||||
this.selectedOptionUpdated = false;
|
||||
|
||||
if (gdjs.dialogueTree._isLineTypeText()) {
|
||||
if (
|
||||
this.dialogueDataType === 'options' ||
|
||||
this.dialogueDataType === 'text' ||
|
||||
!this.dialogueDataType
|
||||
) {
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('parsing:', this.dialogueData);
|
||||
|
||||
if (!this.dialogueData) {
|
||||
gdjs.dialogueTree.stopRunningDialogue();
|
||||
} else if (gdjs.dialogueTree._isLineTypeText()) {
|
||||
if (this.lineNum === this.dialogueData.lineNum && this.dialogueBranchTitle === this.dialogueData.data.title){
|
||||
this.clipTextEnd = this.dialogueText.length - 1;
|
||||
this.dialogueText +=
|
||||
(this.dialogueText === '' ? '' : ' ') + this.dialogueData.text;
|
||||
} else {
|
||||
this.clipTextEnd = 0;
|
||||
this.dialogueText = this.dialogueData.text;
|
||||
this.commandCalls = [];
|
||||
} else {
|
||||
this.dialogueText += this.dialogueData.text;
|
||||
}
|
||||
|
||||
this.dialogueDataType = 'text';
|
||||
this.dialogueBranchTags = this.dialogueData.data.tags;
|
||||
this.dialogueBranchTitle = this.dialogueData.data.title;
|
||||
this.dialogueBranchBody = this.dialogueData.data.body;
|
||||
this.lineNum = this.dialogueData.lineNum;
|
||||
this.dialogueDataType = 'text';
|
||||
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
} else if (gdjs.dialogueTree._isLineTypeOptions()) {
|
||||
this.commandCalls = [];
|
||||
this.dialogueDataType = 'options';
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.optionsCount = this.dialogueData.options.length;
|
||||
this.options = this.dialogueData.options;
|
||||
this.selectedOptionUpdated = true;
|
||||
} else if (gdjs.dialogueTree._isLineTypeCommand()) {
|
||||
this.dialogueDataType = 'command';
|
||||
|
||||
var command = this.dialogueData.text.split(' ');
|
||||
// If last command was to wait, increase time by one
|
||||
var offsetTime =
|
||||
@@ -495,11 +541,6 @@ gdjs.dialogueTree.goToNextDialogueLine = function() {
|
||||
} else {
|
||||
this.dialogueDataType = 'unknown';
|
||||
}
|
||||
|
||||
if (gdjs.dialogueTree._isLineTypeCommand()) {
|
||||
this.dialogueDataType = 'command';
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -617,7 +658,7 @@ gdjs.dialogueTree.getBranchText = function() {
|
||||
*/
|
||||
gdjs.dialogueTree.getVariable = function(key) {
|
||||
if (this.dialogueIsRunning && key in this.runner.variables.data) {
|
||||
return this.runner.variables.data[key];
|
||||
return this.runner.variables.get(key);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@@ -625,11 +666,11 @@ gdjs.dialogueTree.getVariable = function(key) {
|
||||
/**
|
||||
* Check if a specific variable created by the Dialogue parses exists and is equal to a specific value.
|
||||
* @param {string} key The name of the variable you want to check the value of
|
||||
* @param {string} value The value you want to check against
|
||||
* @param {string|boolean|number} value The value you want to check against
|
||||
*/
|
||||
gdjs.dialogueTree.compareVariable = function(key, value) {
|
||||
if (this.dialogueIsRunning && key in this.runner.variables.data) {
|
||||
return this.runner.variables.data[key].toString() === value;
|
||||
return this.runner.variables.get(key) === value;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -637,11 +678,11 @@ gdjs.dialogueTree.compareVariable = function(key, value) {
|
||||
/**
|
||||
* Set a specific variable created by the Dialogue parser to a specific value.
|
||||
* @param {string} key The name of the variable you want to set the value of
|
||||
* @param {string} value The value you want to set
|
||||
* @param {string|boolean|number} value The value you want to set
|
||||
*/
|
||||
gdjs.dialogueTree.setVariable = function(key, value) {
|
||||
if (this.dialogueIsRunning && this.runner.variables.data) {
|
||||
this.runner.variables.data[key] = value;
|
||||
if (this.runner.variables) {
|
||||
this.runner.variables.set(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -652,7 +693,7 @@ gdjs.dialogueTree.setVariable = function(key, value) {
|
||||
* @param {gdjs.Variable} outputVariable The variable where to store the State
|
||||
*/
|
||||
gdjs.dialogueTree.saveState = function(outputVariable) {
|
||||
const dialogueState = {
|
||||
var dialogueState = {
|
||||
variables: gdjs.dialogueTree.runner.variables.data,
|
||||
visited: gdjs.dialogueTree.runner.visited,
|
||||
};
|
||||
@@ -663,17 +704,32 @@ gdjs.dialogueTree.saveState = function(outputVariable) {
|
||||
* Load the current State of the Dialogue Parser from a specified variable.
|
||||
* Can be used to implement persistence in dialogue through your game's Load/Save function.
|
||||
* That way you can later load all the dialogue choices the player has made.
|
||||
* @param {gdjs.Variable} inputVariable The variable where to load the State from.
|
||||
* @param {gdjs.Variable} inputVariable The structured variable where to load the State from.
|
||||
*/
|
||||
gdjs.dialogueTree.loadState = function(inputVariable) {
|
||||
const jsonData = gdjs.evtTools.network.variableStructureToJSON(inputVariable);
|
||||
var loadedState = JSON.parse(
|
||||
gdjs.evtTools.network.variableStructureToJSON(inputVariable)
|
||||
);
|
||||
if (!loadedState) {
|
||||
console.error('Load state variable is empty:', inputVariable);
|
||||
return
|
||||
}
|
||||
try {
|
||||
const loadedState = JSON.parse(
|
||||
gdjs.evtTools.network.variableStructureToJSON(inputVariable)
|
||||
);
|
||||
gdjs.dialogueTree.runner.visited = loadedState.visited;
|
||||
gdjs.dialogueTree.runner.variables.data = loadedState.variables;
|
||||
gdjs.dialogueTree.runner.variables.data = {};
|
||||
Object.keys(loadedState.variables).forEach(function(key) {
|
||||
var value = loadedState.variables[key];
|
||||
gdjs.dialogueTree.runner.variables.set(key, value);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error('Failed to load state from variable:', inputVariable, e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the current State of the Dialogue Parser.
|
||||
*/
|
||||
gdjs.dialogueTree.clearState = function() {
|
||||
gdjs.dialogueTree.runner.visited = {};
|
||||
gdjs.dialogueTree.runner.variables.data = {};
|
||||
};
|
||||
|
@@ -23,7 +23,7 @@ gdjs.DraggableRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
};
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.DraggableRuntimeBehavior.thisIsARuntimeBehaviorConstructor = "DraggableBehavior::Draggable";
|
||||
gdjs.registerBehavior("DraggableBehavior::Draggable", gdjs.DraggableRuntimeBehavior);
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.onDeActivate = function() {
|
||||
this._endDrag();
|
||||
|
@@ -1,6 +1,11 @@
|
||||
|
||||
// @ts-check
|
||||
describe('gdjs.DraggableRuntimeBehavior', function() {
|
||||
var runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}});
|
||||
var runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
resources: {resources: []},
|
||||
// @ts-ignore
|
||||
properties: {windowWidth: 800, windowHeight: 600}
|
||||
});
|
||||
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers:[{name:"", visibility: true}],
|
||||
@@ -10,8 +15,8 @@ describe('gdjs.DraggableRuntimeBehavior', function() {
|
||||
instances: []
|
||||
});
|
||||
|
||||
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{type: "DraggableBehavior::Draggable"}]});
|
||||
var object2 = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{type: "DraggableBehavior::Draggable"}]});
|
||||
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{name: "Behavior1", type: "DraggableBehavior::Draggable"}], variables: []});
|
||||
var object2 = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{name: "Behavior1", type: "DraggableBehavior::Draggable"}], variables: []});
|
||||
runtimeScene.addObject(object);
|
||||
runtimeScene.addObject(object2);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user