From bf925e72401c7e4c6e3008aa760f83df5e7c59ab Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 22 Sep 2023 10:22:09 +0200 Subject: [PATCH 01/14] Added support for closing container without done command as shown by [Jerry](https://discordapp.com/users/jerrytron) adds inky a empty container at the end of the story. Which resultet in unexpected EOF. Now there is a check iff we leave the top level container and are at the end of the story file, the story ends. --- inkcpp/runner_impl.cpp | 11 ++++------- inkcpp_cl/inkcpp_cl.cpp | 12 ++++++++++-- unreal/CMakeLists.txt | 6 +++--- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 02e31147..f6685bd4 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -523,7 +523,7 @@ namespace ink::runtime::internal bool runner_impl::has_tags() const { - return _tags.size() > 0; + return num_tags() > 0; } size_t runner_impl::num_tags() const @@ -1212,7 +1212,7 @@ namespace ink::runtime::internal if (!_threads.empty()) { on_done(false); - return; + break; } else if (_stack.has_frame(&type) && type == frame_type::function) // implicit return is only for functions { @@ -1222,12 +1222,9 @@ namespace ink::runtime::internal // HACK _ptr += sizeof(Command) + sizeof(CommandFlag); execute_return(); + } else if (_container.empty() && _ptr == _story->end()){ + on_done(true); } - /*else TODO I had to remove this to make a test work.... is this important? Have I broken something? - { - on_done(false); // do we need to not set _done here? It wasn't set in the original code #implieddone - return; - }*/ } } break; case Command::VISIT: diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index acdb1c64..f5d55f4b 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -21,6 +21,7 @@ void usage() << "Usage: inkcpp_cl \n" << "\t-o :\tOutput file name\n" << "\t-p []:\tPlay mode\n\toptional snapshot file to load\n\tto create a snapshot file enter '-1' as choice\n" + << "\t--ommit-choice-tags:\tdo not print tags after choices, primarly used to be compatible with inkclecat output" << endl; } @@ -35,7 +36,10 @@ int main(int argc, const char** argv) // Parse options std::string outputFilename; - bool playMode = false, testMode = false, testDirectory = false; + bool playMode = false, + testMode = false, + testDirectory = false, + ommit_choice_tags = false; std::string snapshotFile; for (int i = 1; i < argc - 1; i++) { @@ -52,6 +56,10 @@ int main(int argc, const char** argv) snapshotFile = argv[i]; } } + else if (option == "--ommit-choice-tags") + { + ommit_choice_tags = true; + } else if (option == "-t") testMode = true; else if (option == "-td") @@ -180,7 +188,7 @@ int main(int argc, const char** argv) for (const ink::runtime::choice& c : *thread) { std::cout << index++ << ": " << c.text(); - if(c.has_tags()) { + if(!ommit_choice_tags && c.has_tags()) { std::cout << "\n\t"; for(size_t i = 0; i < c.num_tags(); ++i) { std::cout << "# " << c.get_tag(i) << " "; diff --git a/unreal/CMakeLists.txt b/unreal/CMakeLists.txt index ea0dd498..f5c072e0 100644 --- a/unreal/CMakeLists.txt +++ b/unreal/CMakeLists.txt @@ -22,7 +22,7 @@ set(INKLECATE_CMD "") if(WIN32) FetchContent_MakeAvailable(inklecate_windows) if(NOT inklecate_windows_SOURCE_DIR) - message(WARNING "failed to downloawd inklecate for windows, " + message(WARNING "failed to download inklecate for windows, " "the unreal plugin will be unable use a .ink file as asset directly") else() set(INKLECATE_CMD "Resources/inklecate/windows/inklecate.exe") @@ -30,7 +30,7 @@ if(WIN32) elseif(APPLE) FetchContent_MakeAvailable(inklecate_mac) if(NOT inklecate_mac_SOURCE_DIRE) - message(WARNING "failed to downloawd inklecate for MacOS, " + message(WARNING "failed to download inklecate for MacOS, " "the unreal plugin will be unable use a .ink file as asset directly") else() set(INKLECATE_CMD "Resources/inklecate/mac/inklecate") @@ -38,7 +38,7 @@ elseif(APPLE) elseif(UNIX) FetchContent_MakeAvailable(inklecate_linux) if(NOT inklecate_linux_SOURCE_DIR) - message(WARNING "failed to downloawd inklecate for linux, " + message(WARNING "failed to download inklecate for linux, " "the unreal plugin will be unable use a .ink file as asset directly") else() set(INKLECATE_CMD "Resources/inklecate/linux/inklecate") From 282f3c7a9a08ee18f138c8b9dd666f4ba2559e0d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 22 Sep 2023 10:58:34 +0200 Subject: [PATCH 02/14] Add test for different compiled json Ensures compability for different ink compilers (bonus: use link for inkcpp_test resource file should now also supported on windows) --- inkcpp_test/CMakeLists.txt | 4 +- inkcpp_test/InkyJson.cpp | 33 ++++ inkcpp_test/ink/simple-1.1.1-inklecate.json | 154 +++++++++++++++++++ inkcpp_test/ink/simple-1.1.1-inky.json | 160 ++++++++++++++++++++ 4 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 inkcpp_test/InkyJson.cpp create mode 100644 inkcpp_test/ink/simple-1.1.1-inklecate.json create mode 100644 inkcpp_test/ink/simple-1.1.1-inky.json diff --git a/inkcpp_test/CMakeLists.txt b/inkcpp_test/CMakeLists.txt index f046c378..7ffc8aff 100644 --- a/inkcpp_test/CMakeLists.txt +++ b/inkcpp_test/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(inkcpp_test catch.hpp Main.cpp FallbackFunction.cpp LabelCondition.cpp Observer.cpp + InkyJson.cpp ) target_link_libraries(inkcpp_test PUBLIC inkcpp inkcpp_compiler inkcpp_shared) @@ -29,11 +30,12 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") endif() add_test(NAME UnitTests COMMAND $) + set (source "${CMAKE_CURRENT_SOURCE_DIR}/ink") set (destination "${CMAKE_CURRENT_BINARY_DIR}/ink") add_custom_command( TARGET inkcpp_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${source} ${destination} + COMMAND ${CMAKE_COMMAND} -E create_symlink ${source} ${destination} DEPENDS ${destination} COMMENT "symbolic link resources folder from ${source} => ${destination}" ) diff --git a/inkcpp_test/InkyJson.cpp b/inkcpp_test/InkyJson.cpp new file mode 100644 index 00000000..2a39af88 --- /dev/null +++ b/inkcpp_test/InkyJson.cpp @@ -0,0 +1,33 @@ +#include "catch.hpp" +// #include "../inkcpp_cl/test.cpp" + +#include +#include +#include +#include + +using namespace ink::runtime; + +static constexpr const char* OUTPUT_PART_1 = "Once upon a time...\n"; +static constexpr const char* OUTPUT_PART_2 = "There were two choices.\nThey lived happily ever after.\n"; +static constexpr size_t CHOICE = 0; + +SCENARIO("run inklecate 1.1.1 story") +{ + auto compiler = GENERATE("inklecate", "inky"); + GIVEN(compiler) + { + auto input_file = std::string("ink/simple-1.1.1-") + compiler + ".json"; + ink::compiler::run(input_file.c_str(), "simple.bin"); + auto ink = story::from_file("simple.bin"); + runner thread = ink->new_runner(); + + THEN("Expect normal output") { + REQUIRE(thread->getall() == OUTPUT_PART_1); + REQUIRE(thread->has_choices()); + REQUIRE(thread->num_choices() == 2); + thread->choose(CHOICE); + REQUIRE(thread->getall() == OUTPUT_PART_2); + } + } +} diff --git a/inkcpp_test/ink/simple-1.1.1-inklecate.json b/inkcpp_test/ink/simple-1.1.1-inklecate.json new file mode 100644 index 00000000..e49985a8 --- /dev/null +++ b/inkcpp_test/ink/simple-1.1.1-inklecate.json @@ -0,0 +1,154 @@ +{ + "inkVersion": 21, + "root": [ + [ + { + "->": "start" + }, + [ + "done", + { + "#n": "g-0" + } + ], + null + ], + "done", + { + "start": [ + [ + "^Once upon a time...", + "\n", + [ + "ev", + { + "^->": "start.0.2.$r1" + }, + { + "temp=": "$r" + }, + "str", + { + "->": ".^.s" + }, + [ + { + "#n": "$r1" + } + ], + "/str", + "/ev", + { + "*": ".^.^.c-0", + "flg": 18 + }, + { + "s": [ + "^There were two choices.", + { + "->": "$r", + "var": true + }, + null + ] + } + ], + [ + "ev", + { + "^->": "start.0.3.$r1" + }, + { + "temp=": "$r" + }, + "str", + { + "->": ".^.s" + }, + [ + { + "#n": "$r1" + } + ], + "/str", + "/ev", + { + "*": ".^.^.c-1", + "flg": 18 + }, + { + "s": [ + "^There were four lines of content.", + { + "->": "$r", + "var": true + }, + null + ] + } + ], + { + "c-0": [ + "ev", + { + "^->": "start.0.c-0.$r2" + }, + "/ev", + { + "temp=": "$r" + }, + { + "->": ".^.^.2.s" + }, + [ + { + "#n": "$r2" + } + ], + "\n", + { + "->": ".^.^.g-0" + }, + { + "#f": 5 + } + ], + "c-1": [ + "ev", + { + "^->": "start.0.c-1.$r2" + }, + "/ev", + { + "temp=": "$r" + }, + { + "->": ".^.^.3.s" + }, + [ + { + "#n": "$r2" + } + ], + "\n", + { + "->": ".^.^.g-0" + }, + { + "#f": 5 + } + ], + "g-0": [ + "^They lived happily ever after.", + "\n", + "end", + null + ] + } + ], + null + ] + } + ], + "listDefs": {} +} diff --git a/inkcpp_test/ink/simple-1.1.1-inky.json b/inkcpp_test/ink/simple-1.1.1-inky.json new file mode 100644 index 00000000..7677a825 --- /dev/null +++ b/inkcpp_test/ink/simple-1.1.1-inky.json @@ -0,0 +1,160 @@ +{ + "inkVersion": 21, + "root": [ + [ + { + "->": "start" + }, + [ + "done", + { + "#f": 5, + "#n": "g-0" + } + ], + null + ], + "done", + { + "start": [ + [ + "^Once upon a time...", + "\n", + [ + "ev", + { + "^->": "start.0.2.$r1" + }, + { + "temp=": "$r" + }, + "str", + { + "->": ".^.s" + }, + [ + { + "#n": "$r1" + } + ], + "/str", + "/ev", + { + "*": ".^.^.c-0", + "flg": 18 + }, + { + "s": [ + "^There were two choices.", + { + "->": "$r", + "var": true + }, + null + ] + } + ], + [ + "ev", + { + "^->": "start.0.3.$r1" + }, + { + "temp=": "$r" + }, + "str", + { + "->": ".^.s" + }, + [ + { + "#n": "$r1" + } + ], + "/str", + "/ev", + { + "*": ".^.^.c-1", + "flg": 18 + }, + { + "s": [ + "^There were four lines of content.", + { + "->": "$r", + "var": true + }, + null + ] + } + ], + { + "c-0": [ + "ev", + { + "^->": "start.0.c-0.$r2" + }, + "/ev", + { + "temp=": "$r" + }, + { + "->": ".^.^.2.s" + }, + [ + { + "#n": "$r2" + } + ], + "\n", + { + "->": ".^.^.g-0" + }, + { + "#f": 5 + } + ], + "c-1": [ + "ev", + { + "^->": "start.0.c-1.$r2" + }, + "/ev", + { + "temp=": "$r" + }, + { + "->": ".^.^.3.s" + }, + [ + { + "#n": "$r2" + } + ], + "\n", + { + "->": ".^.^.g-0" + }, + { + "#f": 5 + } + ], + "g-0": [ + "^They lived happily ever after.", + "\n", + "end", + { + "#f": 5 + } + ] + } + ], + { + "#f": 1 + } + ], + "#f": 1 + } + ], + "listDefs": {} +} From dc82a977977c5828ee2a95db245a7162a1ec7f1d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 24 Sep 2023 15:20:48 +0200 Subject: [PATCH 03/14] Fixe random numeric operation --- inkcpp/numeric_operations.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index 3a1068a1..8d909790 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -427,7 +427,7 @@ namespace ink::runtime::internal { using operation_base::operation_base; void operator()(basic_eval_stack& stack, value* vals) { int min = casting::numeric_cast(vals[0]); - int max = casting::numeric_cast(vals[0]); + int max = casting::numeric_cast(vals[1]); stack.push(value{}.set(static_cast(_prng.rand(max - min + 1) + min))); } }; From 1a623a41b7706a7ad8428299222c47021e9fde2a Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 25 Sep 2023 12:22:12 +0200 Subject: [PATCH 04/14] visit START container if jumping to container start --- inkcpp/runner_impl.cpp | 200 ++++++++++++++++++------------ inkcpp_compiler/json_compiler.cpp | 22 ++-- 2 files changed, 132 insertions(+), 90 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index f6685bd4..aee8baf7 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -6,6 +6,7 @@ #include "header.h" #include "string_utils.h" #include "snapshot_impl.h" +#include "system.h" #include "value.h" namespace ink::runtime @@ -170,6 +171,42 @@ namespace ink::runtime::internal void runner_impl::jump(ip_t dest, bool record_visits) { + if (dest == _ptr) { return; }const uint32_t* iter = nullptr; + container_t container_id; + ip_t offset; + bool reversed = _ptr > dest; + + while(_story->iterate_containers(iter, container_id, offset, reversed)) { + if ((offset == _ptr) || (reversed && offset < _ptr) || (!reversed && offset > _ptr)) { break; } + } + if (!reversed && offset == _ptr) { // If we going reversed, we will stay in front of the command, else execute it + if (!_container.empty() && _container.top() == container_id) { + _container.pop(); + } else { + _container.push(container_id); + } + } if (offset != _ptr) { _story->iterate_containers(iter, container_id, offset, !reversed); } + while(_story->iterate_containers(iter, container_id, offset, reversed)) { + if((offset == dest) || (reversed && offset < dest) || (!reversed && offset > dest)) { break; } + if(!_container.empty() && _container.top() == container_id) { + _container.pop(); + } else { + _container.push(container_id); + } + } + if (reversed && offset == dest) { // if we going reversed the command must be reversed, because we will it execute soon + if(!_container.empty() && _container.top() == container_id) { + _container.pop(); + } else { + _container.push(container_id); + } + } + + _ptr = dest; + + + + // Optimization: if we are _is_falling, then we can // _should be_ able to safely assume that there is nothing to do here. A falling // divert should only be taking us from a container to that same container's end point @@ -181,87 +218,87 @@ namespace ink::runtime::internal }*/ // Check which direction we are jumping - bool reverse = dest < _ptr; + // bool reverse = dest < _ptr; // iteration - const uint32_t* iter = nullptr; - container_t container_id; - ip_t offset; - bool inBound = false; - - // Iterate until we find the container marker just before our own - while (_story->iterate_containers(iter, container_id, offset, reverse)) { - if (( !reverse && offset > _ptr ) - || ( reverse && offset < _ptr )) { - - // Step back once in the iteration and break - inBound = true; - _story->iterate_containers(iter, container_id, offset, !reverse); - break; - } - } - - size_t pos = _container.size(); - - bool first = true; - // Start moving forward (or backwards) - if(inBound && (offset == nullptr || (!reverse&&offset<=dest) || (reverse&&offset>dest)) ) - while (_story->iterate_containers(iter, container_id, offset, reverse)) - { - // Break when we've past the destination - if ((!reverse && offset > dest) || (reverse && offset <= dest)) { - // jump back to start of same container - if(first && reverse && offset == dest - && _container.top() == container_id) { - // check if it was start flag - auto con_id = container_id; - _story->iterate_containers(iter, container_id, offset, true); - if(offset == nullptr || con_id == container_id) - { - _globals->visit(container_id); - } - } - break; - } - first = false; - - // Two cases: - - // (1) Container iterator has the same value as the top of the stack. - // This means that this is an end marker for the container we're in - if (!_container.empty() && _container.top() == container_id) - { - if (_container.size() == pos) - pos--; - - // Get out of that container - _container.pop(); - } - - // (2) This must be the entrance marker for a new container. Enter it - else - { - // Push it - _container.push(container_id); - } - } - - // Iterate over the container stack marking any _new_ entries as "visited" - if (record_visits) - { - const container_t* con_iter; - size_t num_new = _container.size() - pos; - while (_container.iter(con_iter)) - { - if (num_new <= 0) - break; - _globals->visit(*con_iter); - --num_new; - } - } - - // Jump - _ptr = dest; + // const uint32_t* iter = nullptr; + // container_t container_id; + // ip_t offset; + // bool inBound = false; + + // // Iterate until we find the container marker just before our own + // while (_story->iterate_containers(iter, container_id, offset, reverse)) { + // if (( !reverse && offset > _ptr ) + // || ( reverse && offset < _ptr )) { + + // // Step back once in the iteration and break + // inBound = true; + // _story->iterate_containers(iter, container_id, offset, !reverse); + // break; + // } + // } + + // size_t pos = _container.size(); + + // bool first = true; + // // Start moving forward (or backwards) + // if(inBound && (offset == nullptr || (!reverse&&offset<=dest) || (reverse&&offset>dest)) ) + // while (_story->iterate_containers(iter, container_id, offset, reverse)) + // { + // // Break when we've past the destination + // if ((!reverse && offset > dest) || (reverse && offset <= dest)) { + // // jump back to start of same container + // if(first && reverse && offset == dest + // && _container.top() == container_id) { + // // check if it was start flag + // auto con_id = container_id; + // _story->iterate_containers(iter, container_id, offset, true); + // if(offset == nullptr || con_id == container_id) + // { + // _globals->visit(container_id); + // } + // } + // break; + // } + // first = false; + + // // Two cases: + + // // (1) Container iterator has the same value as the top of the stack. + // // This means that this is an end marker for the container we're in + // if (!_container.empty() && _container.top() == container_id) + // { + // if (_container.size() == pos) + // pos--; + + // // Get out of that container + // _container.pop(); + // } + + // // (2) This must be the entrance marker for a new container. Enter it + // else + // { + // // Push it + // _container.push(container_id); + // } + // } + + // // Iterate over the container stack marking any _new_ entries as "visited" + // if (record_visits) + // { + // const container_t* con_iter; + // size_t num_new = _container.size() - pos; + // while (_container.iter(con_iter)) + // { + // if (num_new <= 0) + // break; + // _globals->visit(*con_iter); + // --num_new; + // } + // } + + // // Jump + // _ptr = dest; } template void runner_impl::start_frame(uint32_t target) { @@ -1185,7 +1222,8 @@ namespace ink::runtime::internal case Command::START_CONTAINER_MARKER: { // Keep track of current container - _container.push(read()); + auto index = read(); + _container.push(index); // Increment visit count if (flag & CommandFlag::CONTAINER_MARKER_TRACK_VISITS) @@ -1223,7 +1261,7 @@ namespace ink::runtime::internal _ptr += sizeof(Command) + sizeof(CommandFlag); execute_return(); } else if (_container.empty() && _ptr == _story->end()){ - on_done(true); + on_done(false); } } } break; diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index d1bed8c7..4badaa21 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -1,5 +1,6 @@ #include "json_compiler.h" +#include "command.h" #include "list_data.h" #include "system.h" #include "version.h" @@ -52,6 +53,7 @@ namespace ink::compiler::internal container_t indexToReturn = ~0; bool recordInContainerMap = false; vector deferred; + CommandFlag cmd_flags = CommandFlag::NO_FLAGS; }; void json_compiler::handle_container_metadata( @@ -91,14 +93,12 @@ namespace ink::compiler::internal container_t myIndex = _next_container_index++; // Make appropriate flags - CommandFlag cmd_flags = CommandFlag::NO_FLAGS; + data.cmd_flags = CommandFlag::NO_FLAGS; if (visits) - cmd_flags |= CommandFlag::CONTAINER_MARKER_TRACK_VISITS; + data.cmd_flags |= CommandFlag::CONTAINER_MARKER_TRACK_VISITS; if (turns) - cmd_flags |= CommandFlag::CONTAINER_MARKER_TRACK_TURNS; + data.cmd_flags |= CommandFlag::CONTAINER_MARKER_TRACK_TURNS; - // Write command out at this position - _emitter->write(Command::START_CONTAINER_MARKER, myIndex, cmd_flags); data.indexToReturn = myIndex; @@ -128,6 +128,10 @@ namespace ink::compiler::internal // tell the emitter we're beginning a new container uint32_t position = _emitter->start_container(index_in_parent, name_override.empty() ? meta.name : name_override); + // Write command out at this position + if(meta.cmd_flags != CommandFlag::NO_FLAGS) { + _emitter->write(Command::START_CONTAINER_MARKER, meta.indexToReturn, meta.cmd_flags); + } if(meta.recordInContainerMap) { _emitter->add_start_to_container_map(position, meta.indexToReturn); } @@ -218,13 +222,13 @@ namespace ink::compiler::internal _emitter->patch_fallthroughs(offset); } - // Write end container marker - if (meta.indexToReturn != ~0) - _emitter->write(Command::END_CONTAINER_MARKER, meta.indexToReturn); - // End container uint32_t end_position = _emitter->end_container(); + // Write end container marker, End pointer should point to End command (form symetry with START command) + if (meta.indexToReturn != ~0) + _emitter->write(Command::END_CONTAINER_MARKER, meta.indexToReturn); + // Record end position in map if (meta.recordInContainerMap) _emitter->add_end_to_container_map(end_position, meta.indexToReturn); From f860a3911262a187d0c13180fd443aeb17a0e95c Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 26 Sep 2023 12:03:14 +0200 Subject: [PATCH 05/14] No working? --- inkcpp/globals_impl.cpp | 8 +- inkcpp/globals_impl.h | 2 +- inkcpp/runner_impl.cpp | 115 ++++++++++++++++++++++------- inkcpp/story_impl.cpp | 13 ++++ inkcpp/story_impl.h | 2 + inkcpp_compiler/binary_emitter.cpp | 12 +++ inkcpp_compiler/json_compiler.cpp | 4 + shared/private/command.h | 1 + 8 files changed, 128 insertions(+), 29 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 47037d27..a0b0eb4c 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -39,10 +39,12 @@ namespace ink::runtime::internal } } - void globals_impl::visit(uint32_t container_id) + void globals_impl::visit(uint32_t container_id, bool entering_at_start) { - _visit_counts[container_id].visits += 1; - _visit_counts[container_id].turns = 0; + if((!(_owner->container_flag(container_id) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST)) || entering_at_start) { + _visit_counts[container_id].visits += 1; + _visit_counts[container_id].turns = 0; + } } uint32_t globals_impl::visits(uint32_t container_id) const diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index d6d4fc1c..5597063d 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -37,7 +37,7 @@ namespace ink::runtime::internal public: // Records a visit to a container - void visit(uint32_t container_id); + void visit(uint32_t container_id, bool entering_at_start); // Checks the number of visits to a container uint32_t visits(uint32_t container_id) const; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index aee8baf7..82989695 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -9,6 +9,9 @@ #include "system.h" #include "value.h" +#include +#include + namespace ink::runtime { const choice* runner_interface::get_choice(size_t index) const @@ -171,39 +174,101 @@ namespace ink::runtime::internal void runner_impl::jump(ip_t dest, bool record_visits) { - if (dest == _ptr) { return; }const uint32_t* iter = nullptr; - container_t container_id; + std::cout << "jump to: " << (dest - _story->instructions()) << std::endl; + if (dest == _ptr) { return; } + std::vector> stack; + ip_t offset; - bool reversed = _ptr > dest; + const uint32_t* iter = nullptr; + container_t id; + + while(_story->iterate_containers(iter, id, offset)) { + if(offset >= dest) { break; } + if (stack.empty() || std::get(stack.back()) != id) { + inkAssert(static_cast(offset[0]) == Command::START_CONTAINER_MARKER, "Expected start Container"); + stack.push_back({static_cast(offset[1]), id, offset}); + } else { + stack.pop_back(); + } + } + + _ptr = dest; - while(_story->iterate_containers(iter, container_id, offset, reversed)) { - if ((offset == _ptr) || (reversed && offset < _ptr) || (!reversed && offset > _ptr)) { break; } + if (offset == dest) { + _ptr += 6; + stack.push_back({static_cast(offset[1]), id, offset}); } - if (!reversed && offset == _ptr) { // If we going reversed, we will stay in front of the command, else execute it - if (!_container.empty() && _container.top() == container_id) { - _container.pop(); + + bool allEnteredAtStart = true; + auto stack_iter= stack.rbegin(); + auto is_in = [this](container_t id)-> bool{ + const container_t* iter; + while(this->_container.iter(iter)) { + if(*iter == id) { return true; } + } + return false; + }; + ip_t curr = dest; + { + const container_t* iter; + std::cout << "old: "; + while(_container.iter(iter)) { + std::cout << (*iter ) << ", "; + } + std::cout << std::endl; + } + { + std::cout << "new: "; + for(auto iter = stack.rbegin(); iter != stack.rend(); ++iter) { + std::cout << std::get(*iter) << ", "; + } + std::cout << std::endl; + } + while(stack_iter != stack.rend()&& + (!is_in(std::get(*stack_iter)) || std::get(*stack_iter) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST )) + { + auto offset = std::get(*stack_iter); + bool enteringStart = allEnteredAtStart && ((curr - offset) <= 6); + if(!enteringStart) { allEnteredAtStart = false; } + std::cout << "visit: " << std::get(*stack_iter) << "\td: " << (curr - offset) << std::endl; + curr = offset; + _globals->visit(std::get(*stack_iter), enteringStart); + ++stack_iter; + } + + stack_iter = stack.rbegin(); + if(!stack.empty() && !is_in(std::get(*stack_iter))) { + while(stack_iter != stack.rend() && !is_in(std::get(*stack_iter))) { + ++stack_iter; + } + if(stack_iter != stack.rend()) { + while(!_container.empty() && _container.top() != std::get(*stack_iter)) { + _container.pop(); + } } else { - _container.push(container_id); + while(!_container.empty()) { + _container.pop(); + } } - } if (offset != _ptr) { _story->iterate_containers(iter, container_id, offset, !reversed); } - while(_story->iterate_containers(iter, container_id, offset, reversed)) { - if((offset == dest) || (reversed && offset < dest) || (!reversed && offset > dest)) { break; } - if(!_container.empty() && _container.top() == container_id) { + --stack_iter; + while(stack_iter != stack.rbegin()) { + _container.push(std::get(*stack_iter)); + --stack_iter; + } + _container.push(std::get(*stack_iter)); + } else if(stack.empty()) { + while(!_container.empty()) { _container.pop(); - } else { - _container.push(container_id); } } - if (reversed && offset == dest) { // if we going reversed the command must be reversed, because we will it execute soon - if(!_container.empty() && _container.top() == container_id) { - _container.pop(); - } else { - _container.push(container_id); + { + const container_t* iter; + std::cout << "cur: "; + while(_container.iter(iter)) { + std::cout << (*iter ) << ", "; } + std::cout << std::endl; } - - _ptr = dest; - @@ -546,7 +611,6 @@ namespace ink::runtime::internal // Jump to destination and clear choice list jump(_story->instructions() + c.path(), false); - if(!_container.empty()){ _globals->visit(_container.top()); } clear_choices(); clear_tags(); } @@ -1223,12 +1287,13 @@ namespace ink::runtime::internal { // Keep track of current container auto index = read(); + std::cout << "START: " << index << std::endl; _container.push(index); // Increment visit count if (flag & CommandFlag::CONTAINER_MARKER_TRACK_VISITS) { - _globals->visit(_container.top()); + _globals->visit(_container.top(), true); } // TODO Turn counts diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index c7a1a6a6..3ac6f334 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -154,6 +154,19 @@ namespace ink::runtime::internal return false; } + + CommandFlag story_impl::container_flag(container_t id) const { + const uint32_t* iter = nullptr; + ip_t offset; + container_t c_id; + while(iterate_containers(iter, c_id, offset)) { + if (c_id == id) { + inkAssert(static_cast(offset[0]) == Command::START_CONTAINER_MARKER); + return static_cast(offset[1]); + } + } + } + ip_t story_impl::find_offset_for(hash_t path) const { hash_t* iter = _container_hash_start; diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index 2bfa322e..7858d2cc 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -2,6 +2,7 @@ #include #include +#include "command.h" #include "types.h" #include "story.h" #include "header.h" @@ -35,6 +36,7 @@ namespace ink::runtime::internal bool iterate_containers(const uint32_t*& iterator, container_t& index, ip_t& offset, bool reverse = false) const; bool get_container_id(ip_t offset, container_t& container_id) const; + CommandFlag container_flag(container_t id) const; ip_t find_offset_for(hash_t path) const; diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index 05c86371..df214c4e 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -4,6 +4,8 @@ #include "version.h" #include "list_data.h" +#include +#include #include #include #include @@ -286,6 +288,16 @@ namespace ink::compiler::internal void binary_emitter::process_paths() { + std::function draw; + draw = [&draw](const container_data* node, int depth){ + for(int i = 0; i < depth; ++i) { std::cout << "\t"; } + std::cout << (node->counter_index < 1000 ? node->counter_index : -1) << ": " << node->offset; + std::cout << std::endl; + for(auto& child : node->children) { + draw(child, depth + 1); + } + }; + draw(_root, 0); for (auto pair : _paths) { // We need to replace the uint32_t at this location with the byte position of the requested container diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index 4badaa21..7b8f7e7e 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -98,6 +98,8 @@ namespace ink::compiler::internal data.cmd_flags |= CommandFlag::CONTAINER_MARKER_TRACK_VISITS; if (turns) data.cmd_flags |= CommandFlag::CONTAINER_MARKER_TRACK_TURNS; + if (onlyFirst) + data.cmd_flags |= CommandFlag::CONTAINER_MARKER_ONLY_FIRST; data.indexToReturn = myIndex; @@ -131,6 +133,8 @@ namespace ink::compiler::internal // Write command out at this position if(meta.cmd_flags != CommandFlag::NO_FLAGS) { _emitter->write(Command::START_CONTAINER_MARKER, meta.indexToReturn, meta.cmd_flags); + } else { + _emitter->write_raw(Command::VOID); } if(meta.recordInContainerMap) { _emitter->add_start_to_container_map(position, meta.indexToReturn); diff --git a/shared/private/command.h b/shared/private/command.h index ac266791..354b678d 100644 --- a/shared/private/command.h +++ b/shared/private/command.h @@ -135,6 +135,7 @@ namespace ink // == Container marker CONTAINER_MARKER_TRACK_VISITS = 1 << 0, CONTAINER_MARKER_TRACK_TURNS = 1 << 1, + CONTAINER_MARKER_ONLY_FIRST = 1 << 2, // == Variable assignment ASSIGNMENT_IS_REDEFINE = 1 << 0, From 84d35f9df7c9c8b77b61798c11570fdf0170f517 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 26 Sep 2023 13:03:19 +0200 Subject: [PATCH 06/14] Add truthy evaluation --- inkcpp/runner_impl.cpp | 11 ++++-- inkcpp/story_impl.cpp | 1 + inkcpp/value.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++ inkcpp/value.h | 2 + 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 82989695..4e7f426c 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -974,7 +974,7 @@ namespace ink::runtime::internal uint32_t target = read(); // Check for condition - if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().get()) + if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().truthy()) break; // SPECIAL: Fallthrough divert. We're starting to fall out of containers @@ -1018,7 +1018,7 @@ namespace ink::runtime::internal hash_t variable = read(); // Check for condition - if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().get()) + if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().truthy()) break; const value* val = get_var(variable); @@ -1235,6 +1235,7 @@ namespace ink::runtime::internal container_t destination = -1; if (_story->get_container_id(_story->instructions() + path, destination)) { + std::cout << "Once only: " << _globals->visits(destination) << std::endl; // Ignore the choice if we've visited the destination before if (_globals->visits(destination) > 0) break; @@ -1248,7 +1249,10 @@ namespace ink::runtime::internal // Choice is conditional if (flag & CommandFlag::CHOICE_HAS_CONDITION) { // Only show if the top of the eval stack is 'truthy' - if (!_eval.pop().get()) + auto top = _eval.pop(); + bool cond = top.truthy(); + std::cout << (int)top.type() << " Condiditon: " << cond << std::endl; + if(!cond) break; } @@ -1361,6 +1365,7 @@ namespace ink::runtime::internal container_t container = read(); // Push the read count for the requested container index + std::cout << "read count from: " << container << " = " <<_globals->visits(container) << std::endl; _eval.push(value{}.set((int)_globals->visits(container))); } break; case Command::TAG: diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 3ac6f334..33a6e84b 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -165,6 +165,7 @@ namespace ink::runtime::internal return static_cast(offset[1]); } } + inkAssert("Container not found -> can't fetch flag"); } ip_t story_impl::find_offset_for(hash_t path) const diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 3a8b9af5..6f72faa7 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -5,9 +5,98 @@ #include "list_table.h" #include "string_utils.h" #include "string_table.h" +#include "system.h" namespace ink::runtime::internal { + + template + bool truthy_impl(const value& v); + + template<> + bool truthy_impl(const value& v) { + inkAssert("Type was not found in operational types or it has no conversion to boolean"); + } + + template<> + bool truthy_impl(const value& v) { + if(v.type() == value_type::string) { + // if string is not empty + return *v.get().str != 0; + } else { + return truthy_impl(v); + } + } + + template<> + bool truthy_impl(const value& v) { + if(v.type() == value_type::list_flag) { + auto flag = v.get(); + return flag != null_flag && flag != empty_flag; + } else { + return truthy_impl(v); + } + } + + template<> + bool truthy_impl(const value& v) { + if(v.type() == value_type::list) { + inkAssert("Curently not supported"); + } else { + return truthy_impl(v); + } + } + + template<> + bool truthy_impl(const value& v) { + if (v.type() == value_type::float32) { + return v.get() != 0.0f; + } else { + return truthy_impl(v); + } + } + template<> + bool truthy_impl(const value& v) { + if(v.type() == value_type::int32) { + return v.get() != 0; + } else { + return truthy_impl(v); + } + } + + template<> + bool truthy_impl(const value& v) { + if (v.type() == value_type::uint32) { + return v.get() != 0; + } else { + return truthy_impl(v); + } + } + + template<> + bool truthy_impl(const value& v) { + if(v.type() == value_type::boolean) { + return v.get(); + } else { + return truthy_impl(v); + } + } + + template<> + bool truthy_impl(const value& v) { + if (v.type() == value_type::divert) { + inkAssert("Divert can not be evaluated to boolean"); + } else { + return truthy_impl(v); + } + } + + bool value::truthy() const { + return truthy_impl(*this); + } + + + #ifdef INK_ENABLE_STL template void append(std::ostream& os, const value& val, const list_table* lists) { diff --git a/inkcpp/value.h b/inkcpp/value.h index 2e215296..3bdb3da2 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -106,6 +106,8 @@ namespace ink::runtime::internal { typename ret::type get() const { static_assert(ty != ty, "No getter for this type defined!"); } + /// check if value evaluates to true + bool truthy() const; /// set value of type (if possible) template constexpr value& set(Args ...args) { From 4688d0e808b185a3f5b98d9124211fd5ef4fe729 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 26 Sep 2023 13:23:20 +0200 Subject: [PATCH 07/14] Add correct function handling? --- inkcpp/runner_impl.cpp | 13 +++++++------ inkcpp/runner_impl.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 4e7f426c..33bbff96 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -224,6 +224,7 @@ namespace ink::runtime::internal } std::cout << std::endl; } + if(record_visits) { while(stack_iter != stack.rend()&& (!is_in(std::get(*stack_iter)) || std::get(*stack_iter) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST )) { @@ -234,7 +235,7 @@ namespace ink::runtime::internal curr = offset; _globals->visit(std::get(*stack_iter), enteringStart); ++stack_iter; - } + }} stack_iter = stack.rbegin(); if(!stack.empty() && !is_in(std::get(*stack_iter))) { @@ -381,7 +382,7 @@ namespace ink::runtime::internal // Do the jump inkAssert(_story->instructions() + target < _story->end(), "Diverting past end of story data!"); - jump(_story->instructions() + target); + jump(_story->instructions() + target, true); } frame_type runner_impl::execute_return() @@ -610,7 +611,7 @@ namespace ink::runtime::internal _threads.clear(); // Jump to destination and clear choice list - jump(_story->instructions() + c.path(), false); + jump(_story->instructions() + c.path(), true); clear_choices(); clear_tags(); } @@ -764,7 +765,7 @@ namespace ink::runtime::internal // Clear state and move to destination reset(); _ptr = _story->instructions(); - jump(destination); + jump(destination, false); return true; } @@ -1009,7 +1010,7 @@ namespace ink::runtime::internal // Do the jump inkAssert(_story->instructions() + target < _story->end(), "Diverting past end of story data!"); - jump(_story->instructions() + target); + jump(_story->instructions() + target, true); } break; case Command::DIVERT_TO_VARIABLE: @@ -1025,7 +1026,7 @@ namespace ink::runtime::internal inkAssert(val, "Jump destiniation needs to be defined!"); // Move to location - jump(_story->instructions() + val->get()); + jump(_story->instructions() + val->get(), true); inkAssert(_ptr < _story->end(), "Diverted past end of story data!"); } break; diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 590d4945..f58c8100 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -142,7 +142,7 @@ namespace ink::runtime::internal void clear_tags(); // Special code for jumping from the current IP to another - void jump(ip_t, bool record_visits = true); + void jump(ip_t, bool record_visits); void run_binary_operator(unsigned char cmd); void run_unary_operator(unsigned char cmd); From 310e28c8a8db02cc2da6faf95f74c1b4f3219ffe Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 26 Sep 2023 14:14:46 +0200 Subject: [PATCH 08/14] Add TURNS() support --- inkcpp/globals_impl.cpp | 6 +++++ inkcpp/globals_impl.h | 3 +++ inkcpp/runner_impl.cpp | 36 +++++++++++++++++------------- inkcpp_compiler/binary_emitter.cpp | 22 +++++++++--------- inkcpp_compiler/command.cpp | 1 + shared/private/command.h | 2 ++ 6 files changed, 44 insertions(+), 26 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index a0b0eb4c..11c65a79 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -11,6 +11,7 @@ namespace ink::runtime::internal { globals_impl::globals_impl(const story_impl* story) : _num_containers(story->num_containers()) + , _turn_cnt{0} , _visit_counts() , _owner(story) , _runners_start(nullptr) @@ -52,8 +53,13 @@ namespace ink::runtime::internal return _visit_counts[container_id].visits; } + uint32_t globals_impl::turns() const { + return _turn_cnt; + } + void globals_impl::turn() { + ++_turn_cnt; for(size_t i = 0; i < _visit_counts.size(); ++i) { if(_visit_counts[i].turns != -1) { diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index 5597063d..2c28fe14 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -41,6 +41,8 @@ namespace ink::runtime::internal // Checks the number of visits to a container uint32_t visits(uint32_t container_id) const; + // Number of current turn (number of passed choices) + uint32_t turns() const; // Returnn number of turns since container was last visited // \retval -1 if container was never visited before @@ -85,6 +87,7 @@ namespace ink::runtime::internal // Store the number of containers. This is the length of most of our lists const uint32_t _num_containers; + uint32_t _turn_cnt = 0; // Visit count array struct visit_count { uint32_t visits = 0; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 33bbff96..ffa14447 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -174,7 +174,7 @@ namespace ink::runtime::internal void runner_impl::jump(ip_t dest, bool record_visits) { - std::cout << "jump to: " << (dest - _story->instructions()) << std::endl; + // std::cout << "jump to: " << (dest - _story->instructions()) << std::endl; if (dest == _ptr) { return; } std::vector> stack; @@ -194,7 +194,7 @@ namespace ink::runtime::internal _ptr = dest; - if (offset == dest) { + if (offset == dest && static_cast(offset[0]) == Command::START_CONTAINER_MARKER) { _ptr += 6; stack.push_back({static_cast(offset[1]), id, offset}); } @@ -211,18 +211,18 @@ namespace ink::runtime::internal ip_t curr = dest; { const container_t* iter; - std::cout << "old: "; + // std::cout << "old: "; while(_container.iter(iter)) { - std::cout << (*iter ) << ", "; + // std::cout << (*iter ) << ", "; } - std::cout << std::endl; + // std::cout << std::endl; } { - std::cout << "new: "; + // std::cout << "new: "; for(auto iter = stack.rbegin(); iter != stack.rend(); ++iter) { - std::cout << std::get(*iter) << ", "; + // std::cout << std::get(*iter) << ", "; } - std::cout << std::endl; + // std::cout << std::endl; } if(record_visits) { while(stack_iter != stack.rend()&& @@ -231,7 +231,7 @@ namespace ink::runtime::internal auto offset = std::get(*stack_iter); bool enteringStart = allEnteredAtStart && ((curr - offset) <= 6); if(!enteringStart) { allEnteredAtStart = false; } - std::cout << "visit: " << std::get(*stack_iter) << "\td: " << (curr - offset) << std::endl; + // std::cout << "visit: " << std::get(*stack_iter) << "\td: " << (curr - offset) << std::endl; curr = offset; _globals->visit(std::get(*stack_iter), enteringStart); ++stack_iter; @@ -264,11 +264,11 @@ namespace ink::runtime::internal } { const container_t* iter; - std::cout << "cur: "; + // std::cout << "cur: "; while(_container.iter(iter)) { - std::cout << (*iter ) << ", "; + // std::cout << (*iter ) << ", "; } - std::cout << std::endl; + // std::cout << std::endl; } @@ -1236,7 +1236,7 @@ namespace ink::runtime::internal container_t destination = -1; if (_story->get_container_id(_story->instructions() + path, destination)) { - std::cout << "Once only: " << _globals->visits(destination) << std::endl; + // std::cout << "Once only: " << _globals->visits(destination) << std::endl; // Ignore the choice if we've visited the destination before if (_globals->visits(destination) > 0) break; @@ -1252,7 +1252,7 @@ namespace ink::runtime::internal // Only show if the top of the eval stack is 'truthy' auto top = _eval.pop(); bool cond = top.truthy(); - std::cout << (int)top.type() << " Condiditon: " << cond << std::endl; + // std::cout << (int)top.type() << " Condiditon: " << cond << std::endl; if(!cond) break; } @@ -1292,7 +1292,7 @@ namespace ink::runtime::internal { // Keep track of current container auto index = read(); - std::cout << "START: " << index << std::endl; + // std::cout << "START: " << index << std::endl; _container.push(index); // Increment visit count @@ -1341,6 +1341,10 @@ namespace ink::runtime::internal // is 0-indexed for some reason. idk why but this is what ink expects _eval.push(value{}.set((int)_globals->visits(_container.top()) - 1)); } break; + case Command::TURN: + { + _eval.push(value{}.set((int)_globals->turns())); + } break; case Command::SEQUENCE: { // TODO: The C# ink runtime does a bunch of fancy logic @@ -1366,7 +1370,7 @@ namespace ink::runtime::internal container_t container = read(); // Push the read count for the requested container index - std::cout << "read count from: " << container << " = " <<_globals->visits(container) << std::endl; + // std::cout << "read count from: " << container << " = " <<_globals->visits(container) << std::endl; _eval.push(value{}.set((int)_globals->visits(container))); } break; case Command::TAG: diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index df214c4e..fc1471c3 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -56,6 +56,7 @@ namespace ink::compiler::internal // Offset in the binary stream uint32_t offset = 0; + uint32_t end_offset = 0; // Index used in CNT? operations container_t counter_index = ~0; @@ -123,6 +124,7 @@ namespace ink::compiler::internal uint32_t binary_emitter::end_container() { // Move up the chain + _current->end_offset = _containers.pos(); _current = _current->parent; // Return offset @@ -288,16 +290,16 @@ namespace ink::compiler::internal void binary_emitter::process_paths() { - std::function draw; - draw = [&draw](const container_data* node, int depth){ - for(int i = 0; i < depth; ++i) { std::cout << "\t"; } - std::cout << (node->counter_index < 1000 ? node->counter_index : -1) << ": " << node->offset; - std::cout << std::endl; - for(auto& child : node->children) { - draw(child, depth + 1); - } - }; - draw(_root, 0); + // std::function draw; + // draw = [&draw](const container_data* node, int depth){ + // for(int i = 0; i < depth; ++i) { std::cout << "\t"; } + // std::cout << (node->counter_index < 1000 ? node->counter_index : -1) << ": " << node->offset << " - " << node->end_offset; + // std::cout << std::endl; + // for(auto& child : node->children) { + // draw(child, depth + 1); + // } + // }; + // draw(_root, 0); for (auto pair : _paths) { // We need to replace the uint32_t at this location with the byte position of the requested container diff --git a/inkcpp_compiler/command.cpp b/inkcpp_compiler/command.cpp index 47a2b114..c8cea04a 100644 --- a/inkcpp_compiler/command.cpp +++ b/inkcpp_compiler/command.cpp @@ -34,6 +34,7 @@ namespace ink "du", "inkcpp_PUSH_VARIABLE_VALUE", "visit", + "turn", "inkcpp_READ_COUNT", "seq", "srnd", diff --git a/shared/private/command.h b/shared/private/command.h index 354b678d..3160c1aa 100644 --- a/shared/private/command.h +++ b/shared/private/command.h @@ -42,6 +42,7 @@ namespace ink DUPLICATE, PUSH_VARIABLE_VALUE, VISIT, + TURN, /// How many choices where made since start of the story READ_COUNT, SEQUENCE, SEED, @@ -57,6 +58,7 @@ namespace ink // == Threading THREAD, + // == thinary operations LIST_RANGE, OP_BEGIN = LIST_RANGE, From 152544fa46f922e6dd1c949e90675c471e44b86b Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 26 Sep 2023 16:10:48 +0200 Subject: [PATCH 09/14] Start cleaning --- inkcpp/runner_impl.cpp | 13 ++++++++++- inkcpp/simple_restorable_stack.h | 40 ++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index ffa14447..4fa0251e 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -225,8 +225,19 @@ namespace ink::runtime::internal // std::cout << std::endl; } if(record_visits) { + const container_t* iter; + size_t comm_end = 0; + while(_container.rev_iter(iter) && stack_iter != stack.rend()) { + if (std::get(*stack_iter) != *iter) { + break; + } + ++comm_end; + ++stack_iter; + } + stack_iter = stack.rbegin(); + size_t level = stack.size(); while(stack_iter != stack.rend()&& - (!is_in(std::get(*stack_iter)) || std::get(*stack_iter) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST )) + (level > comm_end || std::get(*stack_iter) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST )) { auto offset = std::get(*stack_iter); bool enteringStart = allEnteredAtStart && ((curr - offset) <= 6); diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index cacb8806..a6a90b03 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -24,6 +24,7 @@ namespace ink::runtime::internal void clear(); bool iter(const T*& iterator) const; + bool rev_iter(const T*& iterator) const; // == Save/Restore == void save(); @@ -164,7 +165,11 @@ namespace ink::runtime::internal // Begin at the top of the stack if (iterator == nullptr || iterator < _buffer || iterator > _buffer + _pos) { - iterator = _buffer + _pos - 1; + if (_pos == _save) { + iterator = _buffer + _jump -1; + } else { + iterator = _buffer + _pos - 1; + } return true; } @@ -175,7 +180,7 @@ namespace ink::runtime::internal // Run backwards iterator--; - // Skip nulls + // Skip nulls FIXME: not used? while (*iterator == _null) iterator--; @@ -189,6 +194,37 @@ namespace ink::runtime::internal return true; } + template + inline bool simple_restorable_stack::rev_iter(const T*& iterator) const + { + if (_pos == 0) + return false; + if (iterator == nullptr || iterator < _buffer || iterator > _buffer + _pos) { + if (_jump == 0) { + iterator = _buffer + _save; + } else { + iterator = _buffer; + } + return true; + } + + if (iterator == _buffer + _jump) { + iterator = _buffer + _save; + } + + ++iterator; + + while(*iterator ==_null) { + ++iterator; + } + + if(iterator == _buffer + _pos) { + iterator = nullptr; + return false; + } + return true; + } + template inline void simple_restorable_stack::save() { From bf23697dc58d5894f086201d32ed59f4354a727d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 27 Sep 2023 15:35:17 +0200 Subject: [PATCH 10/14] further cleaning --- inkcpp/runner_impl.cpp | 210 +++++------------------------- inkcpp/runner_impl.h | 9 +- inkcpp/simple_restorable_stack.h | 4 +- inkcpp/story_impl.cpp | 5 + inkcpp/story_impl.h | 2 + inkcpp_compiler/json_compiler.cpp | 2 +- 6 files changed, 53 insertions(+), 179 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 4fa0251e..c9a3bfa5 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -182,13 +182,21 @@ namespace ink::runtime::internal const uint32_t* iter = nullptr; container_t id; + // find current stack while(_story->iterate_containers(iter, id, offset)) { - if(offset >= dest) { break; } - if (stack.empty() || std::get(stack.back()) != id) { - inkAssert(static_cast(offset[0]) == Command::START_CONTAINER_MARKER, "Expected start Container"); - stack.push_back({static_cast(offset[1]), id, offset}); + if(offset >= dest) {break;} + } + bool reversed = _ptr > dest; + size_t comm_end = _container.size(); + while(_story->iterate_containers(iter, id, offset, reversed)) { + if((!reversed && offset >= dest) || (reversed && offset <= dest)) { break; } + if (_container.empty() || _container.top().id != id) { + _container.push({.id=id,.offset=offset}); } else { - stack.pop_back(); + _container.pop(); + if(_container.size() < comm_end) { + comm_end = _container.size(); + } } } @@ -196,93 +204,28 @@ namespace ink::runtime::internal if (offset == dest && static_cast(offset[0]) == Command::START_CONTAINER_MARKER) { _ptr += 6; - stack.push_back({static_cast(offset[1]), id, offset}); + _container.push({.id=id, .offset=offset}); } bool allEnteredAtStart = true; - auto stack_iter= stack.rbegin(); - auto is_in = [this](container_t id)-> bool{ - const container_t* iter; - while(this->_container.iter(iter)) { - if(*iter == id) { return true; } - } - return false; - }; ip_t curr = dest; - { - const container_t* iter; - // std::cout << "old: "; - while(_container.iter(iter)) { - // std::cout << (*iter ) << ", "; - } - // std::cout << std::endl; - } - { - // std::cout << "new: "; - for(auto iter = stack.rbegin(); iter != stack.rend(); ++iter) { - // std::cout << std::get(*iter) << ", "; - } - // std::cout << std::endl; - } + if(record_visits) { - const container_t* iter; - size_t comm_end = 0; - while(_container.rev_iter(iter) && stack_iter != stack.rend()) { - if (std::get(*stack_iter) != *iter) { - break; - } - ++comm_end; - ++stack_iter; - } - stack_iter = stack.rbegin(); - size_t level = stack.size(); - while(stack_iter != stack.rend()&& - (level > comm_end || std::get(*stack_iter) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST )) - { - auto offset = std::get(*stack_iter); - bool enteringStart = allEnteredAtStart && ((curr - offset) <= 6); - if(!enteringStart) { allEnteredAtStart = false; } - // std::cout << "visit: " << std::get(*stack_iter) << "\td: " << (curr - offset) << std::endl; - curr = offset; - _globals->visit(std::get(*stack_iter), enteringStart); - ++stack_iter; - }} - - stack_iter = stack.rbegin(); - if(!stack.empty() && !is_in(std::get(*stack_iter))) { - while(stack_iter != stack.rend() && !is_in(std::get(*stack_iter))) { - ++stack_iter; - } - if(stack_iter != stack.rend()) { - while(!_container.empty() && _container.top() != std::get(*stack_iter)) { - _container.pop(); - } - } else { - while(!_container.empty()) { - _container.pop(); - } - } - --stack_iter; - while(stack_iter != stack.rbegin()) { - _container.push(std::get(*stack_iter)); - --stack_iter; - } - _container.push(std::get(*stack_iter)); - } else if(stack.empty()) { - while(!_container.empty()) { - _container.pop(); - } - } - { - const container_t* iter; - // std::cout << "cur: "; - while(_container.iter(iter)) { - // std::cout << (*iter ) << ", "; + const ContainerData* iter = nullptr; + size_t level = stack.size(); + while(_container.iter(iter) && + (level > comm_end || _story->container_flag(iter->offset) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST )) + { + auto offset = iter->offset; + bool enteringStart = allEnteredAtStart && ((curr - offset) <= 6); // FIXME: == 0 + if(!enteringStart) { allEnteredAtStart = false; } + // std::cout << "visit: " << std::get(*stack_iter) << "\td: " << (curr - offset) << std::endl; + curr = offset; + _globals->visit(iter->id, enteringStart); } - // std::cout << std::endl; } - - + + // Optimization: if we are _is_falling, then we can // _should be_ able to safely assume that there is nothing to do here. A falling @@ -293,89 +236,6 @@ namespace ink::runtime::internal _ptr = dest; return; }*/ - - // Check which direction we are jumping - // bool reverse = dest < _ptr; - - // iteration - // const uint32_t* iter = nullptr; - // container_t container_id; - // ip_t offset; - // bool inBound = false; - - // // Iterate until we find the container marker just before our own - // while (_story->iterate_containers(iter, container_id, offset, reverse)) { - // if (( !reverse && offset > _ptr ) - // || ( reverse && offset < _ptr )) { - - // // Step back once in the iteration and break - // inBound = true; - // _story->iterate_containers(iter, container_id, offset, !reverse); - // break; - // } - // } - - // size_t pos = _container.size(); - - // bool first = true; - // // Start moving forward (or backwards) - // if(inBound && (offset == nullptr || (!reverse&&offset<=dest) || (reverse&&offset>dest)) ) - // while (_story->iterate_containers(iter, container_id, offset, reverse)) - // { - // // Break when we've past the destination - // if ((!reverse && offset > dest) || (reverse && offset <= dest)) { - // // jump back to start of same container - // if(first && reverse && offset == dest - // && _container.top() == container_id) { - // // check if it was start flag - // auto con_id = container_id; - // _story->iterate_containers(iter, container_id, offset, true); - // if(offset == nullptr || con_id == container_id) - // { - // _globals->visit(container_id); - // } - // } - // break; - // } - // first = false; - - // // Two cases: - - // // (1) Container iterator has the same value as the top of the stack. - // // This means that this is an end marker for the container we're in - // if (!_container.empty() && _container.top() == container_id) - // { - // if (_container.size() == pos) - // pos--; - - // // Get out of that container - // _container.pop(); - // } - - // // (2) This must be the entrance marker for a new container. Enter it - // else - // { - // // Push it - // _container.push(container_id); - // } - // } - - // // Iterate over the container stack marking any _new_ entries as "visited" - // if (record_visits) - // { - // const container_t* con_iter; - // size_t num_new = _container.size() - pos; - // while (_container.iter(con_iter)) - // { - // if (num_new <= 0) - // break; - // _globals->visit(*con_iter); - // --num_new; - // } - // } - - // // Jump - // _ptr = dest; } template void runner_impl::start_frame(uint32_t target) { @@ -441,7 +301,7 @@ namespace ink::runtime::internal *global.cast(), *data, static_cast(*this)), - _backup(nullptr), _done(nullptr), _choices(), _container(~0), _rng(time(NULL)) + _backup(nullptr), _done(nullptr), _choices(), _container(ContainerData{}), _rng(time(NULL)) { _ptr = _story->instructions(); _evaluation_mode = false; @@ -1304,20 +1164,20 @@ namespace ink::runtime::internal // Keep track of current container auto index = read(); // std::cout << "START: " << index << std::endl; - _container.push(index); + // offset points to command + _container.push({.id=index, .offset=_ptr - 6}); // Increment visit count - if (flag & CommandFlag::CONTAINER_MARKER_TRACK_VISITS) + if (flag & CommandFlag::CONTAINER_MARKER_TRACK_VISITS || flag & CommandFlag::CONTAINER_MARKER_TRACK_TURNS) { - _globals->visit(_container.top(), true); + _globals->visit(_container.top().id, true); } - // TODO Turn counts } break; case Command::END_CONTAINER_MARKER: { container_t index = read(); - inkAssert(_container.top() == index, "Leaving container we are not in!"); + inkAssert(_container.top().id == index, "Leaving container we are not in!"); // Move up out of the current container _container.pop(); @@ -1350,7 +1210,7 @@ namespace ink::runtime::internal { // Push the visit count for the current container to the top // is 0-indexed for some reason. idk why but this is what ink expects - _eval.push(value{}.set((int)_globals->visits(_container.top()) - 1)); + _eval.push(value{}.set((int)_globals->visits(_container.top().id) - 1)); } break; case Command::TURN: { diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index f58c8100..9c4b31c9 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -257,7 +257,14 @@ namespace ink::runtime::internal functions _functions; // Container set - internal::managed_restorable_stack _container; + struct ContainerData { + container_t id = ~0u; + ip_t offset = 0; + bool operator==(const ContainerData& oth) const { + return oth.id == id && oth.offset == offset; + } + }; + internal::managed_restorable_stack _container; bool _is_falling = false; bool _saved = false; diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index a6a90b03..ebbb4de6 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -6,6 +6,8 @@ namespace ink::runtime::internal { + /// only use this type for simple objects with simple copy operator and no heap references + /// because they will may be serialized, stored and loaded in a different instance template class simple_restorable_stack : public snapshot_interface { @@ -266,7 +268,6 @@ namespace ink::runtime::internal template size_t simple_restorable_stack::snap(unsigned char* data, const snapper&) const { - static_assert(is_same{}() || is_same{}() || is_same{}()); unsigned char* ptr = data; bool should_write = data != nullptr; ptr = snap_write(ptr, _null, should_write); @@ -286,7 +287,6 @@ namespace ink::runtime::internal template const unsigned char* simple_restorable_stack::snap_load(const unsigned char* ptr, const loader& loader) { - static_assert(is_same{}() || is_same{}() || is_same{}()); T null; ptr = snap_read(ptr, null); inkAssert(null == _null, "different null value compared to snapshot!"); diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 33a6e84b..9d96c1e8 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -155,6 +155,11 @@ namespace ink::runtime::internal } + CommandFlag story_impl::container_flag(ip_t offset) const { + inkAssert(static_cast(offset[0]) == Command::START_CONTAINER_MARKER || + static_cast(offset[0]) == Command::END_CONTAINER_MARKER); + return static_cast(offset[1]); + } CommandFlag story_impl::container_flag(container_t id) const { const uint32_t* iter = nullptr; ip_t offset; diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index 7858d2cc..172c16a2 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -36,6 +36,8 @@ namespace ink::runtime::internal bool iterate_containers(const uint32_t*& iterator, container_t& index, ip_t& offset, bool reverse = false) const; bool get_container_id(ip_t offset, container_t& container_id) const; + /// Get container flag from container offset (either start or end) + CommandFlag container_flag(ip_t offset) const; CommandFlag container_flag(container_t id) const; ip_t find_offset_for(hash_t path) const; diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index 7b8f7e7e..08e2ea54 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -231,7 +231,7 @@ namespace ink::compiler::internal // Write end container marker, End pointer should point to End command (form symetry with START command) if (meta.indexToReturn != ~0) - _emitter->write(Command::END_CONTAINER_MARKER, meta.indexToReturn); + _emitter->write(Command::END_CONTAINER_MARKER, meta.indexToReturn, meta.cmd_flags); // Record end position in map if (meta.recordInContainerMap) From b41e7edadc7d679fecd6a117d193939830ee199e Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 27 Sep 2023 21:26:16 +0200 Subject: [PATCH 11/14] Cleanup Jump code --- inkcpp/runner_impl.cpp | 97 ++++++++++++++++++++----------- inkcpp/simple_restorable_stack.h | 19 +++--- inkcpp_compiler/json_compiler.cpp | 2 - shared/public/system.h | 4 ++ 4 files changed, 77 insertions(+), 45 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index c9a3bfa5..c692d1b5 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -9,6 +9,7 @@ #include "system.h" #include "value.h" +#include #include #include @@ -174,68 +175,98 @@ namespace ink::runtime::internal void runner_impl::jump(ip_t dest, bool record_visits) { - // std::cout << "jump to: " << (dest - _story->instructions()) << std::endl; - if (dest == _ptr) { return; } - std::vector> stack; + // Optimization: if we are _is_falling, then we can + // _should be_ able to safely assume that there is nothing to do here. A falling + // divert should only be taking us from a container to that same container's end point + // without entering any other containers + // OR IF if target is same position do nothing + // could happend if jumping to and of an unnamed container + if (_is_falling || dest ==_ptr) + { + _ptr = dest; + return; + } - ip_t offset; const uint32_t* iter = nullptr; container_t id; + ip_t offset; + size_t comm_end; + bool reversed = _ptr > dest; - // find current stack - while(_story->iterate_containers(iter, id, offset)) { - if(offset >= dest) {break;} + if (reversed) { + comm_end = 0; + iter = nullptr; + const ContainerData* old_iter = nullptr; + const uint32_t* last_comm_iter = nullptr; + _container.rev_iter(old_iter); + + // find commen part of old and new stack + while(_story->iterate_containers(iter, id, offset)) { + if(old_iter == nullptr || offset >= dest) { break; } + if(old_iter !=nullptr && id == old_iter->id) { + last_comm_iter = iter; + _container.rev_iter(old_iter); + ++comm_end; + } + } + + // clear old part from stack + while(_container.size() > comm_end) { _container.pop(); } + iter = last_comm_iter; + + } else { + iter = nullptr; + comm_end = _container.size(); + // go to current possition in container list + while(_story->iterate_containers(iter, id, offset)) { + if(offset >= _ptr) {break;} + } + _story->iterate_containers(iter, id, offset, true); } - bool reversed = _ptr > dest; - size_t comm_end = _container.size(); - while(_story->iterate_containers(iter, id, offset, reversed)) { - if((!reversed && offset >= dest) || (reversed && offset <= dest)) { break; } - if (_container.empty() || _container.top().id != id) { - _container.push({.id=id,.offset=offset}); + + // move to destination and update container stack on the go + while(_story->iterate_containers(iter, id, offset)) { + if (offset >= dest) { break; } + if(_container.empty() || _container.top().id != id) { + _container.push({.id = id, .offset = offset}); } else { _container.pop(); - if(_container.size() < comm_end) { + if (_container.size() < comm_end) { comm_end = _container.size(); } } } - _ptr = dest; + // if we jump directly to a named container start, go inside, if its a ONLY_FIRST container + // it will get visited in the next step if (offset == dest && static_cast(offset[0]) == Command::START_CONTAINER_MARKER) { _ptr += 6; _container.push({.id=id, .offset=offset}); + if (reversed && comm_end == _container.size() - 1) { ++comm_end; } } + // iff all container (until now) are entered at first position bool allEnteredAtStart = true; - ip_t curr = dest; - + ip_t child_position = dest; if(record_visits) { const ContainerData* iter = nullptr; - size_t level = stack.size(); + size_t level = _container.size(); while(_container.iter(iter) && (level > comm_end || _story->container_flag(iter->offset) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST )) { auto offset = iter->offset; - bool enteringStart = allEnteredAtStart && ((curr - offset) <= 6); // FIXME: == 0 - if(!enteringStart) { allEnteredAtStart = false; } - // std::cout << "visit: " << std::get(*stack_iter) << "\td: " << (curr - offset) << std::endl; - curr = offset; - _globals->visit(iter->id, enteringStart); + inkAssert(child_position >= offset, "Container stack order is broken"); + // 6 == len of START_CONTAINER_SIGNAL, if its 6 bytes behind the container it is a unnnamed subcontainers first child + // check if child_positino is the first child of current container + allEnteredAtStart = allEnteredAtStart && ((child_position - offset) <= 6); + child_position = offset; + _globals->visit(iter->id, allEnteredAtStart); } } - // Optimization: if we are _is_falling, then we can - // _should be_ able to safely assume that there is nothing to do here. A falling - // divert should only be taking us from a container to that same container's end point - // without entering any other containers - /*if (_is_falling) - { - _ptr = dest; - return; - }*/ } template void runner_impl::start_frame(uint32_t target) { @@ -1201,7 +1232,7 @@ namespace ink::runtime::internal // HACK _ptr += sizeof(Command) + sizeof(CommandFlag); execute_return(); - } else if (_container.empty() && _ptr == _story->end()){ + } else if (_container.empty() && _ptr == _story->end()){ // FIXME on_done(false); } } diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index ebbb4de6..94b6c3d0 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -168,6 +168,10 @@ namespace ink::runtime::internal if (iterator == nullptr || iterator < _buffer || iterator > _buffer + _pos) { if (_pos == _save) { + if(_jump == 0) { + iterator = nullptr; + return false; + } iterator = _buffer + _jump -1; } else { iterator = _buffer + _pos - 1; @@ -182,9 +186,6 @@ namespace ink::runtime::internal // Run backwards iterator--; - // Skip nulls FIXME: not used? - while (*iterator == _null) - iterator--; // End if (iterator < _buffer) @@ -203,23 +204,21 @@ namespace ink::runtime::internal return false; if (iterator == nullptr || iterator < _buffer || iterator > _buffer + _pos) { if (_jump == 0) { + if (_save == _pos) { + iterator = nullptr; + return false; + } iterator = _buffer + _save; } else { iterator = _buffer; } return true; } - + ++iterator; if (iterator == _buffer + _jump) { iterator = _buffer + _save; } - ++iterator; - - while(*iterator ==_null) { - ++iterator; - } - if(iterator == _buffer + _pos) { iterator = nullptr; return false; diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index 08e2ea54..f7f80c5e 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -133,8 +133,6 @@ namespace ink::compiler::internal // Write command out at this position if(meta.cmd_flags != CommandFlag::NO_FLAGS) { _emitter->write(Command::START_CONTAINER_MARKER, meta.indexToReturn, meta.cmd_flags); - } else { - _emitter->write_raw(Command::VOID); } if(meta.recordInContainerMap) { _emitter->add_start_to_container_map(position, meta.indexToReturn); diff --git a/shared/public/system.h b/shared/public/system.h index 0e5af118..6e834119 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -140,6 +140,10 @@ namespace ink template void ink_assert( bool condition, const char* msg = nullptr, Args... args ) { + static const char* EMPTY = ""; + if (msg == nullptr) { + msg = EMPTY; + } if ( !condition ) { if constexpr ( sizeof...( args ) > 0 ) From e762633cae14eee0203796c26e5e7358ce30353d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 28 Sep 2023 15:26:12 +0200 Subject: [PATCH 12/14] Cleaning --- inkcpp/globals_impl.cpp | 1 - inkcpp/globals_impl.h | 1 + inkcpp/runner_impl.cpp | 14 ++------------ inkcpp_compiler/binary_emitter.cpp | 12 ------------ inkcpp_compiler/json_compiler.cpp | 1 - shared/private/command.h | 1 - 6 files changed, 3 insertions(+), 27 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 11c65a79..76621f4a 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -5,7 +5,6 @@ #include "system.h" #include "types.h" -#include namespace ink::runtime::internal { diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index 2c28fe14..a8e2f0bc 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -37,6 +37,7 @@ namespace ink::runtime::internal public: // Records a visit to a container + /// @param start_cmd iff the visit was initiatet through a MARKER_START_CONTAINER void visit(uint32_t container_id, bool entering_at_start); // Checks the number of visits to a container diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index c692d1b5..6481d213 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -9,10 +9,6 @@ #include "system.h" #include "value.h" -#include -#include -#include - namespace ink::runtime { const choice* runner_interface::get_choice(size_t index) const @@ -1138,7 +1134,6 @@ namespace ink::runtime::internal container_t destination = -1; if (_story->get_container_id(_story->instructions() + path, destination)) { - // std::cout << "Once only: " << _globals->visits(destination) << std::endl; // Ignore the choice if we've visited the destination before if (_globals->visits(destination) > 0) break; @@ -1152,10 +1147,7 @@ namespace ink::runtime::internal // Choice is conditional if (flag & CommandFlag::CHOICE_HAS_CONDITION) { // Only show if the top of the eval stack is 'truthy' - auto top = _eval.pop(); - bool cond = top.truthy(); - // std::cout << (int)top.type() << " Condiditon: " << cond << std::endl; - if(!cond) + if(!_eval.pop().truthy()) break; } @@ -1194,8 +1186,7 @@ namespace ink::runtime::internal { // Keep track of current container auto index = read(); - // std::cout << "START: " << index << std::endl; - // offset points to command + // offset points to command, command has size 6 _container.push({.id=index, .offset=_ptr - 6}); // Increment visit count @@ -1272,7 +1263,6 @@ namespace ink::runtime::internal container_t container = read(); // Push the read count for the requested container index - // std::cout << "read count from: " << container << " = " <<_globals->visits(container) << std::endl; _eval.push(value{}.set((int)_globals->visits(container))); } break; case Command::TAG: diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index fc1471c3..1e7c3a5d 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -4,8 +4,6 @@ #include "version.h" #include "list_data.h" -#include -#include #include #include #include @@ -290,16 +288,6 @@ namespace ink::compiler::internal void binary_emitter::process_paths() { - // std::function draw; - // draw = [&draw](const container_data* node, int depth){ - // for(int i = 0; i < depth; ++i) { std::cout << "\t"; } - // std::cout << (node->counter_index < 1000 ? node->counter_index : -1) << ": " << node->offset << " - " << node->end_offset; - // std::cout << std::endl; - // for(auto& child : node->children) { - // draw(child, depth + 1); - // } - // }; - // draw(_root, 0); for (auto pair : _paths) { // We need to replace the uint32_t at this location with the byte position of the requested container diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index f7f80c5e..811b38dd 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -6,7 +6,6 @@ #include "version.h" #include -#include namespace ink::compiler::internal { diff --git a/shared/private/command.h b/shared/private/command.h index 3160c1aa..881963ea 100644 --- a/shared/private/command.h +++ b/shared/private/command.h @@ -58,7 +58,6 @@ namespace ink // == Threading THREAD, - // == thinary operations LIST_RANGE, OP_BEGIN = LIST_RANGE, From 86c414f6c8147a569cd5bfb452288e201758e571 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 28 Sep 2023 15:48:16 +0200 Subject: [PATCH 13/14] Add List truthy support --- inkcpp/runner_impl.cpp | 10 +++++----- inkcpp/value.cpp | 44 ++++++++++++++++++++++-------------------- inkcpp/value.h | 2 +- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 6481d213..ff983bbd 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -873,7 +873,7 @@ namespace ink::runtime::internal uint32_t target = read(); // Check for condition - if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().truthy()) + if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().truthy(_globals->lists())) break; // SPECIAL: Fallthrough divert. We're starting to fall out of containers @@ -917,7 +917,7 @@ namespace ink::runtime::internal hash_t variable = read(); // Check for condition - if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().truthy()) + if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().truthy(_globals->lists())) break; const value* val = get_var(variable); @@ -1147,7 +1147,7 @@ namespace ink::runtime::internal // Choice is conditional if (flag & CommandFlag::CHOICE_HAS_CONDITION) { // Only show if the top of the eval stack is 'truthy' - if(!_eval.pop().truthy()) + if(!_eval.pop().truthy(_globals->lists())) break; } @@ -1223,8 +1223,8 @@ namespace ink::runtime::internal // HACK _ptr += sizeof(Command) + sizeof(CommandFlag); execute_return(); - } else if (_container.empty() && _ptr == _story->end()){ // FIXME - on_done(false); + } else if (_ptr == _story->end()){ // check needed, because it colud exist an unnamed toplevel container (empty named container stack != empty container stack) + on_done(true); } } } break; diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 6f72faa7..30962872 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -11,88 +11,90 @@ namespace ink::runtime::internal { template - bool truthy_impl(const value& v); + bool truthy_impl(const value& v, const list_table& lists); template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { inkAssert("Type was not found in operational types or it has no conversion to boolean"); } template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { if(v.type() == value_type::string) { // if string is not empty return *v.get().str != 0; } else { - return truthy_impl(v); + return truthy_impl(v, lists); } } template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { + // if list is not empty -> valid flag -> filled list if(v.type() == value_type::list_flag) { auto flag = v.get(); return flag != null_flag && flag != empty_flag; } else { - return truthy_impl(v); + return truthy_impl(v, lists); } } template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { + // if list is not empty if(v.type() == value_type::list) { - inkAssert("Curently not supported"); + return lists.count(v.get()) > 0; } else { - return truthy_impl(v); + return truthy_impl(v, lists); } } template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { if (v.type() == value_type::float32) { return v.get() != 0.0f; } else { - return truthy_impl(v); + return truthy_impl(v, lists); } } template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { if(v.type() == value_type::int32) { return v.get() != 0; } else { - return truthy_impl(v); + return truthy_impl(v, lists); } } template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { if (v.type() == value_type::uint32) { return v.get() != 0; } else { - return truthy_impl(v); + return truthy_impl(v, lists); } } template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { if(v.type() == value_type::boolean) { return v.get(); } else { - return truthy_impl(v); + return truthy_impl(v, lists); } } template<> - bool truthy_impl(const value& v) { + bool truthy_impl(const value& v, const list_table& lists) { if (v.type() == value_type::divert) { inkAssert("Divert can not be evaluated to boolean"); } else { - return truthy_impl(v); + return truthy_impl(v, lists); } } - bool value::truthy() const { - return truthy_impl(*this); + bool value::truthy(const list_table& lists) const { + return truthy_impl(*this, lists); } diff --git a/inkcpp/value.h b/inkcpp/value.h index 3bdb3da2..19f7567f 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -107,7 +107,7 @@ namespace ink::runtime::internal { get() const { static_assert(ty != ty, "No getter for this type defined!"); } /// check if value evaluates to true - bool truthy() const; + bool truthy(const list_table& lists) const; /// set value of type (if possible) template constexpr value& set(Args ...args) { From ed98f54756268dd4a349b42e8ca5e9fc43167dd9 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 28 Sep 2023 15:52:52 +0200 Subject: [PATCH 14/14] Add explicit ContainerData != operator --- inkcpp/runner_impl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 9c4b31c9..44396d07 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -263,6 +263,7 @@ namespace ink::runtime::internal bool operator==(const ContainerData& oth) const { return oth.id == id && oth.offset == offset; } + bool operator!=(const ContainerData& oth) const { return !(*this == oth); } }; internal::managed_restorable_stack _container; bool _is_falling = false;