mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
399 Commits
v5.0.0-bet
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a23a8904f6 | ||
![]() |
1311a8b4c5 | ||
![]() |
e8a1af0ef1 | ||
![]() |
bc1095759e | ||
![]() |
125e76bd20 | ||
![]() |
758afea620 | ||
![]() |
cf63960282 | ||
![]() |
deffe37013 | ||
![]() |
0f30c2d614 | ||
![]() |
9d015b9cd1 | ||
![]() |
fc5905b7f4 | ||
![]() |
18be9f5450 | ||
![]() |
31c8d04def | ||
![]() |
77eff757cd | ||
![]() |
c2fedf23b9 | ||
![]() |
6870a53aed | ||
![]() |
92015e8182 | ||
![]() |
2704c654d8 | ||
![]() |
e1242e5397 | ||
![]() |
51d306f98f | ||
![]() |
689904bda5 | ||
![]() |
5b53ffe015 | ||
![]() |
eb2da55504 | ||
![]() |
38cd264bf8 | ||
![]() |
c179730dc4 | ||
![]() |
8109621920 | ||
![]() |
bd0aaa73c7 | ||
![]() |
6d21753288 | ||
![]() |
6993a2f2f9 | ||
![]() |
8e538425c4 | ||
![]() |
3e7e45da41 | ||
![]() |
15eec269c3 | ||
![]() |
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 | ||
![]() |
034c734568 | ||
![]() |
b65aed4c02 | ||
![]() |
adbcef8f59 | ||
![]() |
44dbbd7138 | ||
![]() |
3e63e34d61 | ||
![]() |
dc976003b7 | ||
![]() |
fb3002cd77 | ||
![]() |
b25752907f | ||
![]() |
72da63afcb | ||
![]() |
ab5a593ab3 | ||
![]() |
71dcb20b7b | ||
![]() |
61ffb40dee | ||
![]() |
07152639d1 | ||
![]() |
6655a949ec | ||
![]() |
99312c71e0 | ||
![]() |
3abb5a393d | ||
![]() |
010b52ced6 | ||
![]() |
7a1ce790d4 | ||
![]() |
fbdf530e00 | ||
![]() |
ee1e67a367 | ||
![]() |
5eded536b8 | ||
![]() |
fa511d5faa | ||
![]() |
a0887a1982 | ||
![]() |
975a5a44cc |
@@ -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
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
# Build GDevelop IDE
|
||||
- run:
|
||||
name: Build GDevelop IDE
|
||||
command: cd newIDE/electron-app && npm run build -- --mac --win --linux tar.gz --publish=never
|
||||
command: cd newIDE/electron-app && npm run build -- --mac zip --win --linux tar.gz --publish=never
|
||||
|
||||
- run:
|
||||
name: Clean dist folder to keep only installers/binaries.
|
||||
|
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()
|
||||
|
@@ -88,6 +88,18 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
|
||||
_("Also update the game resolution? If not, the game will "
|
||||
"be stretched or reduced to fit in the window."));
|
||||
|
||||
extension
|
||||
.AddAction("CenterWindow",
|
||||
_("Center the game window on the screen"),
|
||||
_("This action centers the game window on the screen. This "
|
||||
"only works on Windows, macOS and Linux (not when the game "
|
||||
"is executed in a web-browser or on iOS/Android)."),
|
||||
_("Center the game window"),
|
||||
_("Game's window and resolution"),
|
||||
"res/actions/window24.png",
|
||||
"res/actions/window.png")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddAction("SetGameResolutionResizeMode",
|
||||
_("Change the game resolution resize mode"),
|
||||
|
@@ -90,7 +90,7 @@ size_t ParameterMetadataTools::GetObjectParameterIndexFor(
|
||||
// the object in the list of parameters (if possible, just after).
|
||||
// Search "lastObjectName" in the codebase for other place where this
|
||||
// convention is enforced.
|
||||
for (std::size_t pNb = parameterIndex - 1; pNb < parametersMetadata.size();
|
||||
for (std::size_t pNb = parameterIndex; pNb < parametersMetadata.size();
|
||||
pNb--) {
|
||||
if (gd::ParameterMetadata::IsObject(parametersMetadata[pNb].GetType())) {
|
||||
return pNb;
|
||||
|
@@ -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
|
||||
|
@@ -33,7 +33,7 @@ Extensions do have the same distinction between the "**IDE**" part and the "**Ru
|
||||
In GDevelop, developers can associate and manipulate variables in their games. To represent them, we have two things:
|
||||
|
||||
* The **editor** `gd::Variable` that is part of the structure of the game, so living in [GDCore in Variable.h](https://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_variable.html). This is what is shown in the editor, saved on disk in the project file.
|
||||
* The **game engine** variable, called `gdjs.Variable` in GDJS. [Documentation is in the GDJS **game engine**](http://4ian.github.io/GD-Documentation/GDJS%20Runtime%20Documentation/gdjs.Variable.html). This JavaScript class is what is used during a game.
|
||||
* The **game engine** variable, called `gdjs.Variable` in GDJS. [Documentation is in the GDJS **game engine**](https://docs.gdevelop-app.com/GDJS%20Runtime%20Documentation/gdjs.Variable.html). This JavaScript class is what is used during a game.
|
||||
|
||||
The editor `gd::Variable` **know nothing** about the game engine class `gdjs.Variable`. And the `gdjs.Variable` class in the game engine know almost nothing from `gd::Variable` (apart from how it's written in JSON, to be able to load a game default variables).
|
||||
|
||||
@@ -57,7 +57,7 @@ The game engine is in GDJS/Runtime and is all JavaScript.
|
||||
|
||||
## What about events?
|
||||
|
||||
An "**event**" is by default something that [is mostly empty](http://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_base_event.html). In a more traditional programming language, an event can be seen as a scope or block (example: `{ some code here }` in a C style language like JavaScript, Java or C++).
|
||||
An "**event**" is by default something that [is mostly empty](https://docs.gdevelop-app.com/GDCore%20Documentation/classgd_1_1_base_event.html). In a more traditional programming language, an event can be seen as a scope or block (example: `{ some code here }` in a C style language like JavaScript, Java or C++).
|
||||
|
||||
[Default events are defined](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Events/Builtin) in GDevelop Core.
|
||||
In particular, there is StandardEvent, which has conditions and actions. Conditions and actions are both a list of [`gd::Instruction`](https://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_instruction.html). A `gd::Instruction` is either a condition or an action (it depends only on the context where they are used).
|
||||
|
@@ -6,7 +6,7 @@ GDevelop Core is a portable C++ library, compiled to be used in JavaScript in th
|
||||
|
||||
## 1) Getting started 🤓
|
||||
|
||||
First, take a look at the *Readme.md* at the root of the repository and the [developer documentation](http://4ian.github.io/GD-Documentation/).
|
||||
First, take a look at the *Readme.md* at the root of the repository and the [developer documentation](https://docs.gdevelop-app.com/).
|
||||
|
||||
## 2) How to contribute 😎
|
||||
|
||||
@@ -14,7 +14,7 @@ Any contribution is welcome! Whether you want to submit a bug report, a feature
|
||||
or any pull request so as to add a nice feature, do not hesitate to get in touch.
|
||||
|
||||
* Check the [the **roadmap** for ideas and features planned](https://trello.com/b/qf0lM7k8/gdevelop-roadmap).
|
||||
|
||||
|
||||
* Follow the [Development](https://github.com/4ian/GDevelop/tree/master/newIDE#development) section of the README to set up GDevelop and start modifying either **the editor** or **[the game engine/extensions](https://github.com/4ian/GDevelop/tree/master/newIDE#development-of-the-game-engine-or-extensions)**.
|
||||
|
||||
* To submit your changes, you have first to create a Fork on GitHub (use the Fork button on the top right), then [create a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||
|
@@ -964,7 +964,7 @@ HTML_COLORSTYLE_GAMMA = 80
|
||||
# page will contain the date and time when the page was generated. Setting
|
||||
# this to NO can help when comparing the output of multiple runs.
|
||||
|
||||
HTML_TIMESTAMP = YES
|
||||
HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
|
||||
# documentation will contain sections that can be hidden and shown after the
|
||||
|
@@ -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
|
||||
|
@@ -1,2 +1,2 @@
|
||||
Uncompress here the external libraries needed: SFML.
|
||||
Refer to [the documentation](http://4ian.github.io/GD-Documentation/GDCore%20Documentation/setup_dev_env.html) for more information and to get the download links of the libraries.
|
||||
Refer to [the documentation](https://docs.gdevelop-app.com/GDCore%20Documentation/setup_dev_env.html) for more information and to get the download links of the libraries.
|
||||
|
@@ -1,8 +1,9 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
|
||||
* to this extension file or to any other *.js file that you reference inside.
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
@@ -10,8 +11,16 @@
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/*::
|
||||
// Import types to allow Flow to do static type checking on this file.
|
||||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||||
// for the game engine are checked with TypeScript annotations.
|
||||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_, gd) {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
'AdMob',
|
||||
@@ -355,7 +364,7 @@ module.exports = {
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd, extension) {
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
@@ -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,
|
||||
|
@@ -1,8 +1,9 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
|
||||
* to this extension file or to any other *.js file that you reference inside.
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
@@ -11,8 +12,18 @@
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/*::
|
||||
// Import types to allow Flow to do static type checking on this file.
|
||||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||||
// for the game engine are checked with TypeScript annotations.
|
||||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_, gd) {
|
||||
createExtension: function (
|
||||
_ /*: (string) => string */,
|
||||
gd /*: libGDevelop */
|
||||
) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
@@ -27,7 +38,8 @@ module.exports = {
|
||||
.setExtensionHelpPath('/objects/bbtext');
|
||||
|
||||
var objectBBText = new gd.ObjectJsImplementation();
|
||||
objectBBText.updateProperty = function(
|
||||
// $FlowExpectedError
|
||||
objectBBText.updateProperty = function (
|
||||
objectContent,
|
||||
propertyName,
|
||||
newValue
|
||||
@@ -35,73 +47,69 @@ 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;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
objectBBText.getProperties = function(objectContent) {
|
||||
var objectProperties = new gd.MapStringPropertyDescriptor();
|
||||
// $FlowExpectedError
|
||||
objectBBText.getProperties = function (objectContent) {
|
||||
const objectProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
objectProperties.set(
|
||||
'text',
|
||||
new gd.PropertyDescriptor(objectContent.text)
|
||||
.setType('textarea')
|
||||
.setLabel(_('BBCode text'))
|
||||
);
|
||||
objectProperties
|
||||
.getOrCreate('text')
|
||||
.setValue(objectContent.text)
|
||||
.setType('textarea')
|
||||
.setLabel(_('BBCode text'));
|
||||
|
||||
objectProperties.set(
|
||||
'color',
|
||||
new gd.PropertyDescriptor(objectContent.color)
|
||||
.setType('color')
|
||||
.setLabel(_('Base color'))
|
||||
);
|
||||
objectProperties
|
||||
.getOrCreate('color')
|
||||
.setValue(objectContent.color)
|
||||
.setType('color')
|
||||
.setLabel(_('Base color'));
|
||||
|
||||
objectProperties.set(
|
||||
'opacity',
|
||||
new gd.PropertyDescriptor(objectContent.opacity.toString())
|
||||
.setType('number')
|
||||
.setLabel(_('Opacity (0-255)'))
|
||||
);
|
||||
objectProperties
|
||||
.getOrCreate('opacity')
|
||||
.setValue(objectContent.opacity.toString())
|
||||
.setType('number')
|
||||
.setLabel(_('Opacity (0-255)'));
|
||||
|
||||
objectProperties.set(
|
||||
'fontSize',
|
||||
new gd.PropertyDescriptor(objectContent.fontSize)
|
||||
.setType('number')
|
||||
.setLabel(_('Base size'))
|
||||
);
|
||||
objectProperties
|
||||
.getOrCreate('fontSize')
|
||||
.setValue(objectContent.fontSize.toString())
|
||||
.setType('number')
|
||||
.setLabel(_('Base size'));
|
||||
|
||||
objectProperties.set(
|
||||
'align',
|
||||
new gd.PropertyDescriptor(objectContent.align)
|
||||
.setType('choice')
|
||||
.addExtraInfo('left')
|
||||
.addExtraInfo('center')
|
||||
.addExtraInfo('right')
|
||||
.setLabel(_('Base alignment'))
|
||||
);
|
||||
objectProperties
|
||||
.getOrCreate('align')
|
||||
.setValue(objectContent.align)
|
||||
.setType('choice')
|
||||
.addExtraInfo('left')
|
||||
.addExtraInfo('center')
|
||||
.addExtraInfo('right')
|
||||
.setLabel(_('Base alignment'));
|
||||
|
||||
objectProperties.set(
|
||||
'fontFamily',
|
||||
new gd.PropertyDescriptor(objectContent.fontFamily)
|
||||
.setType('string')
|
||||
.setLabel(_('Base font family'))
|
||||
);
|
||||
objectProperties
|
||||
.getOrCreate('fontFamily')
|
||||
.setValue(objectContent.fontFamily)
|
||||
.setType('resource')
|
||||
.addExtraInfo('font')
|
||||
.setLabel(_('Base font family'));
|
||||
|
||||
objectProperties.set(
|
||||
'wordWrap',
|
||||
new gd.PropertyDescriptor(objectContent.wordWrap ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Word wrapping'))
|
||||
);
|
||||
objectProperties
|
||||
.getOrCreate('wordWrap')
|
||||
.setValue(objectContent.wordWrap ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Word wrapping'));
|
||||
|
||||
objectProperties.set(
|
||||
'visible',
|
||||
new gd.PropertyDescriptor(objectContent.visible ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Visible on start'))
|
||||
);
|
||||
objectProperties
|
||||
.getOrCreate('visible')
|
||||
.setValue(objectContent.visible ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Visible on start'));
|
||||
|
||||
return objectProperties;
|
||||
};
|
||||
@@ -110,7 +118,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',
|
||||
@@ -119,7 +127,8 @@ module.exports = {
|
||||
})
|
||||
);
|
||||
|
||||
objectBBText.updateInitialInstanceProperty = function(
|
||||
// $FlowExpectedError
|
||||
objectBBText.updateInitialInstanceProperty = function (
|
||||
objectContent,
|
||||
instance,
|
||||
propertyName,
|
||||
@@ -129,7 +138,8 @@ module.exports = {
|
||||
) {
|
||||
return false;
|
||||
};
|
||||
objectBBText.getInitialInstanceProperties = function(
|
||||
// $FlowExpectedError
|
||||
objectBBText.getInitialInstanceProperties = function (
|
||||
content,
|
||||
instance,
|
||||
project,
|
||||
@@ -160,7 +170,7 @@ module.exports = {
|
||||
* Useful for setting multiple generic properties.
|
||||
*/
|
||||
const addSettersAndGettersToObject = (gdObject, properties, objectName) => {
|
||||
properties.forEach(property => {
|
||||
properties.forEach((property) => {
|
||||
const parameterType =
|
||||
property.type === 'boolean' ? 'yesorno' : property.type;
|
||||
|
||||
@@ -172,8 +182,6 @@ module.exports = {
|
||||
property.expressionLabel,
|
||||
property.expressionDescription,
|
||||
'',
|
||||
'',
|
||||
property.iconPath,
|
||||
property.iconPath
|
||||
)
|
||||
.addParameter('object', objectName, objectName, false)
|
||||
@@ -186,8 +194,6 @@ module.exports = {
|
||||
property.expressionLabel,
|
||||
property.expressionDescription,
|
||||
'',
|
||||
'',
|
||||
property.iconPath,
|
||||
property.iconPath
|
||||
)
|
||||
.addParameter('object', objectName, objectName, false)
|
||||
@@ -391,13 +397,15 @@ module.exports = {
|
||||
* of your extension behaviors/objects by instanciating behaviors/objects
|
||||
* and setting the property to a given value.
|
||||
*
|
||||
* If you don't have any tests, you can simply return an empty array like this:
|
||||
* `runExtensionSanityTests: function(gd, extension) { return []; }`
|
||||
* If you don't have any tests, you can simply return an empty array.
|
||||
*
|
||||
* But it is recommended to create tests for the behaviors/objects properties you created
|
||||
* to avoid mistakes.
|
||||
*/
|
||||
runExtensionSanityTests: function(gd, extension) {
|
||||
runExtensionSanityTests: function (
|
||||
gd /*: libGDevelop */,
|
||||
extension /*: gdPlatformExtension*/
|
||||
) {
|
||||
return [];
|
||||
},
|
||||
/**
|
||||
@@ -405,11 +413,13 @@ module.exports = {
|
||||
*
|
||||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
|
||||
*/
|
||||
registerEditorConfigurations: function(objectsEditorService) {
|
||||
registerEditorConfigurations: function (
|
||||
objectsEditorService /*: ObjectsEditorService */
|
||||
) {
|
||||
objectsEditorService.registerEditorConfiguration(
|
||||
'BBText::BBText',
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||||
helpPagePath: '/objects/bbtext_object',
|
||||
helpPagePath: '/objects/bbtext',
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -418,7 +428,9 @@ module.exports = {
|
||||
*
|
||||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
|
||||
*/
|
||||
registerInstanceRenderers: function(objectsRenderingService) {
|
||||
registerInstanceRenderers: function (
|
||||
objectsRenderingService /*: ObjectsRenderingService */
|
||||
) {
|
||||
const RenderedInstance = objectsRenderingService.RenderedInstance;
|
||||
const PIXI = objectsRenderingService.PIXI;
|
||||
const MultiStyleText = objectsRenderingService.requireModule(
|
||||
@@ -477,7 +489,7 @@ module.exports = {
|
||||
/**
|
||||
* Return the path to the thumbnail of the specified object.
|
||||
*/
|
||||
RenderedBBTextInstance.getThumbnail = function(
|
||||
RenderedBBTextInstance.getThumbnail = function (
|
||||
project,
|
||||
resourcesLoader,
|
||||
object
|
||||
@@ -488,7 +500,7 @@ module.exports = {
|
||||
/**
|
||||
* This is called to update the PIXI object on the scene editor
|
||||
*/
|
||||
RenderedBBTextInstance.prototype.update = function() {
|
||||
RenderedBBTextInstance.prototype.update = function () {
|
||||
const rawText = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('text')
|
||||
@@ -515,11 +527,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)
|
||||
@@ -562,14 +588,14 @@ module.exports = {
|
||||
/**
|
||||
* Return the width of the instance, when it's not resized.
|
||||
*/
|
||||
RenderedBBTextInstance.prototype.getDefaultWidth = function() {
|
||||
RenderedBBTextInstance.prototype.getDefaultWidth = function () {
|
||||
return this._pixiObject.width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the height of the instance, when it's not resized.
|
||||
*/
|
||||
RenderedBBTextInstance.prototype.getDefaultHeight = function() {
|
||||
RenderedBBTextInstance.prototype.getDefaultHeight = function () {
|
||||
return this._pixiObject.height;
|
||||
};
|
||||
|
||||
|
@@ -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() {
|
||||
|
@@ -1,34 +1,54 @@
|
||||
/**
|
||||
* @typedef {Object} BBTextObjectDataType Base parameters for {@link gdjs.BBTextRuntimeObject}
|
||||
* @property {Object} content The base parameters of the BBText
|
||||
* @property {number} content.opacity The opacity of the BBText
|
||||
* @property {boolean} content.visible Is the text visible?
|
||||
* @property {string} content.text Content of the text
|
||||
* @property {string} content.color The color of the text
|
||||
* @property {string} content.fontFamily The font of the text
|
||||
* @property {number} content.fontSize The size of the text
|
||||
* @property {boolean} content.wordWrap Activate word wrap if set to true
|
||||
* @property {('left'|'center'|'right')} content.align Alignment of the text: "left", "center" or "right"
|
||||
*
|
||||
* @typedef {ObjectData & BBTextObjectDataType} BBTextObjectData
|
||||
*/
|
||||
|
||||
/**
|
||||
* Displays a rich text using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).
|
||||
* @memberof gdjs
|
||||
* @class BBTextRuntimeObject
|
||||
* @extends RuntimeObject
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The {@link gdjs.RuntimeScene} the object belongs to
|
||||
* @param {BBTextObjectData} objectData The object data used to initialize the object
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
gdjs.RuntimeObject.call(this, runtimeScene, objectData);
|
||||
|
||||
/** @type number */
|
||||
this._opacity = objectData.content.opacity;
|
||||
/** @type boolean */
|
||||
/** @type {number} */
|
||||
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 */
|
||||
/** @type {string} */
|
||||
this._text = objectData.content.text;
|
||||
/** @type string */
|
||||
/** @type {string} */
|
||||
this._color = objectData.content.color;
|
||||
/** @type string */
|
||||
/** @type {string} */
|
||||
this._fontFamily = objectData.content.fontFamily;
|
||||
/** @type number */
|
||||
this._fontSize = objectData.content.fontSize;
|
||||
/** @type boolean */
|
||||
/** @type {number} */
|
||||
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 */
|
||||
/** @type {number} */
|
||||
this._wrappingWidth = 250; // This value is the default wrapping width of the runtime object.
|
||||
/** @type string */
|
||||
/** @type {string} */
|
||||
this._align = objectData.content.align;
|
||||
|
||||
if (this._renderer)
|
||||
gdjs.BBTextRuntimeObjectRenderer.call(this._renderer, this, runtimeScene);
|
||||
else
|
||||
/** @type {gdjs.BBTextRuntimeObjectRenderer} */
|
||||
this._renderer = new gdjs.BBTextRuntimeObjectRenderer(this, runtimeScene);
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
@@ -38,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();
|
||||
@@ -48,9 +68,7 @@ gdjs.BBTextRuntimeObject.prototype.getRendererObject = function() {
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @private
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.extraInitializationFromInitialInstance = function(
|
||||
initialInstanceData
|
||||
) {
|
||||
gdjs.BBTextRuntimeObject.prototype.extraInitializationFromInitialInstance = function(initialInstanceData) {
|
||||
// The wrapping width value (this._wrappingWidth) is using the object's width as an innitial value
|
||||
if (initialInstanceData.customSize)
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
|
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")
|
||||
|
58
Extensions/DebuggerTools/JsExtension.js
Normal file
58
Extensions/DebuggerTools/JsExtension.js
Normal file
@@ -0,0 +1,58 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/*::
|
||||
// Import types to allow Flow to do static type checking on this file.
|
||||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||||
// for the game engine are checked with TypeScript annotations.
|
||||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
'DebuggerTools',
|
||||
_('Debugger Tools'),
|
||||
_(
|
||||
'Allow to interact with the editor debugger from the game.'
|
||||
),
|
||||
'Arthur Pacaud (arthuro555)',
|
||||
'MIT'
|
||||
);
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'Pause',
|
||||
_('Pause game execution'),
|
||||
_(
|
||||
'This pauses the game, useful for inspecting the game state through the debugger. ' +
|
||||
'Note that events will be still executed until the end before the game is paused.'
|
||||
),
|
||||
_('Pause game execution'),
|
||||
_('Debugger Tools'),
|
||||
'res/actions/bug32.png',
|
||||
'res/actions/bug32.png'
|
||||
)
|
||||
.addCodeOnlyParameter("currentScene", "")
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DebuggerTools/debuggertools.js')
|
||||
.setFunctionName('gdjs.evtTools.debugger.pause');
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
|
||||
return [];
|
||||
},
|
||||
}
|
19
Extensions/DebuggerTools/debuggertools.js
Normal file
19
Extensions/DebuggerTools/debuggertools.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file
|
||||
* Tools for interacting with the debugger.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The namespace containing tools to interact with the debugger.
|
||||
* @namespace
|
||||
*/
|
||||
gdjs.evtTools.debugger = {};
|
||||
|
||||
/**
|
||||
* Stop the game execution.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene - The current scene.
|
||||
*/
|
||||
gdjs.evtTools.debugger.pause = function(runtimeScene) {
|
||||
runtimeScene.getGame().pause(true);
|
||||
}
|
@@ -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) {
|
||||
|
||||
|
@@ -1,17 +1,26 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
|
||||
* to this extension file or to any other *.js file that you reference inside.
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/*::
|
||||
// Import types to allow Flow to do static type checking on this file.
|
||||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||||
// for the game engine are checked with TypeScript annotations.
|
||||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_, gd) {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
"DeviceSensors",
|
||||
@@ -445,5 +454,5 @@ module.exports = {
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd, extension) { return []; },
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) { return []; },
|
||||
};
|
||||
|
@@ -1,17 +1,26 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
|
||||
* to this extension file or to any other *.js file that you reference inside.
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/*::
|
||||
// Import types to allow Flow to do static type checking on this file.
|
||||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||||
// for the game engine are checked with TypeScript annotations.
|
||||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_, gd) {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
"DeviceVibration",
|
||||
@@ -75,5 +84,5 @@ module.exports = {
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd, extension) { return []; },
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) { return []; },
|
||||
};
|
||||
|
@@ -1,8 +1,9 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
|
||||
* to this extension file or to any other *.js file that you reference inside.
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
@@ -10,8 +11,16 @@
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/*::
|
||||
// Import types to allow Flow to do static type checking on this file.
|
||||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||||
// for the game engine are checked with TypeScript annotations.
|
||||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_, gd) {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
@@ -45,7 +54,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 +78,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 +177,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,18 +218,52 @@ 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');
|
||||
|
||||
@@ -257,13 +299,27 @@ 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',
|
||||
_('Get the current dialogue line text'),
|
||||
_('Returns the current dialogue line text'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -275,7 +331,6 @@ module.exports = {
|
||||
_('Get the number of options in an options line type'),
|
||||
_('Get the number of options in an options line type'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -289,7 +344,6 @@ module.exports = {
|
||||
"Get the text of an option from an Options line type, using the option's Number. The numbers start from 0."
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('expression', _('Option Index Number'), '', false)
|
||||
@@ -304,7 +358,6 @@ module.exports = {
|
||||
"Get the text of all available options from an Options line type as a horizontal list. You can also pass the selected option's cursor string, which by default is ->"
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Options Selection Cursor'), '', false)
|
||||
@@ -320,7 +373,6 @@ module.exports = {
|
||||
"Get the text of all available options from an Options line type as a vertical list. You can also pass the selected option's cursor string, which by default is ->"
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Options Selection Cursor'), '', false)
|
||||
@@ -336,7 +388,6 @@ module.exports = {
|
||||
'Get the number of the currently selected option. Use this to help you render the option selection marker at the right place.'
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -350,7 +401,6 @@ module.exports = {
|
||||
'Get dialogue line text clipped by the typewriter effect. Use the "Scroll clipped text" action to control the typewriter effect.'
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -362,7 +412,6 @@ module.exports = {
|
||||
_('Get the title of the current branch of the running dialogue'),
|
||||
_('Get the title of the current branch of the running dialogue'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -374,7 +423,6 @@ module.exports = {
|
||||
_('Get the tags of the current branch of the running dialogue'),
|
||||
_('Get the tags of the current branch of the running dialogue'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -386,7 +434,6 @@ module.exports = {
|
||||
_('Get a tag of the current branch of the running dialogue via its index'),
|
||||
_('Get a tag of the current branch of the running dialogue via its index'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('expression', _('Tag Index Number'), '', false)
|
||||
@@ -401,7 +448,6 @@ module.exports = {
|
||||
'Get the parameters of a command call - <<command withParameter anotherParameter>>'
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('expression', _('parameter Index Number'), '', true)
|
||||
@@ -414,7 +460,6 @@ module.exports = {
|
||||
_('Get the number of parameters in the currently passed command'),
|
||||
_('Get the number of parameters in the currently passed command'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -430,7 +475,6 @@ module.exports = {
|
||||
'Get parameter from a Tag found by the branch contains tag condition'
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('expression', _('parameter Index Number'), '', true)
|
||||
@@ -443,7 +487,6 @@ module.exports = {
|
||||
_('Get a list of all visited branches'),
|
||||
_('Get a list of all visited branches'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -455,7 +498,6 @@ module.exports = {
|
||||
_('Get the full raw text of the current branch'),
|
||||
_('Get the full raw text of the current branch'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -467,7 +509,6 @@ module.exports = {
|
||||
_('Get dialogue state value'),
|
||||
_('Get dialogue state value'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Variable Name'), '', false)
|
||||
@@ -592,9 +633,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 +646,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 +661,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',
|
||||
@@ -637,7 +712,7 @@ module.exports = {
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd, extension) {
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
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).
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user