mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
393 Commits
refactor/g
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b9ba8e1b7b | ||
![]() |
84ea9a9643 | ||
![]() |
13c44250f2 | ||
![]() |
a5907a6883 | ||
![]() |
5902906bcc | ||
![]() |
4db041bf10 | ||
![]() |
cc1d26201e | ||
![]() |
aa71e78507 | ||
![]() |
ee49ca6c14 | ||
![]() |
a649789f4c | ||
![]() |
61e8e95d5b | ||
![]() |
cc158a9250 | ||
![]() |
a91ccacb89 | ||
![]() |
bb9e8a2ea9 | ||
![]() |
3bf40cd46c | ||
![]() |
aa823c1287 | ||
![]() |
24a666ab83 | ||
![]() |
9e652b228d | ||
![]() |
09bedc6ce5 | ||
![]() |
91e57340d4 | ||
![]() |
c385aae845 | ||
![]() |
460b582ab9 | ||
![]() |
3a9f896f04 | ||
![]() |
2851a20787 | ||
![]() |
9077c5d4f7 | ||
![]() |
693b64cddf | ||
![]() |
661d329170 | ||
![]() |
66ce941d46 | ||
![]() |
d4023efe0f | ||
![]() |
815bd92469 | ||
![]() |
8685defaa8 | ||
![]() |
ad89af6ad5 | ||
![]() |
0428417295 | ||
![]() |
54c0424785 | ||
![]() |
f316d28fe3 | ||
![]() |
104b6c2800 | ||
![]() |
0ac504c0ab | ||
![]() |
0508da60e5 | ||
![]() |
2e511c75bf | ||
![]() |
b76df0247d | ||
![]() |
816fb242be | ||
![]() |
a4b452b037 | ||
![]() |
0d5fbdabc9 | ||
![]() |
268beb256a | ||
![]() |
e33e61d2fd | ||
![]() |
07d770148f | ||
![]() |
2a5b5ee4a2 | ||
![]() |
0420ad8888 | ||
![]() |
0e69a87eec | ||
![]() |
9b178bc985 | ||
![]() |
85cfb644c3 | ||
![]() |
2ba2b3b509 | ||
![]() |
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 |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -84,7 +84,8 @@
|
||||
"any": "cpp",
|
||||
"array": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"numeric": "cpp"
|
||||
"numeric": "cpp",
|
||||
"__memory": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
|
@@ -75,6 +75,11 @@ else()
|
||||
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support (with GNU extensions). Please use a different C++ compiler.")
|
||||
endif()
|
||||
|
||||
# Mark some warnings as errors
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-stack-address")
|
||||
endif()
|
||||
|
||||
#Define common directories:
|
||||
set(GD_base_dir ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
@@ -31,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;
|
||||
@@ -52,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,
|
||||
|
@@ -31,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()
|
||||
@@ -53,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,
|
||||
|
@@ -1,6 +1,8 @@
|
||||
#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"
|
||||
@@ -628,7 +630,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" ||
|
||||
@@ -660,7 +662,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) + "\"";
|
||||
}
|
||||
}
|
||||
@@ -1109,6 +1111,33 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
|
||||
}
|
||||
}
|
||||
|
||||
size_t EventsCodeGenerator::GenerateSingleUsageUniqueIdForEventsList() {
|
||||
return eventsListNextUniqueId++;
|
||||
}
|
||||
|
||||
size_t EventsCodeGenerator::GenerateSingleUsageUniqueIdFor(
|
||||
const Instruction* instruction) {
|
||||
if (!instruction) {
|
||||
std::cout << "ERROR: During code generation, a null pointer was passed to "
|
||||
"GenerateSingleUsageUniqueIdFor."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Base the unique id on the adress in memory so that the same instruction
|
||||
// in memory will get the same id across different code generations.
|
||||
size_t uniqueId = (size_t)instruction;
|
||||
|
||||
// While in most case this function is called a single time for each instruction,
|
||||
// it's possible for an instruction to be appearing more than once in the events,
|
||||
// if we used links. In this case, simply increment the unique id to be sure that
|
||||
// ids are effectively uniques, and stay stable (given the same order of links).
|
||||
while (instructionUniqueIds.find(uniqueId) != instructionUniqueIds.end()) {
|
||||
uniqueId++;
|
||||
}
|
||||
instructionUniqueIds.insert(uniqueId);
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GetObjectListName(
|
||||
const gd::String& name, const gd::EventsCodeGenerationContext& context) {
|
||||
return ManObjListName(name);
|
||||
@@ -1137,7 +1166,8 @@ EventsCodeGenerator::EventsCodeGenerator(gd::Project& project_,
|
||||
errorOccurred(false),
|
||||
compilationForRuntime(false),
|
||||
maxCustomConditionsDepth(0),
|
||||
maxConditionsListsSize(0){};
|
||||
maxConditionsListsSize(0),
|
||||
eventsListNextUniqueId(0){};
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(
|
||||
const gd::Platform& platform_,
|
||||
@@ -1152,6 +1182,7 @@ EventsCodeGenerator::EventsCodeGenerator(
|
||||
errorOccurred(false),
|
||||
compilationForRuntime(false),
|
||||
maxCustomConditionsDepth(0),
|
||||
maxConditionsListsSize(0){};
|
||||
maxConditionsListsSize(0),
|
||||
eventsListNextUniqueId(0){};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -123,7 +124,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
*
|
||||
*/
|
||||
std::vector<gd::String> GenerateParametersCodes(
|
||||
const std::vector<gd::Expression> & parameters,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersInfo,
|
||||
EventsCodeGenerationContext& context,
|
||||
std::vector<std::pair<gd::String, gd::String> >*
|
||||
@@ -321,7 +322,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* group.
|
||||
*
|
||||
* Get a list containing the "real" objects name when the events refers to \a
|
||||
* objectName :<br> If \a objectName if really an object, the list will only
|
||||
* objectName :<br> If \a objectName is really an object, the list will only
|
||||
* contains \a objectName unchanged.<br> If \a objectName is a group, the list
|
||||
* will contains all the objects of the group.<br> If \a objectName is the
|
||||
* "current" object in the context ( i.e: The object being used for launching
|
||||
@@ -411,6 +412,29 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
|
||||
enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE };
|
||||
|
||||
/**
|
||||
* Generate a single unique number for the specified instruction.
|
||||
*
|
||||
* This is useful for instructions that need to identify themselves in the
|
||||
* generated code like the "Trigger Once" conditions. The id is stable across
|
||||
* code generations if the instructions are the same objects in memory.
|
||||
*
|
||||
* Note that if this function is called multiple times with the same
|
||||
* instruction, the unique number returned will be *different*. This is
|
||||
* because a single instruction might appear at multiple places in events due
|
||||
* to the usage of links.
|
||||
*/
|
||||
size_t GenerateSingleUsageUniqueIdFor(const gd::Instruction* instruction);
|
||||
|
||||
/**
|
||||
* Generate a single unique number for an events list.
|
||||
*
|
||||
* This is useful to create unique function names for events list, that are
|
||||
* stable across code generation given the exact same list of events. They are
|
||||
* *not* stable if events are moved/reorganized.
|
||||
*/
|
||||
size_t GenerateSingleUsageUniqueIdForEventsList();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Generate the code for a single parameter.
|
||||
@@ -704,7 +728,8 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
/**
|
||||
* Generate the getter to get the name of the specified behavior.
|
||||
*/
|
||||
virtual gd::String GenerateGetBehaviorNameCode(const gd::String& behaviorName);
|
||||
virtual gd::String GenerateGetBehaviorNameCode(
|
||||
const gd::String& behaviorName);
|
||||
|
||||
const gd::Platform& platform; ///< The platform being used.
|
||||
|
||||
@@ -732,6 +757,11 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
size_t maxCustomConditionsDepth; ///< The maximum depth value for all the
|
||||
///< custom conditions created.
|
||||
size_t maxConditionsListsSize; ///< The maximum size of a list of conditions.
|
||||
|
||||
std::set<size_t>
|
||||
instructionUniqueIds; ///< The unique ids generated for instructions.
|
||||
size_t eventsListNextUniqueId; ///< The next identifier to use for an events
|
||||
///< list function name.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -127,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 " +
|
||||
@@ -359,4 +359,8 @@ void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
output += GenerateDefaultValue(node.type);
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) {
|
||||
output += GenerateDefaultValue(node.type);
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -73,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:
|
||||
|
@@ -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;
|
||||
@@ -127,15 +128,17 @@ class GD_CORE_API BaseEvent {
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return a list of all expressions of the event.
|
||||
* \note Used to preprocess or search in the expressions.
|
||||
* \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<gd::Expression*> GetAllExpressions() {
|
||||
std::vector<gd::Expression*> noExpr;
|
||||
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;
|
||||
};
|
||||
|
||||
|
@@ -4,9 +4,12 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Events/InstructionsList.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -15,18 +18,14 @@ namespace gd {
|
||||
|
||||
gd::Expression Instruction::badExpression("");
|
||||
|
||||
Instruction::Instruction(gd::String type_)
|
||||
: type(type_),
|
||||
inverted(false) {
|
||||
Instruction::Instruction(gd::String type_) : type(type_), inverted(false) {
|
||||
parameters.reserve(8);
|
||||
}
|
||||
|
||||
Instruction::Instruction(gd::String type_,
|
||||
const std::vector<gd::Expression>& parameters_,
|
||||
bool inverted_)
|
||||
: type(type_),
|
||||
inverted(inverted_),
|
||||
parameters(parameters_) {
|
||||
: type(type_), inverted(inverted_), parameters(parameters_) {
|
||||
parameters.reserve(8);
|
||||
}
|
||||
|
||||
@@ -56,4 +55,17 @@ void Instruction::SetParameter(std::size_t nb, const gd::Expression& val) {
|
||||
parameters[nb] = val;
|
||||
}
|
||||
|
||||
std::shared_ptr<Instruction> GD_CORE_API
|
||||
CloneRememberingOriginalElement(std::shared_ptr<Instruction> instruction) {
|
||||
std::shared_ptr<Instruction> copy =
|
||||
std::make_shared<Instruction>(*instruction);
|
||||
// Original instruction is either the original instruction of the copied
|
||||
// instruction, or the instruction copied.
|
||||
copy->originalInstruction = instruction->originalInstruction.expired()
|
||||
? instruction
|
||||
: instruction->originalInstruction;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
#ifndef INSTRUCTION_H
|
||||
#define INSTRUCTION_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Events/InstructionsList.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -131,6 +133,17 @@ class GD_CORE_API Instruction {
|
||||
*/
|
||||
inline gd::InstructionsList& GetSubInstructions() { return subInstructions; };
|
||||
|
||||
/**
|
||||
* \brief Return the original instruction this instruction was copied from.
|
||||
*
|
||||
* Useful to get reference to the original instruction in memory during code
|
||||
* generation, to ensure stable unique identifiers.
|
||||
*/
|
||||
std::weak_ptr<Instruction> GetOriginalInstruction() { return originalInstruction; };
|
||||
|
||||
friend std::shared_ptr<Instruction> CloneRememberingOriginalElement(
|
||||
std::shared_ptr<Instruction> instruction);
|
||||
|
||||
private:
|
||||
gd::String type; ///< Instruction type
|
||||
bool inverted; ///< True if the instruction if inverted. Only applicable for
|
||||
@@ -139,9 +152,23 @@ class GD_CORE_API Instruction {
|
||||
parameters; ///< Vector containing the parameters
|
||||
gd::InstructionsList subInstructions; ///< Sub instructions, if applicable.
|
||||
|
||||
std::weak_ptr<Instruction>
|
||||
originalInstruction; ///< Pointer used to remember which gd::Instruction
|
||||
///< this instruction was copied from. Useful to
|
||||
///< ensure the stability of code generation (as
|
||||
///< some part of code generation uses the pointer
|
||||
///< to the instruction as a unique identifier).
|
||||
|
||||
static gd::Expression badExpression;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone the given instruction, returning an instruction for which
|
||||
* `GetOriginalInstruction()` returns the originally copied instruction.
|
||||
*/
|
||||
std::shared_ptr<Instruction> GD_CORE_API
|
||||
CloneRememberingOriginalElement(std::shared_ptr<Instruction> instruction);
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // INSTRUCTION_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
|
||||
|
@@ -13,11 +13,12 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("BuiltinObject",
|
||||
_("Features for all objects"),
|
||||
_("Common features that can be used for all objects in GDevelop."),
|
||||
"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",
|
||||
|
@@ -107,8 +107,7 @@ void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
|
||||
}
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> SpriteObject::GetProperties(
|
||||
gd::Project& project) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> SpriteObject::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[_("Animate even if hidden or far from the screen")]
|
||||
.SetValue(updateIfNotVisible ? "true" : "false")
|
||||
@@ -119,8 +118,7 @@ std::map<gd::String, gd::PropertyDescriptor> SpriteObject::GetProperties(
|
||||
}
|
||||
|
||||
bool SpriteObject::UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
if (name == _("Animate even if hidden or far from the screen"))
|
||||
updateIfNotVisible = value == "1";
|
||||
|
||||
|
@@ -48,11 +48,9 @@ class GD_CORE_API SpriteObject : public gd::Object {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
gd::Project& project) const override;
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
|
||||
bool UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) override;
|
||||
const gd::String& value) override;
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetInitialInstanceProperties(
|
||||
const gd::InitialInstance& position,
|
||||
|
@@ -119,6 +119,18 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
_("Manipulation of text"),
|
||||
"res/conditions/toujours24.png")
|
||||
|
||||
.AddParameter("string", _("Text"))
|
||||
.AddParameter("string", _("Text to search for"))
|
||||
.SetHidden(); // Deprecated, see StrFindLast instead.
|
||||
|
||||
extension
|
||||
.AddExpression("StrFindLast",
|
||||
_("Search the last occurence in a text"),
|
||||
_("Search the last occurence in a string (return the position of "
|
||||
"the result, from the beginning of the string, or -1 if not found)"),
|
||||
_("Manipulation of text"),
|
||||
"res/conditions/toujours24.png")
|
||||
|
||||
.AddParameter("string", _("Text"))
|
||||
.AddParameter("string", _("Text to search for"));
|
||||
|
||||
@@ -145,6 +157,22 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
_("Manipulation of text"),
|
||||
"res/conditions/toujours24.png")
|
||||
|
||||
.AddParameter("string", _("Text"))
|
||||
.AddParameter("string", _("Text to search for"))
|
||||
.AddParameter("expression",
|
||||
_("Position of the last character in the string to be "
|
||||
"considered in the search"))
|
||||
.SetHidden(); // Deprecated, see StrFindLastFrom instead.
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"StrFindLastFrom",
|
||||
_("Search the last occurence in a text, starting from a position"),
|
||||
_("Search in a text the last occurence, starting from a position (return "
|
||||
" the position of the result, from the beginning of the string, or -1 if not found)"),
|
||||
_("Manipulation of text"),
|
||||
"res/conditions/toujours24.png")
|
||||
|
||||
.AddParameter("string", _("Text"))
|
||||
.AddParameter("string", _("Text to search for"))
|
||||
.AddParameter("expression",
|
||||
|
@@ -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()
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
@@ -146,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
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -49,9 +49,15 @@ InstructionSentenceFormatter::GetAsFormattedText(
|
||||
|
||||
gd::String sentence = metadata.GetSentence();
|
||||
std::replace(sentence.Raw().begin(), sentence.Raw().end(), '\n', ' ');
|
||||
bool parse = true;
|
||||
|
||||
size_t loopCount = 0;
|
||||
bool parse = true;
|
||||
while (parse) {
|
||||
if (loopCount > 40) {
|
||||
break;
|
||||
}
|
||||
loopCount++;
|
||||
|
||||
// Search first parameter
|
||||
parse = false;
|
||||
size_t firstParamPosition = gd::String::npos;
|
||||
|
@@ -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(){};
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -15,7 +15,7 @@ Behavior::~Behavior(){};
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor> Behavior::GetProperties(
|
||||
const gd::SerializerElement& behaviorContent, gd::Project& project) const {
|
||||
const gd::SerializerElement& behaviorContent) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
||||
return nothing;
|
||||
}
|
||||
|
@@ -61,7 +61,7 @@ class GD_CORE_API Behavior {
|
||||
* \see gd::PropertyDescriptor
|
||||
*/
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
const gd::SerializerElement& behaviorContent, gd::Project& project) const;
|
||||
const gd::SerializerElement& behaviorContent) const;
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE wants to update a custom property of the
|
||||
@@ -72,8 +72,7 @@ class GD_CORE_API Behavior {
|
||||
*/
|
||||
virtual bool UpdateProperty(gd::SerializerElement& behaviorContent,
|
||||
const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
return false;
|
||||
};
|
||||
#endif
|
||||
|
@@ -16,7 +16,7 @@ BehaviorsSharedData::~BehaviorsSharedData(){};
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor> BehaviorsSharedData::GetProperties(
|
||||
const gd::SerializerElement& behaviorSharedDataContent, gd::Project& project) const {
|
||||
const gd::SerializerElement& behaviorSharedDataContent) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
||||
return nothing;
|
||||
}
|
||||
|
@@ -64,8 +64,7 @@ class GD_CORE_API BehaviorsSharedData {
|
||||
* \see gd::PropertyDescriptor
|
||||
*/
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
const gd::SerializerElement& behaviorSharedDataContent,
|
||||
gd::Project& project) const;
|
||||
const gd::SerializerElement& behaviorSharedDataContent) const;
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE wants to update a property of the shared data
|
||||
@@ -75,8 +74,7 @@ class GD_CORE_API BehaviorsSharedData {
|
||||
*/
|
||||
virtual bool UpdateProperty(gd::SerializerElement& behaviorSharedDataContent,
|
||||
const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
return false;
|
||||
};
|
||||
#endif
|
||||
|
@@ -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
|
||||
|
@@ -125,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.
|
||||
|
@@ -5,10 +5,12 @@
|
||||
*/
|
||||
|
||||
#include "GDCore/Project/InitialInstance.h"
|
||||
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#endif
|
||||
@@ -27,7 +29,8 @@ InitialInstance::InitialInstance()
|
||||
personalizedSize(false),
|
||||
width(0),
|
||||
height(0),
|
||||
locked(false) {}
|
||||
locked(false),
|
||||
persistentUuid(UUID::MakeUuid4()) {}
|
||||
|
||||
void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
SetObjectName(element.GetStringAttribute("name", "", "nom"));
|
||||
@@ -42,6 +45,9 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
SetLayer(element.GetStringAttribute("layer"));
|
||||
SetLocked(element.GetBoolAttribute("locked", false));
|
||||
|
||||
persistentUuid = element.GetStringAttribute("persistentUuid");
|
||||
if (persistentUuid.empty()) ResetPersistentUuid();
|
||||
|
||||
floatInfos.clear();
|
||||
const SerializerElement& floatPropElement =
|
||||
element.GetChild("numberProperties", 0, "floatInfos");
|
||||
@@ -79,6 +85,9 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("height", GetCustomHeight());
|
||||
element.SetAttribute("locked", IsLocked());
|
||||
|
||||
if (persistentUuid.empty()) persistentUuid = UUID::MakeUuid4();
|
||||
element.SetStringAttribute("persistentUuid", persistentUuid);
|
||||
|
||||
SerializerElement& floatPropElement = element.AddChild("numberProperties");
|
||||
floatPropElement.ConsiderAsArrayOf("property");
|
||||
for (std::map<gd::String, float>::const_iterator floatInfo =
|
||||
@@ -104,6 +113,11 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
GetVariables().SerializeTo(element.AddChild("initialVariables"));
|
||||
}
|
||||
|
||||
InitialInstance& InitialInstance::ResetPersistentUuid() {
|
||||
persistentUuid = UUID::MakeUuid4();
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
InitialInstance::GetCustomProperties(gd::Project& project, gd::Layout& layout) {
|
||||
@@ -146,13 +160,12 @@ const gd::String& InitialInstance::GetRawStringProperty(
|
||||
return it != stringInfos.end() ? it->second : *badStringProperyValue;
|
||||
}
|
||||
|
||||
void InitialInstance::SetRawFloatProperty(const gd::String& name, float value)
|
||||
{
|
||||
void InitialInstance::SetRawFloatProperty(const gd::String& name, float value) {
|
||||
floatInfos[name] = value;
|
||||
}
|
||||
|
||||
void InitialInstance::SetRawStringProperty(const gd::String& name, const gd::String& value)
|
||||
{
|
||||
void InitialInstance::SetRawStringProperty(const gd::String& name,
|
||||
const gd::String& value) {
|
||||
stringInfos[name] = value;
|
||||
}
|
||||
#endif
|
||||
|
@@ -200,7 +200,7 @@ class GD_CORE_API InitialInstance {
|
||||
/**
|
||||
* \brief Get the value of a float property stored in the instance.
|
||||
* \note Only use this when \a GetCustomProperties is too slow (when rendering
|
||||
* instances for example).
|
||||
* instances for example).
|
||||
* \return the value of the property, or 0 if it does
|
||||
* not exists.
|
||||
*/
|
||||
@@ -209,7 +209,7 @@ class GD_CORE_API InitialInstance {
|
||||
/**
|
||||
* \brief Get the value of a string property stored in the instance.
|
||||
* \note Only use this when \a GetCustomProperties is too slow (when rendering
|
||||
* instances for example).
|
||||
* instances for example).
|
||||
* \return the value of the propety, or an empty
|
||||
* string if it does not exists.
|
||||
*/
|
||||
@@ -240,6 +240,12 @@ class GD_CORE_API InitialInstance {
|
||||
* \brief Unserialize the instances container.
|
||||
*/
|
||||
virtual void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Reset the persistent UUID used to recognize
|
||||
* the same initial instance between serialization.
|
||||
*/
|
||||
InitialInstance& ResetPersistentUuid();
|
||||
///@}
|
||||
|
||||
// More properties can be stored in floatInfos and stringInfos.
|
||||
@@ -260,6 +266,7 @@ class GD_CORE_API InitialInstance {
|
||||
float height; ///< Object custom height
|
||||
gd::VariablesContainer initialVariables; ///< Instance specific variables
|
||||
bool locked; ///< True if the instance is locked
|
||||
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID, useful for hot reloading.
|
||||
|
||||
static gd::String*
|
||||
badStringProperyValue; ///< Empty string returned by GetRawStringProperty
|
||||
|
@@ -77,8 +77,7 @@ gd::BehaviorContent& Object::AddBehavior(
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor> Object::GetProperties(
|
||||
gd::Project& project) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> Object::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
||||
return nothing;
|
||||
}
|
||||
|
@@ -139,8 +139,7 @@ class GD_CORE_API Object {
|
||||
* \return a std::map with properties names as key.
|
||||
* \see gd::PropertyDescriptor
|
||||
*/
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
gd::Project& project) const;
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE wants to update a custom property of the object
|
||||
@@ -148,8 +147,7 @@ class GD_CORE_API Object {
|
||||
* \return false if the new value cannot be set
|
||||
*/
|
||||
virtual bool UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
return false;
|
||||
};
|
||||
///@}
|
||||
|
@@ -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
|
||||
|
@@ -99,14 +99,12 @@ std::vector<gd::String> ResourcesManager::GetAllResourceNames() const {
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor> Resource::GetProperties(
|
||||
gd::Project& project) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> Resource::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties(
|
||||
gd::Project& project) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[_("Smooth the image")]
|
||||
.SetValue(smooth ? "true" : "false")
|
||||
@@ -119,8 +117,7 @@ std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties(
|
||||
}
|
||||
|
||||
bool ImageResource::UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
if (name == _("Smooth the image"))
|
||||
smooth = value == "1";
|
||||
else if (name == _("Always loaded in memory"))
|
||||
@@ -563,8 +560,7 @@ void JsonResource::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("disablePreload", IsPreloadDisabled());
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties(
|
||||
gd::Project& project) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties["disablePreload"]
|
||||
.SetValue(disablePreload ? "true" : "false")
|
||||
@@ -575,8 +571,7 @@ std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties(
|
||||
}
|
||||
|
||||
bool JsonResource::UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
if (name == "disablePreload") disablePreload = value == "1";
|
||||
|
||||
return true;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Project;
|
||||
@@ -112,8 +113,7 @@ class GD_CORE_API Resource {
|
||||
* \return a std::map with properties names as key.
|
||||
* \see gd::PropertyDescriptor
|
||||
*/
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
gd::Project& project) const;
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE wants to update a custom property of the
|
||||
@@ -122,8 +122,7 @@ class GD_CORE_API Resource {
|
||||
* \return false if the new value cannot be set
|
||||
*/
|
||||
virtual bool UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
return false;
|
||||
};
|
||||
///@}
|
||||
@@ -178,11 +177,9 @@ class GD_CORE_API ImageResource : public Resource {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual bool UseFile() override { return true; }
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
gd::Project& project) const override;
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
|
||||
bool UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) override;
|
||||
const gd::String& value) override;
|
||||
|
||||
/**
|
||||
* \brief Serialize the object
|
||||
@@ -315,11 +312,9 @@ class GD_CORE_API JsonResource : public Resource {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual bool UseFile() override { return true; }
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
gd::Project& project) const override;
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
|
||||
bool UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) override;
|
||||
const gd::String& value) override;
|
||||
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
#endif
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -90,7 +90,7 @@ template <typename T>
|
||||
void SPtrList<T>::Init(const gd::SPtrList<T>& other) {
|
||||
elements.clear();
|
||||
for (size_t i = 0; i < other.elements.size(); ++i)
|
||||
elements.push_back(std::make_shared<T>(other[i]));
|
||||
elements.push_back(CloneRememberingOriginalElement(other.elements[i]));
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
24
Core/GDCore/Tools/UUID/UUID.h
Normal file
24
Core/GDCore/Tools/UUID/UUID.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef GDCORE_TOOLS_UUID_UUID_H
|
||||
#define GDCORE_TOOLS_UUID_UUID_H
|
||||
|
||||
#include "GDCore/String.h"
|
||||
#include "sole.h"
|
||||
|
||||
namespace gd {
|
||||
namespace UUID {
|
||||
|
||||
/**
|
||||
* Generate a random UUID v4
|
||||
*/
|
||||
inline gd::String MakeUuid4() { return gd::String::From(sole::uuid4()); }
|
||||
|
||||
} // namespace UUID
|
||||
} // namespace gd
|
||||
|
||||
#endif
|
249
Core/GDCore/Tools/UUID/sole.h
Normal file
249
Core/GDCore/Tools/UUID/sole.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* Modified version of sole (https://github.com/r-lyeh-archived/sole) C++11 library
|
||||
* to only generate UUID v4.
|
||||
*
|
||||
* Sole is a lightweight C++11 library to generate universally unique identificators.
|
||||
* Sole provides interface for UUID versions 0, 1 and 4.
|
||||
*
|
||||
* https://github.com/r-lyeh/sole
|
||||
* Copyright (c) 2013,2014,2015 r-lyeh. zlib/libpng licensed.
|
||||
*
|
||||
* Based on code by Dmitri Bouianov, Philip O'Toole, Poco C++ libraries and anonymous
|
||||
* code found on the net. Thanks guys!
|
||||
*
|
||||
* Theory: (see Hoylen's answer at [1])
|
||||
* - UUID version 1 (48-bit MAC address + 60-bit clock with a resolution of 100ns)
|
||||
* Clock wraps in 3603 A.D.
|
||||
* Up to 10000000 UUIDs per second.
|
||||
* MAC address revealed.
|
||||
*
|
||||
* - UUID Version 4 (122-bits of randomness)
|
||||
* See [2] or other analysis that describe how very unlikely a duplicate is.
|
||||
*
|
||||
* - Use v1 if you need to sort or classify UUIDs per machine.
|
||||
* Use v1 if you are worried about leaving it up to probabilities (e.g. your are the
|
||||
* type of person worried about the earth getting destroyed by a large asteroid in your
|
||||
* lifetime). Just use a v1 and it is guaranteed to be unique till 3603 AD.
|
||||
*
|
||||
* - Use v4 if you are worried about security issues and determinism. That is because
|
||||
* v1 UUIDs reveal the MAC address of the machine it was generated on and they can be
|
||||
* predictable. Use v4 if you need more than 10 million uuids per second, or if your
|
||||
* application wants to live past 3603 A.D.
|
||||
* Additionally a custom UUID v0 is provided:
|
||||
* - 16-bit PID + 48-bit MAC address + 60-bit clock with a resolution of 100ns since Unix epoch
|
||||
* - Format is EPOCH_LOW-EPOCH_MID-VERSION(0)|EPOCH_HI-PID-MAC
|
||||
* - Clock wraps in 3991 A.D.
|
||||
* - Up to 10000000 UUIDs per second.
|
||||
* - MAC address and PID revealed.
|
||||
* References:
|
||||
* - [1] http://stackoverflow.com/questions/1155008/how-unique-is-uuid
|
||||
* - [2] http://en.wikipedia.org/wiki/UUID#Random%5FUUID%5Fprobability%5Fof%5Fduplicates
|
||||
* - http://en.wikipedia.org/wiki/Universally_unique_identifier
|
||||
* - http://en.cppreference.com/w/cpp/numeric/random/random_device
|
||||
* - http://www.itu.int/ITU-T/asn1/uuid.html f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
||||
* - rlyeh ~~ listening to Hedon Cries / Until The Sun Goes up
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdio.h> // for size_t; should be stddef.h instead; however, clang+archlinux fails when compiling it (@Travis-Ci)
|
||||
#include <sys/types.h> // for uint32_t; should be stdint.h instead; however, GCC 5 on OSX fails when compiling it (See issue #11)
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
// public API
|
||||
|
||||
namespace sole
|
||||
{
|
||||
// 128-bit basic UUID type that allows comparison and sorting.
|
||||
// Use .str() for printing and .pretty() for pretty printing.
|
||||
// Also, ostream friendly.
|
||||
struct uuid
|
||||
{
|
||||
uint64_t ab;
|
||||
uint64_t cd;
|
||||
|
||||
bool operator==( const uuid &other ) const;
|
||||
bool operator!=( const uuid &other ) const;
|
||||
bool operator <( const uuid &other ) const;
|
||||
|
||||
std::string base62() const;
|
||||
std::string str() const;
|
||||
|
||||
template<typename ostream>
|
||||
inline friend ostream &operator<<( ostream &os, const uuid &self ) {
|
||||
return os << self.str(), os;
|
||||
}
|
||||
};
|
||||
|
||||
// Generators
|
||||
uuid uuid4(); // UUID v4, pros: anonymous, fast; con: uuids "can clash"
|
||||
|
||||
// Rebuilders
|
||||
uuid rebuild( uint64_t ab, uint64_t cd );
|
||||
uuid rebuild( const std::string &uustr );
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4127)
|
||||
#endif
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash< sole::uuid > {
|
||||
public:
|
||||
// hash functor: hash uuid to size_t value by pseudorandomizing transform
|
||||
size_t operator()( const sole::uuid &uuid ) const {
|
||||
if( sizeof(size_t) > 4 ) {
|
||||
return size_t( uuid.ab ^ uuid.cd );
|
||||
} else {
|
||||
uint64_t hash64 = uuid.ab ^ uuid.cd;
|
||||
return size_t( uint32_t( hash64 >> 32 ) ^ uint32_t( hash64 ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// implementation
|
||||
|
||||
#include <memory.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
inline bool sole::uuid::operator==( const sole::uuid &other ) const {
|
||||
return ab == other.ab && cd == other.cd;
|
||||
}
|
||||
inline bool sole::uuid::operator!=( const sole::uuid &other ) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
inline bool sole::uuid::operator<( const sole::uuid &other ) const {
|
||||
if( ab < other.ab ) return true;
|
||||
if( ab > other.ab ) return false;
|
||||
if( cd < other.cd ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace sole {
|
||||
|
||||
inline std::string uuid::str() const {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::nouppercase << std::setfill('0');
|
||||
|
||||
uint32_t a = (ab >> 32);
|
||||
uint32_t b = (ab & 0xFFFFFFFF);
|
||||
uint32_t c = (cd >> 32);
|
||||
uint32_t d = (cd & 0xFFFFFFFF);
|
||||
|
||||
ss << std::setw(8) << (a) << '-';
|
||||
ss << std::setw(4) << (b >> 16) << '-';
|
||||
ss << std::setw(4) << (b & 0xFFFF) << '-';
|
||||
ss << std::setw(4) << (c >> 16 ) << '-';
|
||||
ss << std::setw(4) << (c & 0xFFFF);
|
||||
ss << std::setw(8) << d;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
inline std::string uuid::base62() const {
|
||||
int base62len = 10 + 26 + 26;
|
||||
const char base62[] =
|
||||
"0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
char res[24], *end = &res[24]; *(--end) = '\0';
|
||||
uint64_t rem, AB = ab, CD = cd;
|
||||
do {
|
||||
rem = CD % base62len;
|
||||
*--end = base62[int(rem)];
|
||||
CD /= base62len;
|
||||
} while (CD > 0);
|
||||
*--end = '-';
|
||||
do {
|
||||
rem = AB % base62len;
|
||||
*--end = base62[int(rem)];
|
||||
AB /= base62len;
|
||||
} while (AB > 0);
|
||||
return end;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// UUID implementations
|
||||
|
||||
inline uuid uuid4() {
|
||||
static std::random_device rd;
|
||||
static std::uniform_int_distribution<uint64_t> dist(0, (uint64_t)(~0));
|
||||
|
||||
uuid my;
|
||||
|
||||
my.ab = dist(rd);
|
||||
my.cd = dist(rd);
|
||||
|
||||
my.ab = (my.ab & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL;
|
||||
my.cd = (my.cd & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL;
|
||||
|
||||
return my;
|
||||
}
|
||||
|
||||
inline uuid rebuild( uint64_t ab, uint64_t cd ) {
|
||||
uuid u;
|
||||
u.ab = ab; u.cd = cd;
|
||||
return u;
|
||||
}
|
||||
|
||||
inline uuid rebuild( const std::string &uustr ) {
|
||||
char sep;
|
||||
uint64_t a,b,c,d,e;
|
||||
uuid u = { 0, 0 };
|
||||
auto idx = uustr.find_first_of("-");
|
||||
if( idx != std::string::npos ) {
|
||||
// single separator, base62 notation
|
||||
if( uustr.find_first_of("-",idx+1) == std::string::npos ) {
|
||||
auto rebase62 = [&]( const char *input, size_t limit ) -> uint64_t {
|
||||
int base62len = 10 + 26 + 26;
|
||||
auto strpos = []( char ch ) -> size_t {
|
||||
if( ch >= 'a' ) return ch - 'a' + 10 + 26;
|
||||
if( ch >= 'A' ) return ch - 'A' + 10;
|
||||
return ch - '0';
|
||||
};
|
||||
uint64_t res = strpos( input[0] );
|
||||
for( size_t i = 1; i < limit; ++i )
|
||||
res = base62len * res + strpos( input[i] );
|
||||
return res;
|
||||
};
|
||||
u.ab = rebase62( &uustr[0], idx );
|
||||
u.cd = rebase62( &uustr[idx+1], uustr.size() - (idx+1) );
|
||||
}
|
||||
// else classic hex notation
|
||||
else {
|
||||
std::stringstream ss( uustr );
|
||||
if( ss >> std::hex >> a >> sep >> b >> sep >> c >> sep >> d >> sep >> e ) {
|
||||
if( ss.eof() ) {
|
||||
u.ab = (a << 32) | (b << 16) | c;
|
||||
u.cd = (d << 48) | e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
} // ::sole
|
@@ -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");
|
||||
|
@@ -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,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 [];
|
||||
},
|
||||
};
|
||||
|
@@ -48,7 +48,7 @@ gd::String GetAnchorAsString(AnchorBehavior::VerticalAnchor anchor) {
|
||||
} // namespace
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
|
||||
const gd::SerializerElement& behaviorContent, gd::Project& project) const {
|
||||
const gd::SerializerElement& behaviorContent) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
|
||||
properties[_("relativeToOriginalWindowSize")]
|
||||
@@ -129,8 +129,7 @@ AnchorBehavior::VerticalAnchor GetVerticalAnchorFromString(
|
||||
|
||||
bool AnchorBehavior::UpdateProperty(gd::SerializerElement& behaviorContent,
|
||||
const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
if (name == _("relativeToOriginalWindowSize"))
|
||||
behaviorContent.SetAttribute("relativeToOriginalWindowSize", value == "1");
|
||||
else if (name == _("Left edge anchor"))
|
||||
|
@@ -38,12 +38,10 @@ class GD_EXTENSION_API AnchorBehavior : public Behavior {
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
const gd::SerializerElement& behaviorContent,
|
||||
gd::Project& project) const override;
|
||||
const gd::SerializerElement& behaviorContent) const override;
|
||||
virtual bool UpdateProperty(gd::SerializerElement& behaviorContent,
|
||||
const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) override;
|
||||
const gd::String& value) override;
|
||||
#endif
|
||||
|
||||
virtual void InitializeContent(
|
||||
|
@@ -40,6 +40,26 @@ gdjs.AnchorRuntimeBehavior.VerticalAnchor = {
|
||||
PROPORTIONAL: 3
|
||||
};
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.leftEdgeAnchor !== newBehaviorData.leftEdgeAnchor) {
|
||||
this._leftEdgeAnchor = newBehaviorData.leftEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.rightEdgeAnchor !== newBehaviorData.rightEdgeAnchor) {
|
||||
this._rightEdgeAnchor = newBehaviorData.rightEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.topEdgeAnchor !== newBehaviorData.topEdgeAnchor) {
|
||||
this._topEdgeAnchor = newBehaviorData.topEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.bottomEdgeAnchor !== newBehaviorData.bottomEdgeAnchor) {
|
||||
this._bottomEdgeAnchor = newBehaviorData.bottomEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.relativeToOriginalWindowSize !== newBehaviorData.relativeToOriginalWindowSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype.onActivate = function() {
|
||||
this._invalidDistances = true;
|
||||
};
|
||||
|
@@ -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,74 +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('resource')
|
||||
.addExtraInfo('font')
|
||||
.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;
|
||||
};
|
||||
@@ -111,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',
|
||||
@@ -120,7 +127,8 @@ module.exports = {
|
||||
})
|
||||
);
|
||||
|
||||
objectBBText.updateInitialInstanceProperty = function(
|
||||
// $FlowExpectedError
|
||||
objectBBText.updateInitialInstanceProperty = function (
|
||||
objectContent,
|
||||
instance,
|
||||
propertyName,
|
||||
@@ -130,7 +138,8 @@ module.exports = {
|
||||
) {
|
||||
return false;
|
||||
};
|
||||
objectBBText.getInitialInstanceProperties = function(
|
||||
// $FlowExpectedError
|
||||
objectBBText.getInitialInstanceProperties = function (
|
||||
content,
|
||||
instance,
|
||||
project,
|
||||
@@ -161,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;
|
||||
|
||||
@@ -173,8 +182,6 @@ module.exports = {
|
||||
property.expressionLabel,
|
||||
property.expressionDescription,
|
||||
'',
|
||||
'',
|
||||
property.iconPath,
|
||||
property.iconPath
|
||||
)
|
||||
.addParameter('object', objectName, objectName, false)
|
||||
@@ -187,8 +194,6 @@ module.exports = {
|
||||
property.expressionLabel,
|
||||
property.expressionDescription,
|
||||
'',
|
||||
'',
|
||||
property.iconPath,
|
||||
property.iconPath
|
||||
)
|
||||
.addParameter('object', objectName, objectName, false)
|
||||
@@ -392,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 [];
|
||||
},
|
||||
/**
|
||||
@@ -406,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',
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -419,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(
|
||||
@@ -454,6 +465,7 @@ module.exports = {
|
||||
|
||||
const bbTextStyles = {
|
||||
default: {
|
||||
// Use a default font family the time for the resource font to be loaded.
|
||||
fontFamily: 'Arial',
|
||||
fontSize: '24px',
|
||||
fill: '#cccccc',
|
||||
@@ -478,7 +490,7 @@ module.exports = {
|
||||
/**
|
||||
* Return the path to the thumbnail of the specified object.
|
||||
*/
|
||||
RenderedBBTextInstance.getThumbnail = function(
|
||||
RenderedBBTextInstance.getThumbnail = function (
|
||||
project,
|
||||
resourcesLoader,
|
||||
object
|
||||
@@ -489,66 +501,51 @@ module.exports = {
|
||||
/**
|
||||
* This is called to update the PIXI object on the scene editor
|
||||
*/
|
||||
RenderedBBTextInstance.prototype.update = function() {
|
||||
const rawText = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('text')
|
||||
.getValue();
|
||||
RenderedBBTextInstance.prototype.update = function () {
|
||||
const properties = this._associatedObject.getProperties();
|
||||
|
||||
const rawText = properties.get('text').getValue();
|
||||
if (rawText !== this._pixiObject.text) {
|
||||
this._pixiObject.setText(rawText);
|
||||
this._pixiObject.text = rawText;
|
||||
}
|
||||
|
||||
const opacity = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('opacity')
|
||||
.getValue();
|
||||
const opacity = properties.get('opacity').getValue();
|
||||
this._pixiObject.alpha = opacity / 255;
|
||||
|
||||
const color = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('color')
|
||||
.getValue();
|
||||
const color = properties.get('color').getValue();
|
||||
this._pixiObject.textStyles.default.fill = color;
|
||||
|
||||
const fontSize = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('fontSize')
|
||||
.getValue();
|
||||
const fontSize = properties.get('fontSize').getValue();
|
||||
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
|
||||
|
||||
const fontResourceName = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('fontFamily')
|
||||
.getValue();
|
||||
const fontResourceName = properties.get('fontFamily').getValue();
|
||||
|
||||
if (this._fontResourceName !== fontResourceName) {
|
||||
this._fontResourceName = fontResourceName;
|
||||
|
||||
this._pixiResourcesLoader
|
||||
.loadFontFamily(this._project, fontResourceName)
|
||||
.then(fontFamily => {
|
||||
.then((fontFamily) => {
|
||||
// Once the font is loaded, we can use the given fontFamily.
|
||||
this._pixiObject.textStyles.default.fontFamily = fontFamily;
|
||||
this._pixiObject.dirty = true;
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
// Ignore errors
|
||||
console.warn('Unable to load font family', err);
|
||||
console.warn(
|
||||
'Unable to load font family for RenderedBBTextInstance',
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const wordWrap = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('wordWrap')
|
||||
.getValue();
|
||||
const wordWrap = properties.get('wordWrap').getValue() === 'true';
|
||||
if (wordWrap !== this._pixiObject._style.wordWrap) {
|
||||
this._pixiObject._style.wordWrap = wordWrap === 'true';
|
||||
this._pixiObject._style.wordWrap = wordWrap;
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
const align = this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('align')
|
||||
.getValue();
|
||||
const align = properties.get('align').getValue();
|
||||
if (align !== this._pixiObject._style.align) {
|
||||
this._pixiObject._style.align = align;
|
||||
this._pixiObject.dirty = true;
|
||||
@@ -566,7 +563,7 @@ module.exports = {
|
||||
const customWidth = this._instance.getCustomWidth();
|
||||
if (
|
||||
this._pixiObject &&
|
||||
this._pixiObject.textStyles.default.wordWrapWidth !== customWidth
|
||||
this._pixiObject._style.wordWrapWidth !== customWidth
|
||||
) {
|
||||
this._pixiObject._style.wordWrapWidth = customWidth;
|
||||
this._pixiObject.dirty = true;
|
||||
@@ -577,14 +574,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;
|
||||
};
|
||||
|
||||
|
@@ -6,7 +6,7 @@
|
||||
* @param {gdjs.BBTextRuntimeObject} runtimeObject The object to render
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The gdjs.RuntimeScene in which the object is
|
||||
*/
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene) {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer = function (runtimeObject, runtimeScene) {
|
||||
this._object = runtimeObject;
|
||||
|
||||
// Load (or reset) the text
|
||||
@@ -25,8 +25,6 @@ gdjs.BBTextRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene) {
|
||||
align: runtimeObject._align,
|
||||
},
|
||||
});
|
||||
|
||||
this._object.hidden = !runtimeObject._visible;
|
||||
} else {
|
||||
this.updateColor();
|
||||
this.updateAlignment();
|
||||
@@ -48,68 +46,69 @@ gdjs.BBTextRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene) {
|
||||
this.updatePosition();
|
||||
this.updateAngle();
|
||||
this.updateOpacity();
|
||||
this.updateVisible();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectRenderer = gdjs.BBTextRuntimeObjectPixiRenderer;
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getRendererObject = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getRendererObject = function () {
|
||||
return this._pixiObject;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateWordWrap = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateWordWrap = function () {
|
||||
this._pixiObject._style.wordWrap = this._object._wordWrap;
|
||||
this._pixiObject.dirty = true;
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateWrappingWidth = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateWrappingWidth = function () {
|
||||
this._pixiObject._style.wordWrapWidth = this._object._wrappingWidth;
|
||||
this._pixiObject.dirty = true;
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateText = function() {
|
||||
this._pixiObject.setText(this._object._text);
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateText = function () {
|
||||
this._pixiObject.text = this._object._text;
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateColor = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateColor = function () {
|
||||
this._pixiObject.textStyles.default.fill = this._object._color;
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAlignment = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAlignment = function () {
|
||||
this._pixiObject._style.align = this._object._align;
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontFamily = function() {
|
||||
this._pixiObject.textStyles.default.fontFamily = this._object._fontFamily;
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontFamily = function () {
|
||||
this._pixiObject.textStyles.default.fontFamily = this._object._runtimeScene
|
||||
.getGame()
|
||||
.getFontManager()
|
||||
.getFontFamily(this._object._fontFamily);
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontSize = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontSize = function () {
|
||||
this._pixiObject.textStyles.default.fontSize = this._object._fontSize + 'px';
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updatePosition = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updatePosition = function () {
|
||||
this._pixiObject.position.x = this._object.x + this._pixiObject.width / 2;
|
||||
this._pixiObject.position.y = this._object.y + this._pixiObject.height / 2;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateVisible = function() {
|
||||
this._pixiObject.hidden = !this._object._visible;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAngle = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAngle = function () {
|
||||
this._pixiObject.rotation = gdjs.toRad(this._object.angle);
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateOpacity = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateOpacity = function () {
|
||||
this._pixiObject.alpha = this._object._opacity / 255;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getWidth = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getWidth = function () {
|
||||
return this._pixiObject.width;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getHeight = function() {
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getHeight = function () {
|
||||
return this._pixiObject.height;
|
||||
};
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* @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 {boolean} content.visible Deprecated - 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
|
||||
@@ -25,9 +25,8 @@ gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
gdjs.RuntimeObject.call(this, runtimeScene, objectData);
|
||||
|
||||
/** @type {number} */
|
||||
this._opacity = objectData.content.opacity;
|
||||
/** @type {boolean} */
|
||||
this._visible = objectData.content.visible;
|
||||
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 {string} */
|
||||
this._text = objectData.content.text;
|
||||
/** @type {string} */
|
||||
@@ -35,7 +34,8 @@ gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
/** @type {string} */
|
||||
this._fontFamily = objectData.content.fontFamily;
|
||||
/** @type {number} */
|
||||
this._fontSize = objectData.content.fontSize;
|
||||
this._fontSize = parseFloat(objectData.content.fontSize);
|
||||
// parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
|
||||
/** @type {boolean} */
|
||||
this._wordWrap = objectData.content.wordWrap;
|
||||
/** @type {number} */
|
||||
@@ -46,9 +46,12 @@ gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
if (this._renderer)
|
||||
gdjs.BBTextRuntimeObjectRenderer.call(this._renderer, this, runtimeScene);
|
||||
else
|
||||
/** @type {gdjs.BBTextRuntimeObjectRenderer} */
|
||||
this._renderer = new gdjs.BBTextRuntimeObjectRenderer(this, runtimeScene);
|
||||
|
||||
// While this should rather be exposed as a property for all objects, honor the "visible"
|
||||
// property that is specific to this object.
|
||||
this.hidden = !objectData.content.visible;
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
};
|
||||
@@ -62,14 +65,49 @@ gdjs.BBTextRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {BBTextObjectDataType} oldObjectData
|
||||
* @param {BBTextObjectDataType} newObjectData
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.content.opacity !== newObjectData.content.opacity) {
|
||||
this.setOpacity(newObjectData.content.opacity);
|
||||
}
|
||||
if (oldObjectData.content.visible !== newObjectData.content.visible) {
|
||||
this.hide(!newObjectData.content.visible);
|
||||
}
|
||||
if (oldObjectData.content.text !== newObjectData.content.text) {
|
||||
this.setBBText(newObjectData.content.text);
|
||||
}
|
||||
if (oldObjectData.content.color !== newObjectData.content.color) {
|
||||
this._color = newObjectData.content.color;
|
||||
this._renderer.updateColor();
|
||||
}
|
||||
if (oldObjectData.content.fontFamily !== newObjectData.content.fontFamily) {
|
||||
this.setFontFamily(newObjectData.content.fontFamily);
|
||||
}
|
||||
if (oldObjectData.content.fontSize !== newObjectData.content.fontSize) {
|
||||
this.setFontSize(newObjectData.content.fontSize);
|
||||
}
|
||||
if (oldObjectData.content.wordWrap !== newObjectData.content.wordWrap) {
|
||||
this.setWordWrap(newObjectData.content.wordWrap);
|
||||
}
|
||||
if (oldObjectData.content.align !== newObjectData.content.align) {
|
||||
this.setAlignment(newObjectData.content.align);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @private
|
||||
*/
|
||||
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);
|
||||
else
|
||||
this.setWrappingWidth(250); // This value is the default wrapping width of the runtime object.
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.onDestroyFromScene = function(runtimeScene) {
|
||||
@@ -77,13 +115,16 @@ gdjs.BBTextRuntimeObject.prototype.onDestroyFromScene = function(runtimeScene) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Set/Get BBText base style properties
|
||||
* Set the markup text to display.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.setBBText = function(text) {
|
||||
this._text = text;
|
||||
this._renderer.updateText();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the markup text displayed by the object.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.getBBText = function() {
|
||||
return this._text;
|
||||
};
|
||||
|
@@ -1,9 +1,67 @@
|
||||
This extension is using release version 0.8.0 (commit 336bed0b206043e2c3e81c373b7ca02094ecabe7) of the pixi-multistyle-text library:
|
||||
https://github.com/tleunen/pixi-multistyle-text
|
||||
# pixi-multistyle-text
|
||||
|
||||
The BBcode tag feature was especially added for Gdevelop and this extension (commit 2a7be2084598933502c76419d7a86c0e6cd11719)
|
||||
[](https://nodei.co/npm/pixi-multistyle-text/)
|
||||
|
||||
README:
|
||||
Add a MultiStyleText object inside pixi.js to easily create text using different styles.
|
||||
Add a `MultiStyleText` object inside [pixi.js](https://github.com/GoodBoyDigital/pixi.js) to easily create text using different styles.
|
||||
|
||||
License: MIT
|
||||
## Example
|
||||
|
||||
In the example below, we are defining 4 text styles.
|
||||
`default` is the default style for the text, and the others matches the tags inside the text.
|
||||
|
||||
```js
|
||||
let text = new MultiStyleText("Let's make some <ml>multiline</ml>\nand <ms>multistyle</ms> text for\n<pixi>Pixi.js!</pixi>",
|
||||
{
|
||||
"default": {
|
||||
fontFamily: "Arial",
|
||||
fontSize: "24px",
|
||||
fill: "#cccccc",
|
||||
align: "center"
|
||||
},
|
||||
"ml": {
|
||||
fontStyle: "italic",
|
||||
fill: "#ff8888"
|
||||
},
|
||||
"ms": {
|
||||
fontStyle: "italic",
|
||||
fill: "#4488ff"
|
||||
},
|
||||
"pixi": {
|
||||
fontSize: "64px",
|
||||
fill: "#efefef"
|
||||
}
|
||||
});
|
||||
```
|
||||
## Build instructions
|
||||
|
||||
```
|
||||
$ yarn install
|
||||
$ yarn build
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### `text = new MultiStyleText(text, textStyles)`
|
||||
|
||||
Creates a new `MultiStyleText` with the given text and styles.
|
||||
|
||||
#### `textStyles`
|
||||
Type: `{ [key: string]: ExtendedTextStyle }`
|
||||
|
||||
Each key of this dictionary should match with a tag in the text. Use the key `default` for the default style.
|
||||
|
||||
Each `ExtendedTextStyle` object can have [any of the properties of a standard PIXI text style](http://pixijs.download/release/docs/PIXI.TextStyle.html), in addition to a `valign` property that allows you to specify where text is rendered relative to larger text on the same line (`"top"`, `"middle"`, or `"bottom"`).
|
||||
|
||||
The `align`, `wordWrap`, `wordWrapWidth`, and `breakWord` properties are ignored on all styles _except for the `default` style_, which controls those properties for the entire text object.
|
||||
|
||||
If text is rendered without any value assigned to a given parameter, Pixi's defaults are used.
|
||||
|
||||
## Demo
|
||||
```
|
||||
$ yarn demo
|
||||
```
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT, see [LICENSE.md](http://github.com/tleunen/pixi-multistyle-text/blob/master/LICENSE.md) for details.
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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 @@ void DestroyOutsideBehavior::InitializeContent(gd::SerializerElement& content) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
DestroyOutsideBehavior::GetProperties(
|
||||
const gd::SerializerElement& behaviorContent, gd::Project& project) const {
|
||||
const gd::SerializerElement& behaviorContent) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
|
||||
properties["extraBorder"]
|
||||
@@ -33,8 +33,7 @@ DestroyOutsideBehavior::GetProperties(
|
||||
bool DestroyOutsideBehavior::UpdateProperty(
|
||||
gd::SerializerElement& behaviorContent,
|
||||
const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
const gd::String& value) {
|
||||
if (name == "extraBorder")
|
||||
behaviorContent.SetAttribute("extraBorder", value.To<double>());
|
||||
else
|
||||
|
@@ -21,16 +21,14 @@ class GD_EXTENSION_API DestroyOutsideBehavior : public gd::Behavior {
|
||||
public:
|
||||
DestroyOutsideBehavior(){};
|
||||
virtual ~DestroyOutsideBehavior(){};
|
||||
virtual Behavior* Clone() const { return new DestroyOutsideBehavior(*this); }
|
||||
virtual Behavior* Clone() const override { return new DestroyOutsideBehavior(*this); }
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
const gd::SerializerElement& behaviorContent,
|
||||
gd::Project& project) const override;
|
||||
const gd::SerializerElement& behaviorContent) const override;
|
||||
virtual bool UpdateProperty(gd::SerializerElement& behaviorContent,
|
||||
const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) override;
|
||||
const gd::String& value) override;
|
||||
#endif
|
||||
|
||||
virtual void InitializeContent(
|
||||
|
@@ -20,6 +20,14 @@ gdjs.DestroyOutsideRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("DestroyOutsideBehavior::DestroyOutside", gdjs.DestroyOutsideRuntimeBehavior);
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.extraBorder !== newBehaviorData.extraBorder) {
|
||||
this._extraBorder = newBehaviorData.extraBorder;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
|
@@ -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).
|
File diff suppressed because one or more lines are too long
5
Extensions/DialogueTree/bondage.js/version.txt
Normal file
5
Extensions/DialogueTree/bondage.js/version.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
This extension is using bondage.js library to parse yarn syntax.
|
||||
https://github.com/hylyh/bondage.js
|
||||
|
||||
The current build used is built from commit 3c63e21
|
||||
|
@@ -101,7 +101,20 @@ gdjs.dialogueTree.isRunning = function() {
|
||||
gdjs.dialogueTree.scrollClippedText = function() {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning) return;
|
||||
|
||||
if (this.dialogueText) {
|
||||
// Autoscroll commands so the user doesnt have to press again
|
||||
if (
|
||||
gdjs.dialogueTree._isLineTypeCommand() &&
|
||||
this.dialogueDataType === 'text' &&
|
||||
this.dialogueBranchTitle === this.dialogueData.data.title &&
|
||||
this.lineNum === this.dialogueData.lineNum &&
|
||||
gdjs.dialogueTree.hasClippedScrollingCompleted()
|
||||
) {
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
return
|
||||
}
|
||||
|
||||
// Increment scrolling of clipped text
|
||||
if (this.dialogueText && this.dialogueDataType === 'text' && this.clipTextEnd < this.dialogueText.length) {
|
||||
this.clipTextEnd += 1;
|
||||
}
|
||||
};
|
||||
@@ -110,7 +123,7 @@ gdjs.dialogueTree.scrollClippedText = function() {
|
||||
* Scroll the clipped text to its end, so the entire text is printed. This can be useful in keeping the event sheet logic simpler, while supporting more variation.
|
||||
*/
|
||||
gdjs.dialogueTree.completeClippedTextScrolling = function() {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning || !this.dialogueText)
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning || !this.dialogueText || this.dialogueDataType !== 'text')
|
||||
return;
|
||||
this.clipTextEnd = this.dialogueText.length;
|
||||
};
|
||||
@@ -120,8 +133,11 @@ gdjs.dialogueTree.completeClippedTextScrolling = function() {
|
||||
* Useful to prevent the user from skipping to next line before the current one has been printed fully.
|
||||
*/
|
||||
gdjs.dialogueTree.hasClippedScrollingCompleted = function() {
|
||||
if (this.dialogueData && this.dialogueText.length) {
|
||||
return this.clipTextEnd >= this.dialogueText.length;
|
||||
if (!this.dialogueIsRunning || this.dialogueDataType === '') return false;
|
||||
|
||||
if (this.dialogueData && this.dialogueText.length > 0 && this.clipTextEnd >= this.dialogueText.length) {
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.warn('Scroll completed:', this.clipTextEnd,'/', this.dialogueText.length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -131,8 +147,8 @@ gdjs.dialogueTree.hasClippedScrollingCompleted = function() {
|
||||
* Used with the scrollClippedText to achieve a classic scrolling text, as well as any <<wait>> effects to pause scrolling.
|
||||
*/
|
||||
gdjs.dialogueTree.getClippedLineText = function() {
|
||||
return this.dialogueText.length
|
||||
? this.dialogueText.substring(0, this.clipTextEnd)
|
||||
return this.dialogueIsRunning && this.dialogueText.length
|
||||
? this.dialogueText.substring(0, this.clipTextEnd + 1)
|
||||
: '';
|
||||
};
|
||||
|
||||
@@ -141,8 +157,9 @@ gdjs.dialogueTree.getClippedLineText = function() {
|
||||
* Note that using this instead getClippedLineText will skip any <<wait>> commands entirely.
|
||||
*/
|
||||
gdjs.dialogueTree.getLineText = function() {
|
||||
this.completeClippedTextScrolling();
|
||||
return this.dialogueText.length ? this.dialogueText : '';
|
||||
return this.dialogueIsRunning && this.dialogueText.length
|
||||
? this.dialogueText
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,6 +177,7 @@ gdjs.dialogueTree.commandParametersCount = function() {
|
||||
* @param {number} paramIndex The index of the parameter to get.
|
||||
*/
|
||||
gdjs.dialogueTree.getCommandParameter = function(paramIndex) {
|
||||
if (paramIndex === -1 && this.commandParameters.length > 0) return this.commandParameters[0];
|
||||
if (
|
||||
this.commandParameters &&
|
||||
this.commandParameters.length >= paramIndex + 1
|
||||
@@ -177,23 +195,28 @@ gdjs.dialogueTree.getCommandParameter = function(paramIndex) {
|
||||
* @param {string} command The command you want to check for being called. Write it without the `<<>>`.
|
||||
*/
|
||||
gdjs.dialogueTree.isCommandCalled = function(command) {
|
||||
if (!this.dialogueIsRunning) return false;
|
||||
|
||||
var commandCalls = gdjs.dialogueTree.commandCalls;
|
||||
var clipTextEnd = gdjs.dialogueTree.clipTextEnd;
|
||||
var dialogueText = gdjs.dialogueTree.dialogueText;
|
||||
|
||||
if (this.pauseScrolling || !commandCalls) return false;
|
||||
return this.commandCalls.some(function(call, index) {
|
||||
if (clipTextEnd < call.time) return false;
|
||||
if (call.cmd === 'wait' && clipTextEnd !== dialogueText.length) {
|
||||
if (clipTextEnd !== 0 && clipTextEnd < call.time) return false;
|
||||
if (call.cmd === 'wait' && (clipTextEnd === 0 || clipTextEnd !== dialogueText.length)) {
|
||||
gdjs.dialogueTree.pauseScrolling = true;
|
||||
setTimeout(function() {
|
||||
gdjs.dialogueTree.pauseScrolling = false;
|
||||
commandCalls.splice(index, 1);
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('CMD:', call);
|
||||
}, parseInt(call.params[1], 10));
|
||||
}
|
||||
if (call.cmd === command) {
|
||||
gdjs.dialogueTree.commandParameters = call.params;
|
||||
commandCalls.splice(index, 1);
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('CMD:', call);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -225,7 +248,7 @@ gdjs.dialogueTree._cycledOptionIndex = function(optionIndex) {
|
||||
* @param {number} optionIndex The index of the option you want to get
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOption = function(optionIndex) {
|
||||
if (!this.options.length) return [];
|
||||
if (!this.dialogueIsRunning || !this.options.length) return [];
|
||||
optionIndex = gdjs.dialogueTree._normalizedOptionIndex(optionIndex);
|
||||
return this.options[optionIndex];
|
||||
};
|
||||
@@ -239,7 +262,7 @@ gdjs.dialogueTree.getLineOptionsText = function(
|
||||
optionSelectionCursor,
|
||||
addNewLine
|
||||
) {
|
||||
if (!this.options.length) return '';
|
||||
if (!this.dialogueIsRunning || !this.options.length) return '';
|
||||
var textResult = '';
|
||||
this.options.forEach(function(optionText, index) {
|
||||
if (index === gdjs.dialogueTree.selectedOption) {
|
||||
@@ -266,7 +289,7 @@ gdjs.dialogueTree.getLineOptionsTextVertical = function(optionSelectionCursor) {
|
||||
* @returns {number} The number of options
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOptionsCount = function() {
|
||||
if (this.options.length) {
|
||||
if (this.dialogueIsRunning && this.options.length) {
|
||||
return this.optionsCount;
|
||||
}
|
||||
return 0;
|
||||
@@ -278,6 +301,7 @@ gdjs.dialogueTree.getLineOptionsCount = function() {
|
||||
* This will advance the dialogue tree to the dialogue branch was selected by the player.
|
||||
*/
|
||||
gdjs.dialogueTree.confirmSelectOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (
|
||||
this.dialogueData.select &&
|
||||
!this.selectedOptionUpdated &&
|
||||
@@ -301,6 +325,7 @@ gdjs.dialogueTree.confirmSelectOption = function() {
|
||||
* Select next option during Options type line parsing. Hook this to your game input.
|
||||
*/
|
||||
gdjs.dialogueTree.selectNextOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption += 1;
|
||||
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
|
||||
@@ -314,6 +339,7 @@ gdjs.dialogueTree.selectNextOption = function() {
|
||||
* Select previous option during Options type line parsing. Hook this to your game input.
|
||||
*/
|
||||
gdjs.dialogueTree.selectPreviousOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption -= 1;
|
||||
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
|
||||
@@ -328,9 +354,10 @@ gdjs.dialogueTree.selectPreviousOption = function() {
|
||||
* @param {number} optionIndex The index of the option to select
|
||||
*/
|
||||
gdjs.dialogueTree.selectOption = function(optionIndex) {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption = gdjs.dialogueTree._normalizedOptionIndex(
|
||||
this.selectedOption
|
||||
optionIndex
|
||||
);
|
||||
this.selectedOptionUpdated = true;
|
||||
}
|
||||
@@ -341,6 +368,7 @@ gdjs.dialogueTree.selectOption = function(optionIndex) {
|
||||
* @returns {number} The index of the currently selected option
|
||||
*/
|
||||
gdjs.dialogueTree.getSelectedOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
return this.selectedOption;
|
||||
}
|
||||
@@ -374,16 +402,21 @@ gdjs.dialogueTree.hasSelectedOptionChanged = function() {
|
||||
* @param {string} type The type you want to check for ( one of the three above )
|
||||
*/
|
||||
gdjs.dialogueTree.isDialogueLineType = function(type) {
|
||||
if (
|
||||
this.commandCalls &&
|
||||
this.commandCalls.some(function(call) {
|
||||
return gdjs.dialogueTree.clipTextEnd > call.time && call.cmd === 'wait';
|
||||
})
|
||||
) {
|
||||
return !this.pauseScrolling;
|
||||
if (!this.dialogueIsRunning) return false;
|
||||
if (this.commandCalls && type === 'command') {
|
||||
if (
|
||||
this.commandCalls.some(function(call) {
|
||||
return gdjs.dialogueTree.clipTextEnd > call.time && call.cmd === 'wait';
|
||||
})
|
||||
) {
|
||||
return !this.pauseScrolling;
|
||||
}
|
||||
if (this.commandCalls.length > 0 && this.commandParameters.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.dialogueIsRunning ? this.dialogueDataType === type : false;
|
||||
return this.dialogueDataType === type;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -410,19 +443,27 @@ gdjs.dialogueTree.startFrom = function(startDialogueNode) {
|
||||
if (!this.hasDialogueBranch(startDialogueNode)) return;
|
||||
this.optionsCount = 0;
|
||||
this.options = [];
|
||||
this.dialogueBranchTitle = '';
|
||||
this.dialogueBranchBody = '';
|
||||
this.dialogueBranchTags = [];
|
||||
this.tagParameters = [];
|
||||
this.dialogue = this.runner.run(startDialogueNode);
|
||||
this.dialogueData = null;
|
||||
this.dialogueDataType = '';
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.commandCalls = [];
|
||||
this.commandParameters = [];
|
||||
this.pauseScrolling = false;
|
||||
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
this.dialogueBranchTags = this.dialogueData.data.tags;
|
||||
this.dialogueBranchTitle = this.dialogueData.data.title;
|
||||
this.dialogueBranchBody = this.dialogueData.data.body;
|
||||
this.lineNum = this.dialogueData.lineNum;
|
||||
if (gdjs.dialogueTree._isLineTypeText()){
|
||||
this.dialogueDataType = 'text';
|
||||
} else if (gdjs.dialogueTree._isLineTypeOptions()){
|
||||
this.dialogueDataType = 'options';
|
||||
} else {
|
||||
this.dialogueDataType = 'command';
|
||||
};
|
||||
|
||||
this.dialogueIsRunning = true;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
};
|
||||
@@ -452,32 +493,37 @@ gdjs.dialogueTree.goToNextDialogueLine = function() {
|
||||
this.selectedOption = -1;
|
||||
this.selectedOptionUpdated = false;
|
||||
|
||||
if (gdjs.dialogueTree._isLineTypeText()) {
|
||||
if (
|
||||
this.dialogueDataType === 'options' ||
|
||||
this.dialogueDataType === 'text' ||
|
||||
!this.dialogueDataType
|
||||
) {
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('parsing:', this.dialogueData);
|
||||
|
||||
if (!this.dialogueData) {
|
||||
gdjs.dialogueTree.stopRunningDialogue();
|
||||
} else if (gdjs.dialogueTree._isLineTypeText()) {
|
||||
if (this.lineNum === this.dialogueData.lineNum && this.dialogueBranchTitle === this.dialogueData.data.title){
|
||||
this.clipTextEnd = this.dialogueText.length - 1;
|
||||
this.dialogueText +=
|
||||
(this.dialogueText === '' ? '' : ' ') + this.dialogueData.text;
|
||||
} else {
|
||||
this.clipTextEnd = 0;
|
||||
this.dialogueText = this.dialogueData.text;
|
||||
this.commandCalls = [];
|
||||
} else {
|
||||
this.dialogueText += this.dialogueData.text;
|
||||
}
|
||||
|
||||
this.dialogueDataType = 'text';
|
||||
this.dialogueBranchTags = this.dialogueData.data.tags;
|
||||
this.dialogueBranchTitle = this.dialogueData.data.title;
|
||||
this.dialogueBranchBody = this.dialogueData.data.body;
|
||||
this.lineNum = this.dialogueData.lineNum;
|
||||
this.dialogueDataType = 'text';
|
||||
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
} else if (gdjs.dialogueTree._isLineTypeOptions()) {
|
||||
this.commandCalls = [];
|
||||
this.dialogueDataType = 'options';
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.optionsCount = this.dialogueData.options.length;
|
||||
this.options = this.dialogueData.options;
|
||||
this.selectedOptionUpdated = true;
|
||||
} else if (gdjs.dialogueTree._isLineTypeCommand()) {
|
||||
this.dialogueDataType = 'command';
|
||||
|
||||
var command = this.dialogueData.text.split(' ');
|
||||
// If last command was to wait, increase time by one
|
||||
var offsetTime =
|
||||
@@ -495,11 +541,6 @@ gdjs.dialogueTree.goToNextDialogueLine = function() {
|
||||
} else {
|
||||
this.dialogueDataType = 'unknown';
|
||||
}
|
||||
|
||||
if (gdjs.dialogueTree._isLineTypeCommand()) {
|
||||
this.dialogueDataType = 'command';
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -617,7 +658,7 @@ gdjs.dialogueTree.getBranchText = function() {
|
||||
*/
|
||||
gdjs.dialogueTree.getVariable = function(key) {
|
||||
if (this.dialogueIsRunning && key in this.runner.variables.data) {
|
||||
return this.runner.variables.data[key];
|
||||
return this.runner.variables.get(key);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@@ -625,11 +666,11 @@ gdjs.dialogueTree.getVariable = function(key) {
|
||||
/**
|
||||
* Check if a specific variable created by the Dialogue parses exists and is equal to a specific value.
|
||||
* @param {string} key The name of the variable you want to check the value of
|
||||
* @param {string} value The value you want to check against
|
||||
* @param {string|boolean|number} value The value you want to check against
|
||||
*/
|
||||
gdjs.dialogueTree.compareVariable = function(key, value) {
|
||||
if (this.dialogueIsRunning && key in this.runner.variables.data) {
|
||||
return this.runner.variables.data[key].toString() === value;
|
||||
return this.runner.variables.get(key) === value;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -637,11 +678,11 @@ gdjs.dialogueTree.compareVariable = function(key, value) {
|
||||
/**
|
||||
* Set a specific variable created by the Dialogue parser to a specific value.
|
||||
* @param {string} key The name of the variable you want to set the value of
|
||||
* @param {string} value The value you want to set
|
||||
* @param {string|boolean|number} value The value you want to set
|
||||
*/
|
||||
gdjs.dialogueTree.setVariable = function(key, value) {
|
||||
if (this.dialogueIsRunning && this.runner.variables.data) {
|
||||
this.runner.variables.data[key] = value;
|
||||
if (this.runner.variables) {
|
||||
this.runner.variables.set(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -652,7 +693,7 @@ gdjs.dialogueTree.setVariable = function(key, value) {
|
||||
* @param {gdjs.Variable} outputVariable The variable where to store the State
|
||||
*/
|
||||
gdjs.dialogueTree.saveState = function(outputVariable) {
|
||||
const dialogueState = {
|
||||
var dialogueState = {
|
||||
variables: gdjs.dialogueTree.runner.variables.data,
|
||||
visited: gdjs.dialogueTree.runner.visited,
|
||||
};
|
||||
@@ -663,17 +704,32 @@ gdjs.dialogueTree.saveState = function(outputVariable) {
|
||||
* Load the current State of the Dialogue Parser from a specified variable.
|
||||
* Can be used to implement persistence in dialogue through your game's Load/Save function.
|
||||
* That way you can later load all the dialogue choices the player has made.
|
||||
* @param {gdjs.Variable} inputVariable The variable where to load the State from.
|
||||
* @param {gdjs.Variable} inputVariable The structured variable where to load the State from.
|
||||
*/
|
||||
gdjs.dialogueTree.loadState = function(inputVariable) {
|
||||
const jsonData = gdjs.evtTools.network.variableStructureToJSON(inputVariable);
|
||||
var loadedState = JSON.parse(
|
||||
gdjs.evtTools.network.variableStructureToJSON(inputVariable)
|
||||
);
|
||||
if (!loadedState) {
|
||||
console.error('Load state variable is empty:', inputVariable);
|
||||
return
|
||||
}
|
||||
try {
|
||||
const loadedState = JSON.parse(
|
||||
gdjs.evtTools.network.variableStructureToJSON(inputVariable)
|
||||
);
|
||||
gdjs.dialogueTree.runner.visited = loadedState.visited;
|
||||
gdjs.dialogueTree.runner.variables.data = loadedState.variables;
|
||||
gdjs.dialogueTree.runner.variables.data = {};
|
||||
Object.keys(loadedState.variables).forEach(function(key) {
|
||||
var value = loadedState.variables[key];
|
||||
gdjs.dialogueTree.runner.variables.set(key, value);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error('Failed to load state from variable:', inputVariable, e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the current State of the Dialogue Parser.
|
||||
*/
|
||||
gdjs.dialogueTree.clearState = function() {
|
||||
gdjs.dialogueTree.runner.visited = {};
|
||||
gdjs.dialogueTree.runner.variables.data = {};
|
||||
};
|
||||
|
@@ -25,6 +25,11 @@ gdjs.DraggableRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
gdjs.DraggableRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("DraggableBehavior::Draggable", gdjs.DraggableRuntimeBehavior);
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
// Nothing to update.
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.onDeActivate = function() {
|
||||
this._endDrag();
|
||||
};
|
||||
|
@@ -1,17 +1,22 @@
|
||||
|
||||
// @ts-check
|
||||
describe('gdjs.DraggableRuntimeBehavior', function() {
|
||||
var runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}});
|
||||
var runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
resources: {resources: []},
|
||||
// @ts-ignore
|
||||
properties: {windowWidth: 800, windowHeight: 600}
|
||||
});
|
||||
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers:[{name:"", visibility: true}],
|
||||
layers:[{name:"", visibility: true, effects: []}],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: []
|
||||
});
|
||||
|
||||
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{type: "DraggableBehavior::Draggable"}]});
|
||||
var object2 = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{type: "DraggableBehavior::Draggable"}]});
|
||||
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{name: "Behavior1", type: "DraggableBehavior::Draggable"}], variables: []});
|
||||
var object2 = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{name: "Behavior1", type: "DraggableBehavior::Draggable"}], variables: []});
|
||||
runtimeScene.addObject(object);
|
||||
runtimeScene.addObject(object2);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user