From 1a7802fc35e06cfc78303fcd11a20a9011a9fa7e Mon Sep 17 00:00:00 2001 From: Francisco Tufro Date: Sat, 20 Feb 2021 19:05:06 -0300 Subject: [PATCH 1/6] Implemented Tags --- inkcpp/include/runner.h | 4 ++++ inkcpp/runner_impl.cpp | 26 ++++++++++++++++++++++++++ inkcpp/runner_impl.h | 12 ++++++++++++ inkcpp_compiler/command.cpp | 1 + inkcpp_compiler/json_compiler.cpp | 6 +++++- shared/private/command.h | 1 + 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index d23891b5..13e699d8 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -149,6 +149,10 @@ namespace ink::runtime */ virtual void choose(size_t index) = 0; + virtual bool has_tags() = 0; + virtual size_t num_tags() = 0; + virtual const char* get_tag(size_t index) = 0; + protected: // internal bind implementation. not for calling. virtual void internal_bind(hash_t name, internal::function_base* function) = 0; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 0dc95e7d..2d26b389 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -60,6 +60,11 @@ namespace ink::runtime::internal _num_choices = 0; } + void runner_impl::clear_tags() + { + _num_tags = 0; + } + void runner_impl::jump(ip_t dest, bool record_visits) { // Optimization: if we are _is_falling, then we can @@ -388,6 +393,7 @@ namespace ink::runtime::internal // Jump to destination and clear choice list jump(_story->instructions() + _choices[index].path()); clear_choices(); + clear_tags(); } void runner_impl::getline_silent() @@ -397,6 +403,22 @@ namespace ink::runtime::internal _output.clear(); } + bool runner_impl::has_tags() + { + return _num_tags > 0; + } + + size_t runner_impl::num_tags() + { + return _num_tags; + } + + const char* runner_impl::get_tag(size_t index) + { + inkAssert(index < _num_tags, "Tag index exceeds _num_tags"); + return _tags[index]; + } + #ifdef INK_ENABLE_CSTD char* runner_impl::getline_alloc() { @@ -948,6 +970,10 @@ namespace ink::runtime::internal // Push the read count for the requested container index _eval.push((int)_globals->visits(container)); } break; + case Command::TAG: + { + _tags[_num_tags++] = read(); + } break; default: inkAssert(false, "Unrecognized command!"); break; diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 3b32dc14..3b444391 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -48,6 +48,11 @@ namespace ink::runtime::internal // runs silently void getline_silent(); + virtual bool has_tags() override; + virtual size_t num_tags() override; + virtual const char* get_tag(size_t index) override; + + #ifdef INK_ENABLE_CSTD // c-style getline virtual char* getline_alloc() override; @@ -112,6 +117,8 @@ namespace ink::runtime::internal choice& add_choice(); void clear_choices(); + void clear_tags(); + // Special code for jumping from the current IP to another void jump(ip_t, bool record_visits = true); @@ -156,6 +163,11 @@ namespace ink::runtime::internal choice _choices[MAX_CHOICES]; size_t _num_choices = 0; + // Tag list + static const size_t MAX_TAGS = 100; + const char* _tags[MAX_TAGS]; + size_t _num_tags = 0; + // TODO: Move to story? Both? functions _functions; diff --git a/inkcpp_compiler/command.cpp b/inkcpp_compiler/command.cpp index 26406028..04fb83b9 100644 --- a/inkcpp_compiler/command.cpp +++ b/inkcpp_compiler/command.cpp @@ -11,6 +11,7 @@ namespace ink "\n", "<>", "void", + "#", "DIVERT", "DIVERT_TO_VARIABLE", "TUNNEL", diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index 1bd65808..07178536 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -299,7 +299,7 @@ namespace ink::compiler::internal else if (get(command, "CNT?", val)) { // TODO: Why is this true again? - _emitter->write_path(Command::READ_COUNT, CommandFlag::NO_FLAGS, val, true); + _emitter->write_path(Command::READ_COUNT, CommandFlag::NO_FLAGS, val, false); } // Internal function call @@ -318,5 +318,9 @@ namespace ink::compiler::internal // Encode argument count into command flag and write out the hash of the function name _emitter->write(Command::CALL_EXTERNAL, hash_string(val.c_str()), (CommandFlag)numArgs); } + else if (get(command, "#", val)) + { + _emitter->write_string(Command::TAG, CommandFlag::NO_FLAGS, val); + } } } \ No newline at end of file diff --git a/shared/private/command.h b/shared/private/command.h index a164be23..c5f70da1 100644 --- a/shared/private/command.h +++ b/shared/private/command.h @@ -13,6 +13,7 @@ namespace ink NEWLINE, GLUE, VOID, + TAG, // == Diverts DIVERT, From 62c7063cbde74f55ddccd5e10dbf8619df6a26a3 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 19:20:25 +0100 Subject: [PATCH 2/6] Add Tag printing to command line tool --- inkcpp_cl/inkcpp_cl.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 328aaaea..5db8d701 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -146,6 +146,13 @@ int main(int argc, const char** argv) while (thread->can_continue()) std::cout << thread->getline(); + if (thread->has_tags()){ + std::cout << "# tags: "; + for (int i = 0; i < thread->num_tags(); ++i) { + std::cout << thread->get_tag(i) << ", "; + } + std::cout << std::endl; + } if (thread->has_choices()) { // Extra end line @@ -175,4 +182,4 @@ int main(int argc, const char** argv) std::cerr << "Unhandled ink runtime exception: " << e.what() << std::endl; return 1; } -} \ No newline at end of file +} From 73955553fd0044ed3b9037098fde0ef1f1c9d6d7 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 19:21:05 +0100 Subject: [PATCH 3/6] Make tag fetch functions const --- inkcpp/include/runner.h | 8 ++++---- inkcpp/runner_impl.cpp | 6 +++--- inkcpp/runner_impl.h | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 13e699d8..89b42e2d 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -149,9 +149,9 @@ namespace ink::runtime */ virtual void choose(size_t index) = 0; - virtual bool has_tags() = 0; - virtual size_t num_tags() = 0; - virtual const char* get_tag(size_t index) = 0; + virtual bool has_tags() const = 0; + virtual size_t num_tags() const = 0; + virtual const char* get_tag(size_t index) const = 0; protected: // internal bind implementation. not for calling. @@ -240,4 +240,4 @@ namespace ink::runtime inline const choice* operator[](size_t index) { return get_choice(index); } #pragma endregion }; -} \ No newline at end of file +} diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 2d26b389..8e07ebc9 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -403,17 +403,17 @@ namespace ink::runtime::internal _output.clear(); } - bool runner_impl::has_tags() + bool runner_impl::has_tags() const { return _num_tags > 0; } - size_t runner_impl::num_tags() + size_t runner_impl::num_tags() const { return _num_tags; } - const char* runner_impl::get_tag(size_t index) + const char* runner_impl::get_tag(size_t index) const { inkAssert(index < _num_tags, "Tag index exceeds _num_tags"); return _tags[index]; diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 3b444391..a92efc10 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -48,9 +48,9 @@ namespace ink::runtime::internal // runs silently void getline_silent(); - virtual bool has_tags() override; - virtual size_t num_tags() override; - virtual const char* get_tag(size_t index) override; + virtual bool has_tags() const override; + virtual size_t num_tags() const override; + virtual const char* get_tag(size_t index) const override; #ifdef INK_ENABLE_CSTD From d34126921479c6032c48c5bd4d18563c57e55fe4 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 19:21:24 +0100 Subject: [PATCH 4/6] Add tag behavior example --- notes/TagsNotes.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 notes/TagsNotes.md diff --git a/notes/TagsNotes.md b/notes/TagsNotes.md new file mode 100644 index 00000000..46ba2ab0 --- /dev/null +++ b/notes/TagsNotes.md @@ -0,0 +1,24 @@ +Tags are accessible via runner functions: + +- `bool has_tags()` are there any tags now? +- `size_t num_tags()` how many tags are there right now? +- `const char* get_tag(size_t i)` get string value of tag + +Tags are acquired until a choice appears. After choosing the acquired tags will +be cleared. +``` +# Tag1 +# Tag 2 +Some Text # Tag3 +* A # Tag 4 +* B # Tag 5 +- out # Tag6 +``` + +Will produce: +> Some Text: Tag1, Tag2, Tag3 +> * A +> * B +> <1 +> A +> out: Tag4, Tag6 From a9c2b1029bf73200bc20ed5010b1c038039f460d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 19:52:17 +0100 Subject: [PATCH 5/6] Add test for tags --- inkcpp_test/CMakeLists.txt | 1 + inkcpp_test/Tags.cpp | 96 +++++++++++++++++++++++++++++++++++ inkcpp_test/ink/TagsStory.ink | 21 ++++++++ 3 files changed, 118 insertions(+) create mode 100644 inkcpp_test/Tags.cpp create mode 100644 inkcpp_test/ink/TagsStory.ink diff --git a/inkcpp_test/CMakeLists.txt b/inkcpp_test/CMakeLists.txt index 27a01340..bbf43dac 100644 --- a/inkcpp_test/CMakeLists.txt +++ b/inkcpp_test/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(inkcpp_test catch.hpp Main.cpp Restorable.cpp Value.cpp Globals.cpp + Tags.cpp ) target_link_libraries(inkcpp_test PUBLIC inkcpp inkcpp_compiler) diff --git a/inkcpp_test/Tags.cpp b/inkcpp_test/Tags.cpp new file mode 100644 index 00000000..274b2d33 --- /dev/null +++ b/inkcpp_test/Tags.cpp @@ -0,0 +1,96 @@ +#include "catch.hpp" +#include "../inkcpp_cl/test.h" + +#include +#include +#include +#include +#include + +using namespace ink::runtime; + +SCENARIO("run story with tags", "[tags]") +{ + GIVEN("a story with tags") + { + inklecate("ink/TagsStory.ink", "TagsStory.tmp"); + ink::compiler::run("TagsStory.tmp", "TagsStory.bin"); + auto ink = story::from_file("TagsStory.bin"); + runner thread = ink->new_runner(); + WHEN("start thread") + { + THEN("No tags") + { + REQUIRE(thread->has_tags() == false); + } + WHEN("approach first line") + { + std::string line = thread->getall(); + THEN("print no tags") + { + REQUIRE(line == "Hello\n"); + } + THEN("collect all previous Tags (global, knot, line) in correct order") + { + REQUIRE(thread->has_tags() == true); + REQUIRE(thread->num_tags() == 4); + REQUIRE(std::string(thread->get_tag(0)) == "global_tag"); + REQUIRE(std::string(thread->get_tag(1)) == "knot_tag_start"); + REQUIRE(std::string(thread->get_tag(2)) == "second_knot_tag_start"); + REQUIRE(std::string(thread->get_tag(3)) == "output_tag_h"); + } + WHEN("print choices") + { + auto itr = thread->begin(); + std::string choices[2] = { + (itr++)->text(), + (itr++)->text() + }; + THEN("choices won't print tags, tags are still the same") + { + REQUIRE(choices[0] == "a"); + REQUIRE(choices[1] == "b"); + REQUIRE(thread->has_tags() == true); + REQUIRE(thread->num_tags() == 4); + REQUIRE(std::string(thread->get_tag(0)) == "global_tag"); + REQUIRE(std::string(thread->get_tag(1)) == "knot_tag_start"); + REQUIRE(std::string(thread->get_tag(2)) == "second_knot_tag_start"); + REQUIRE(std::string(thread->get_tag(3)) == "output_tag_h"); + } + WHEN("choose divert") + { + thread->choose(1); + THEN("choosing won't add tags!") + { + REQUIRE(thread->has_tags() == false); + REQUIRE(thread->num_tags() == 0); + } + WHEN("proceed") + { + std::string line = thread->getline(); + THEN("new knot tag and now line tag, also choice tag. AND dont print tag in choice") + { + REQUIRE(line == "bKnot2\n"); + REQUIRE(thread->has_tags() == true); + REQUIRE(thread->num_tags() == 3); + REQUIRE(std::string(thread->get_tag(0)) == "choice_tag_b"); + REQUIRE(std::string(thread->get_tag(1)) == "knot_tag_2"); + REQUIRE(std::string(thread->get_tag(2)) == "output_tag_k"); + } + WHEN("choose choice without tag, and proceed to end") + { + thread->choose(0); + thread->getline(); + THEN("no tags, tags behind END are ignored") + { + REQUIRE(thread->has_tags() == false); + REQUIRE(thread->num_tags() == 0); + } + } + } + } + } + } + } + } +} diff --git a/inkcpp_test/ink/TagsStory.ink b/inkcpp_test/ink/TagsStory.ink new file mode 100644 index 00000000..f15afc31 --- /dev/null +++ b/inkcpp_test/ink/TagsStory.ink @@ -0,0 +1,21 @@ +# global_tag +->start +===start +# knot_tag_start +# second_knot_tag_start + +Hello # output_tag_h +* a +* b->knot2 # choice_tag_b +- World! # output_tag_w +* c # choice_tag_c +* d # choice_tag_d +- ->END + +===knot2 +# knot_tag_2 + +Knot2 # output_tag_k +* e +* f # choice_tag_f +- out->END # close_tag From bff08dd3f9fc08664ddf04d711de39c6e24df672 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 2 Mar 2021 13:37:25 +0100 Subject: [PATCH 6/6] Add Null terminator for setting global strings --- inkcpp/globals_impl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index c7dc5920..8637eb8a 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -133,6 +133,7 @@ namespace ink::runtime::internal for(const char* i = val; *i; ++i) { *ptr++ = *i; } + *ptr = 0; internal::data d; d.set_string(new_string, true); *v = internal::value(d);