diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b35225ae..a2248d94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -168,7 +168,7 @@ jobs: # Upload results artifact - name: Upload Results Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: results path: proofing/ink-proof/${{ matrix.artifact }}.txt @@ -176,7 +176,7 @@ jobs: # Upload website artifact - name: Upload Ink-Proof Website Artifact if: ${{ matrix.proof }} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ matrix.artifact }}-www path: proofing/ink-proof/out @@ -199,9 +199,28 @@ jobs: python3 -m pip install build + pytest --user - name: Build python release run: python3 -m build + + - uses: suisei-cn/actions-download-file@v1.4.0 + name: Download Inklecate + id: download_inklecate + with: + url: https://github.com/inkle/ink/releases/download/v1.1.1/inklecate_linux.zip + target: "inklecate/" + - name: Deploy Inkelcate + shell: bash + run: | + cd inklecate + unzip *.zip + echo "INKLECATE=${{ matrix.inklecate_pre }}$GITHUB_WORKSPACE/inklecate/inklecate${{ matrix.inklecate_post }}" >> $GITHUB_ENV + + - name: Test python release + run: | + python3 -m pip install dist/*.whl --user + python3 -m pytest - name: Remove wheel run: | rm dist/*.whl diff --git a/inkcpp/functions.cpp b/inkcpp/functions.cpp index 0c923183..26cd1de2 100644 --- a/inkcpp/functions.cpp +++ b/inkcpp/functions.cpp @@ -2,56 +2,47 @@ namespace ink::runtime::internal { - functions::functions() : _list(nullptr), _last(nullptr) - { - } - - functions::~functions() - { - // clean list - while (_list) - { - entry* toDelete = _list; - _list = _list->next; +functions::functions() + : _list(nullptr) + , _last(nullptr) +{ +} - // delete both value and entry - delete toDelete->value; - delete toDelete; - } - _list = _last = nullptr; +functions::~functions() +{ + // clean list + while (_list) { + entry* toDelete = _list; + _list = _list->next; + + // delete both value and entry + delete toDelete->value; + delete toDelete; } + _list = _last = nullptr; +} - void functions::add(hash_t name, function_base* func) - { - entry* current = new entry; - current->name = name; - current->value = func; - current->next = nullptr; - - if (_list == nullptr) - { - _list = _last = current; - } - else - { - _last->next = current; - _last = current; - } +void functions::add(hash_t name, function_base* func) +{ + entry* current = new entry; + current->name = name; + current->value = func; + current->next = nullptr; + + if (_list == nullptr) { + _list = _last = current; + } else { + _last->next = current; + _last = current; } +} - bool functions::call(hash_t name, basic_eval_stack* stack, size_t num_arguments, string_table& strings, list_table& lists) - { - // find entry - entry* iter = _list; - while (iter != nullptr && iter->name != name) - iter = iter->next; - - // failed to find - if (iter == nullptr) - return false; - - // call - iter->value->call(stack, num_arguments, strings, lists); - return true; - } -} \ No newline at end of file +function_base* functions::find(hash_t name) +{ + // find entry + entry* iter = _list; + while (iter != nullptr && iter->name != name) + iter = iter->next; + return iter == nullptr ? nullptr : iter->value; +} +} // namespace ink::runtime::internal diff --git a/inkcpp/functions.h b/inkcpp/functions.h index 8e45e21f..57dc052e 100644 --- a/inkcpp/functions.h +++ b/inkcpp/functions.h @@ -5,31 +5,30 @@ namespace ink::runtime::internal { - class basic_eval_stack; +class basic_eval_stack; - // Stores bound functions - class functions - { - public: - functions(); - ~functions(); - - // Adds a function to the registry - void add(hash_t name, function_base* func); +// Stores bound functions +class functions +{ +public: + functions(); + ~functions(); - // Calls a function (if available) - bool call(hash_t name, basic_eval_stack* stack, size_t num_arguments, string_table& strings, list_table& lists); + // Adds a function to the registry + void add(hash_t name, function_base* func); - private: - struct entry - { - hash_t name; - function_base* value; - entry* next; - }; + // Calls a function (if available) + function_base* find(hash_t name); - // TODO: Better than a linked list? - entry* _list; - entry* _last; +private: + struct entry { + hash_t name; + function_base* value; + entry* next; }; -} \ No newline at end of file + + // TODO: Better than a linked list? + entry* _list; + entry* _last; +}; +} // namespace ink::runtime::internal diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 76621f4a..163f4177 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -5,263 +5,273 @@ #include "system.h" #include "types.h" - 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) - , _lists(story->list_meta(), story->get_header()) - , _globals_initialized(false) - { - _visit_counts.resize(_num_containers); - if (_lists) { - // initelize static lists - const list_flag* flags = story->lists(); - while(*flags != null_flag) { - list_table::list l = _lists.create_permament(); - while(*flags != null_flag) { - _lists.add_inplace(l, *flags); - ++flags; - } +globals_impl::globals_impl(const story_impl* story) + : _num_containers(story->num_containers()) + , _turn_cnt{0} + , _visit_counts() + , _visit_counts_backup() + , _owner(story) + , _runners_start(nullptr) + , _lists(story->list_meta(), story->get_header()) + , _globals_initialized(false) +{ + _visit_counts.resize(_num_containers); + _visit_counts_backup.resize(_num_containers); + if (_lists) { + // initelize static lists + const list_flag* flags = story->lists(); + while (*flags != null_flag) { + list_table::list l = _lists.create_permament(); + while (*flags != null_flag) { + _lists.add_inplace(l, *flags); ++flags; } - for(const auto& flag : _lists.named_flags()) { - set_variable(hash_string(flag.name), value{}.set( - list_flag{ - flag.flag.list_id, - flag.flag.flag - })); - } + ++flags; } - } - - void globals_impl::visit(uint32_t container_id, bool entering_at_start) - { - 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; + for (const auto& flag : _lists.named_flags()) { + set_variable( + hash_string(flag.name), + value{}.set(list_flag{flag.flag.list_id, flag.flag.flag}) + ); } } +} - uint32_t globals_impl::visits(uint32_t container_id) const - { - return _visit_counts[container_id].visits; +void globals_impl::visit(uint32_t container_id, bool entering_at_start) +{ + 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::turns() const { - return _turn_cnt; - } +uint32_t globals_impl::visits(uint32_t container_id) const +{ + return _visit_counts[container_id].visits; +} - void globals_impl::turn() - { - ++_turn_cnt; - for(size_t i = 0; i < _visit_counts.size(); ++i) - { - if(_visit_counts[i].turns != -1) { - _visit_counts[i].turns += 1; - } +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) { + _visit_counts[i].turns += 1; } } +} - uint32_t globals_impl::turns(uint32_t container_id) const - { - return _visit_counts[container_id].turns; - } +uint32_t globals_impl::turns(uint32_t container_id) const +{ + return _visit_counts[container_id].turns; +} +void globals_impl::add_runner(const runner_impl* runner) +{ + // cache start of list + auto first = _runners_start; + // create new entry as start, linked to the previous start + _runners_start = new runner_entry{runner, first}; +} - void globals_impl::add_runner(const runner_impl* runner) - { - // cache start of list - auto first = _runners_start; +void globals_impl::remove_runner(const runner_impl* runner) +{ + // iterate linked list + runner_entry* prev = nullptr; + auto iter = _runners_start; + while (iter != nullptr) { + if (iter->object == runner) { + // Fixup next pointer + if (prev != nullptr) + prev->next = iter->next; + else + _runners_start = iter->next; + + // delete + delete iter; + return; + } - // create new entry as start, linked to the previous start - _runners_start = new runner_entry{ runner, first }; + // move on to next entry + prev = iter; + iter = iter->next; } +} - void globals_impl::remove_runner(const runner_impl* runner) - { - // iterate linked list - runner_entry* prev = nullptr; - auto iter = _runners_start; - while (iter != nullptr) - { - if (iter->object == runner) - { - // Fixup next pointer - if (prev != nullptr) - prev->next = iter->next; - else - _runners_start = iter->next; - - // delete - delete iter; - return; - } - - // move on to next entry - prev = iter; - iter = iter->next; - } +void globals_impl::set_variable(hash_t name, const value& val) +{ + ink::optional old_var = ink::nullopt; + value* p_old_var = get_variable(name); + if (p_old_var != nullptr) { + old_var = *p_old_var; } - void globals_impl::set_variable(hash_t name, const value& val) - { - ink::optional old_var = ink::nullopt; - value* p_old_var = get_variable(name); - if (p_old_var != nullptr) { - old_var = *p_old_var; - } - - _variables.set(name, val); + _variables.set(name, val); - for(auto& callback : _callbacks) { - if (callback.name == name) { - if (old_var.has_value()) { - callback.operation->call(val.to_interface_value(lists()), {old_var->to_interface_value(lists())}); - } else { - callback.operation->call(val.to_interface_value(lists()), ink::nullopt); - } + for (auto& callback : _callbacks) { + if (callback.name == name) { + if (old_var.has_value()) { + callback.operation->call( + val.to_interface_value(lists()), {old_var->to_interface_value(lists())} + ); + } else { + callback.operation->call(val.to_interface_value(lists()), ink::nullopt); } } } +} - const value* globals_impl::get_variable(hash_t name) const - { - return _variables.get(name); - } +const value* globals_impl::get_variable(hash_t name) const { return _variables.get(name); } + +value* globals_impl::get_variable(hash_t name) { return _variables.get(name); } - value* globals_impl::get_variable(hash_t name) { - return _variables.get(name); +optional globals_impl::get_var(hash_t name) const +{ + auto* var = get_variable(name); + if (! var) { + return nullopt; } + return {var->to_interface_value(_lists)}; +} - optional globals_impl::get_var(hash_t name) const { - auto* var = get_variable(name); - if (!var) { return nullopt; } - return {var->to_interface_value(_lists)}; +bool globals_impl::set_var(hash_t name, const ink::runtime::value& val) +{ + auto* var = get_variable(name); + if (! var) { + return false; } - - bool globals_impl::set_var(hash_t name, const ink::runtime::value& val) { - auto* var = get_variable(name); - if (!var) { return false; } - ink::runtime::value old_val = var->to_interface_value(lists()); - - bool ret = false; - if ( val.type == ink::runtime::value::Type::String) { - if (!(var->type() == value_type::none || var->type() == value_type::string)) { return false; } - size_t size = 0; - char* ptr; - for ( const char* i = val.v_string; *i; ++i ) { ++size; } - char* new_string = strings().create( size + 1 ); - strings().mark_used( new_string ); - ptr = new_string; - for ( const char* i = val.v_string; *i; ++i ) { - *ptr++ = *i; - } - *ptr = 0; - *var = value{}.set( static_cast( new_string ), true ); - ret = true; - } else { - ret = var->set(val); - } + ink::runtime::value old_val = var->to_interface_value(lists()); - for (auto& callback : _callbacks) { - if (callback.name == name) { - callback.operation->call(val, {old_val}); - } + bool ret = false; + if (val.type == ink::runtime::value::Type::String) { + if (! (var->type() == value_type::none || var->type() == value_type::string)) { + return false; + } + size_t size = 0; + char* ptr; + for (const char* i = val.v_string; *i; ++i) { + ++size; + } + char* new_string = strings().create(size + 1); + strings().mark_used(new_string); + ptr = new_string; + for (const char* i = val.v_string; *i; ++i) { + *ptr++ = *i; } - - return ret; + *ptr = 0; + *var = value{}.set(static_cast(new_string), true); + ret = true; + } else { + ret = var->set(val); } - void globals_impl::internal_observe(hash_t name, callback_base* callback) { - _callbacks.push() = Callback { .name = name, .operation = callback }; - if (_globals_initialized) { - value* p_var = _variables.get(name); - inkAssert(p_var != nullptr, "Global variable to observe does not exists after initiliazation. This variable will therofe not get any value."); - callback->call(p_var->to_interface_value(lists()), ink::nullopt); + for (auto& callback : _callbacks) { + if (callback.name == name) { + callback.operation->call(val, {old_val}); } } - void globals_impl::initialize_globals(runner_impl* run) - { - // If no way to move there, then there are no globals. - if (!run->move_to(hash_string("global decl"))) - { - return; - } + return ret; +} - // execute one line to startup the globals - run->getline_silent(); - _globals_initialized = true; +void globals_impl::internal_observe(hash_t name, callback_base* callback) +{ + _callbacks.push() = Callback{.name = name, .operation = callback}; + if (_globals_initialized) { + value* p_var = _variables.get(name); + inkAssert( + p_var != nullptr, + "Global variable to observe does not exists after initiliazation. This variable will " + "therofe not get any value." + ); + callback->call(p_var->to_interface_value(lists()), ink::nullopt); } +} - void globals_impl::gc() - { - // Mark all strings as unused - _strings.clear_usage(); - _lists.clear_usage(); - - // Iterate runners and mark their strings - auto iter = _runners_start; - while (iter != nullptr) - { - iter->object->mark_used(_strings, _lists); - iter = iter->next; - } +void globals_impl::initialize_globals(runner_impl* run) +{ + // If no way to move there, then there are no globals. + if (! run->move_to(hash_string("global decl"))) { + return; + } - // Mark our own strings - _variables.mark_used(_strings, _lists); + // execute one line to startup the globals + run->getline_silent(); + _globals_initialized = true; +} - // run garbage collection - _strings.gc(); - _lists.gc(); +void globals_impl::gc() +{ + // Mark all strings as unused + _strings.clear_usage(); + _lists.clear_usage(); + + // Iterate runners and mark their strings + auto iter = _runners_start; + while (iter != nullptr) { + iter->object->mark_used(_strings, _lists); + iter = iter->next; } - void globals_impl::save() - { - _variables.save(); - } + // Mark our own strings + _variables.mark_used(_strings, _lists); - void globals_impl::restore() - { - _variables.restore(); - } + // run garbage collection + _strings.gc(); + _lists.gc(); +} - void globals_impl::forget() - { - _variables.forget(); +void globals_impl::save() +{ + for (int i = 0; i < _num_containers; ++i) { + _visit_counts_backup[i] = _visit_counts[i]; } + _variables.save(); +} - snapshot* globals_impl::create_snapshot() const - { - return new snapshot_impl(*this); +void globals_impl::restore() +{ + for (int i = 0; i < _num_containers; ++i) { + _visit_counts[i] = _visit_counts_backup[i]; } + _variables.restore(); +} - size_t globals_impl::snap(unsigned char* data, const snapper& snapper) const - { - unsigned char* ptr = data; - inkAssert(_num_containers == _visit_counts.size(), "Should be equal!"); - inkAssert(_globals_initialized, "Only support snapshot of globals with runner! or you don't need a snapshot for this state"); - ptr += _visit_counts.snap( data ? ptr : nullptr, snapper ); - ptr += _strings.snap( data ? ptr : nullptr, snapper ); - ptr += _lists.snap( data ? ptr : nullptr, snapper ); - ptr += _variables.snap(data ? ptr : nullptr, snapper ); - return ptr - data; - } +void globals_impl::forget() { _variables.forget(); } - const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loader& loader) - { - _globals_initialized = true; - ptr = _visit_counts.snap_load(ptr, loader); - inkAssert(_num_containers == _visit_counts.size(), "errer when loading visit counts, story file dont match snapshot!"); - ptr = _strings.snap_load(ptr, loader); - ptr = _lists.snap_load(ptr, loader); - ptr = _variables.snap_load(ptr, loader); - return ptr; - } +snapshot* globals_impl::create_snapshot() const { return new snapshot_impl(*this); } + +size_t globals_impl::snap(unsigned char* data, const snapper& snapper) const +{ + unsigned char* ptr = data; + inkAssert(_num_containers == _visit_counts.size(), "Should be equal!"); + inkAssert( + _globals_initialized, + "Only support snapshot of globals with runner! or you don't need a snapshot for this state" + ); + ptr += _visit_counts.snap(data ? ptr : nullptr, snapper); + ptr += _strings.snap(data ? ptr : nullptr, snapper); + ptr += _lists.snap(data ? ptr : nullptr, snapper); + ptr += _variables.snap(data ? ptr : nullptr, snapper); + return ptr - data; +} + +const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loader& loader) +{ + _globals_initialized = true; + ptr = _visit_counts.snap_load(ptr, loader); + inkAssert( + _num_containers == _visit_counts.size(), + "errer when loading visit counts, story file dont match snapshot!" + ); + ptr = _strings.snap_load(ptr, loader); + ptr = _lists.snap_load(ptr, loader); + ptr = _variables.snap_load(ptr, loader); + return ptr; } +} // namespace ink::runtime::internal diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index c2d4ff23..2913a48a 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -13,120 +13,131 @@ namespace ink::runtime::internal { - class story_impl; - class runner_impl; - class snapshot_impl; - - // Implementation of the global store - class globals_impl final : public globals_interface, public snapshot_interface - { - friend snapshot_impl; - public: - size_t snap(unsigned char* data, const snapper&) const; - const unsigned char* snap_load(const unsigned char* data, const loader&); - // Initializes a new global store from the given story - globals_impl(const story_impl*); - virtual ~globals_impl() { } - - snapshot* create_snapshot() const override; - - protected: - optional get_var(hash_t name) const override; - bool set_var(hash_t name, const ink::runtime::value& val) override; - void internal_observe(hash_t name, internal::callback_base* callback) override; - - 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 - 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 - uint32_t turns(uint32_t container_id) const; - - // signal that a turn is habbend (eg. a choice is taken) - void turn(); - - // registers/unregisters a runner as using this globals object - void add_runner(const runner_impl*); - void remove_runner(const runner_impl*); - - // sets a global variable - void set_variable(hash_t name, const value&); - - // gets a global variable - const value* get_variable(hash_t name) const; - value* get_variable(hash_t name); - - // checks if globals are initialized - bool are_globals_initialized() const { return _globals_initialized; } - - // initializes globals using a runner - void initialize_globals(runner_impl*); - - // gets the allocated string table - inline string_table& strings() { return _strings; } - inline const string_table& strings() const { return _strings; } - - // gets list entries - list_table& lists() { return _lists; } - - // run garbage collection - void gc(); - - // == Save/Restore == - void save(); - void restore(); - void forget(); - - private: - // 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; - int32_t turns = -1; - bool operator==(const visit_count& vc) const { - return visits == vc.visits && turns == vc.turns; - } - bool operator!=(const visit_count& vc) const { - return !(*this == vc); - } - }; - managed_array _visit_counts; - - // Pointer back to owner story. - const story_impl* const _owner; - - struct runner_entry +class story_impl; +class runner_impl; +class snapshot_impl; + +// Implementation of the global store +class globals_impl final + : public globals_interface + , public snapshot_interface +{ + friend snapshot_impl; + +public: + size_t snap(unsigned char* data, const snapper&) const; + const unsigned char* snap_load(const unsigned char* data, const loader&); + // Initializes a new global store from the given story + globals_impl(const story_impl*); + + virtual ~globals_impl() {} + + snapshot* create_snapshot() const override; + +protected: + optional get_var(hash_t name) const override; + bool set_var(hash_t name, const ink::runtime::value& val) override; + void internal_observe(hash_t name, internal::callback_base* callback) override; + +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 + 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 + uint32_t turns(uint32_t container_id) const; + + // signal that a turn is habbend (eg. a choice is taken) + void turn(); + + // registers/unregisters a runner as using this globals object + void add_runner(const runner_impl*); + void remove_runner(const runner_impl*); + + // sets a global variable + void set_variable(hash_t name, const value&); + + // gets a global variable + const value* get_variable(hash_t name) const; + value* get_variable(hash_t name); + + // checks if globals are initialized + bool are_globals_initialized() const { return _globals_initialized; } + + // initializes globals using a runner + void initialize_globals(runner_impl*); + + // gets the allocated string table + inline string_table& strings() { return _strings; } + + inline const string_table& strings() const { return _strings; } + + // gets list entries + list_table& lists() { return _lists; } + + // run garbage collection + void gc(); + + // == Save/Restore == + void save(); + void restore(); + void forget(); + +private: + // 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; + int32_t turns = -1; + + bool operator==(const visit_count& vc) const { - const runner_impl* object; - runner_entry* next = nullptr; - }; - - // Linked list of runners operating under this globals object. - // Used for garbage collection of the global string table. - runner_entry* _runners_start; - - // Allocated string table (shared by all runners using this global store) - mutable string_table _strings; - mutable list_table _lists; - - // Implemented as a stack (slow lookup) because it has save/restore functionality. - // If I could create an avl tree with save/restore, that'd be great but seems super complex. - internal::stack _variables; - struct Callback { - hash_t name; - callback_base* operation; - }; - managed_array _callbacks; - bool _globals_initialized; + return visits == vc.visits && turns == vc.turns; + } + + bool operator!=(const visit_count& vc) const { return ! (*this == vc); } }; -} + + managed_array _visit_counts; + managed_array _visit_counts_backup; + + // Pointer back to owner story. + const story_impl* const _owner; + + struct runner_entry { + const runner_impl* object; + runner_entry* next = nullptr; + }; + + // Linked list of runners operating under this globals object. + // Used for garbage collection of the global string table. + runner_entry* _runners_start; + + // Allocated string table (shared by all runners using this global store) + mutable string_table _strings; + mutable list_table _lists; + + // Implemented as a stack (slow lookup) because it has save/restore functionality. + // If I could create an avl tree with save/restore, that'd be great but seems super complex. + internal::stack < abs(config::limitGlobalVariables), config::limitGlobalVariables<0> _variables; + + struct Callback { + hash_t name; + callback_base* operation; + }; + + managed_array < Callback, + config::limitGlobalVariableObservers<0, abs(config::limitGlobalVariableObservers)> _callbacks; + bool _globals_initialized; +}; +} // namespace ink::runtime::internal diff --git a/inkcpp/include/functional.h b/inkcpp/include/functional.h index 0cbc145e..0959948a 100644 --- a/inkcpp/include/functional.h +++ b/inkcpp/include/functional.h @@ -114,9 +114,15 @@ class callback final : public callback_base class function_base { public: + function_base(bool lookaheadSafe) + : _lookaheadSafe(lookaheadSafe) + { + } + virtual ~function_base() {} // calls the underlying function object taking parameters from a stack + // TODO: remove ? #ifdef INK_ENABLE_UNREAL virtual void call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists) @@ -127,7 +133,10 @@ class function_base = 0; #endif + bool lookaheadSafe() const { return _lookaheadSafe; } + protected: + bool _lookaheadSafe; // used to hide basic_eval_stack and value definitions template static T pop(basic_eval_stack* stack, list_table& lists); @@ -150,8 +159,9 @@ template class function : public function_base { public: - function(F functor) - : functor(functor) + function(F functor, bool lookaheadSafe) + : function_base(lookaheadSafe) + , functor(functor) { } @@ -265,8 +275,9 @@ template class function_array_delegate : public function_base { public: - function_array_delegate(const D& del) - : invocableDelegate(del) + function_array_delegate(const D& del, bool lookaheadSafe) + : function_baes(lookaheadSafe) + , invocableDelegate(del) { } diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 82e1e0b1..06265c1a 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -187,11 +187,14 @@ class runner_interface * this provides a generic way to bind functions with abitrary length * @param name name hash * @param function callable + * @param lookaheadSafe if false stop glue lookahead if encounter this function + * this prevents double execution of external functions but can lead to + * missing glues */ template - inline void bind(hash_t name, F function) + inline void bind(hash_t name, F function, bool lookaheadSafe = false) { - internal_bind(name, new internal::function(function)); + internal_bind(name, new internal::function(function, lookaheadSafe)); } /** @@ -202,18 +205,21 @@ class runner_interface * * @param name name string * @param function callable + * @param lookaheadSafe if false stop glue lookahead if encounter this function + * this prevents double execution of external functions but can lead to + * missing glues */ template - inline void bind(const char* name, F function) + inline void bind(const char* name, F function, bool lookaheadSafe = false) { - bind(ink::hash_string(name), function); + bind(ink::hash_string(name), function, lookaheadSafe); } #ifdef INK_ENABLE_UNREAL template - void bind_delegate(hash_t name, D functionDelegate) + void bind_delegate(hash_t name, D functionDelegate, bool lookaheadSafe) { - internal_bind(name, new internal::function_array_delegate(functionDelegate)); + internal_bind(name, new internal::function_array_delegate(functionDelegate, lookaheadSafe)); } #endif diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 09075bf6..c8bcb823 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -211,10 +211,7 @@ void basic_stream::get(value* ptr, size_t length) _size = start; } -bool basic_stream::has_marker() const -{ - return entries_since_marker() >= 0; -} +bool basic_stream::has_marker() const { return entries_since_marker() >= 0; } int basic_stream::entries_since_marker() const { @@ -419,6 +416,8 @@ bool basic_stream::text_past_save() const // TODO: Cache what counts as whitespace? if (! is_whitespace(d.get(), false)) return true; + } else if (d.printable()) { + return true; } } diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 26a62585..24043299 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -17,10 +17,7 @@ const choice* runner_interface::get_choice(size_t index) const return begin() + index; } -size_t runner_interface::num_choices() const -{ - return end() - begin(); -} +size_t runner_interface::num_choices() const { return end() - begin(); } } // namespace ink::runtime namespace ink::runtime::internal @@ -520,10 +517,7 @@ void runner_impl::advance_line() _globals->gc(); } -bool runner_impl::can_continue() const -{ - return _ptr != nullptr; -} +bool runner_impl::can_continue() const { return _ptr != nullptr; } void runner_impl::choose(size_t index) { @@ -571,10 +565,7 @@ void runner_impl::getline_silent() _output.clear(); } -bool runner_impl::has_tags() const -{ - return num_tags() > 0; -} +bool runner_impl::has_tags() const { return num_tags() > 0; } size_t runner_impl::num_tags() const { @@ -587,10 +578,7 @@ const char* runner_impl::get_tag(size_t index) const return _tags[index]; } -snapshot* runner_impl::create_snapshot() const -{ - return _globals->create_snapshot(); -} +snapshot* runner_impl::create_snapshot() const { return _globals->create_snapshot(); } size_t runner_impl::snap(unsigned char* data, snapper& snapper) const { @@ -758,8 +746,10 @@ bool runner_impl::line_step() if (_ptr != nullptr) { // Save a snapshot of the current runtime state so we // can return here if we end up hitting a new line - forget(); - save(); + // forget(); + if (! _saved) { + save(); + } } // Otherwise, make sure we don't have any snapshots hanging around // expect we are in choice handleing @@ -859,7 +849,9 @@ void runner_impl::step() if (_evaluation_mode) { _eval.push(values::newline); } else { - _output << values::newline; + if (! _output.ends_with(value_type::newline)) { + _output << values::newline; + } } } break; case Command::GLUE: { @@ -1042,13 +1034,14 @@ void runner_impl::step() int numArguments = ( int ) flag; // find and execute. will automatically push a valid if applicable - bool success = _functions.call( - functionName, &_eval, numArguments, _globals->strings(), _globals->lists() - ); - - // If we failed, notify a potential fallback function - if (! success) { + auto* fn = _functions.find(functionName); + if (fn == nullptr) { _eval.push(values::ex_fn_not_found); + } else if (_output.saved() && _output.saved_ends_with(value_type::newline) && ! fn->lookaheadSafe()) { + // TODO: seperate token? + _output.append(values::newline); + } else { + fn->call(&_eval, numArguments, _globals->strings(), _globals->lists()); } } break; diff --git a/inkcpp_py/CMakeLists.txt b/inkcpp_py/CMakeLists.txt index 1d9f4ce1..91b494f4 100644 --- a/inkcpp_py/CMakeLists.txt +++ b/inkcpp_py/CMakeLists.txt @@ -1,5 +1,5 @@ add_subdirectory(pybind11) -pybind11_add_module(inkcpp_py module.cpp) +pybind11_add_module(inkcpp_py src/module.cpp) target_compile_definitions(inkcpp_py PRIVATE VERSION_INFO=${VERSION}) target_link_libraries(inkcpp_py PUBLIC inkcpp inkcpp_compiler inkcpp_shared) diff --git a/inkcpp_py/sources.json b/inkcpp_py/sources.json deleted file mode 100644 index 71069dce..00000000 --- a/inkcpp_py/sources.json +++ /dev/null @@ -1,362 +0,0 @@ -[ -"shared/", -"shared/CMakeLists.txt", -"shared/private", -"shared/private/command.h", -"shared/private/header.h", -"shared/public", -"shared/public/config.h", -"shared/public/system.h", -"shared/public/version.h", -"inkcpp/", -"inkcpp/CMakeLists.txt", -"inkcpp/array.h", -"inkcpp/avl_array.h", -"inkcpp/casting.h", -"inkcpp/choice.cpp", -"inkcpp/collections", -"inkcpp/collections/restorable.cpp", -"inkcpp/collections/restorable.h", -"inkcpp/container_operations.cpp", -"inkcpp/container_operations.h", -"inkcpp/executioner.h", -"inkcpp/functional.cpp", -"inkcpp/functions.cpp", -"inkcpp/functions.h", -"inkcpp/globals_impl.cpp", -"inkcpp/globals_impl.h", -"inkcpp/header.cpp", -"inkcpp/include", -"inkcpp/include/choice.h", -"inkcpp/include/functional.h", -"inkcpp/include/globals.h", -"inkcpp/include/list.h", -"inkcpp/include/runner.h", -"inkcpp/include/snapshot.h", -"inkcpp/include/story.h", -"inkcpp/include/story_ptr.h", -"inkcpp/include/traits.h", -"inkcpp/include/types.h", -"inkcpp/list_impl.cpp", -"inkcpp/list_impl.h", -"inkcpp/list_operations.cpp", -"inkcpp/list_operations.h", -"inkcpp/list_table.cpp", -"inkcpp/list_table.h", -"inkcpp/numeric_operations.cpp", -"inkcpp/numeric_operations.h", -"inkcpp/operation_bases.h", -"inkcpp/operations.h", -"inkcpp/output.cpp", -"inkcpp/output.h", -"inkcpp/platform.h", -"inkcpp/random.h", -"inkcpp/runner_impl.cpp", -"inkcpp/runner_impl.h", -"inkcpp/simple_restorable_stack.h", -"inkcpp/snapshot_impl.cpp", -"inkcpp/snapshot_impl.h", -"inkcpp/snapshot_interface.h", -"inkcpp/stack.cpp", -"inkcpp/stack.h", -"inkcpp/story_impl.cpp", -"inkcpp/story_impl.h", -"inkcpp/story_ptr.cpp", -"inkcpp/string_operations.cpp", -"inkcpp/string_operations.h", -"inkcpp/string_table.cpp", -"inkcpp/string_table.h", -"inkcpp/string_utils.h", -"inkcpp/system.cpp", -"inkcpp/tuple.hpp", -"inkcpp/value.cpp", -"inkcpp/value.h", -"inkcpp_compiler/", -"inkcpp_compiler/CMakeLists.txt", -"inkcpp_compiler/binary_emitter.cpp", -"inkcpp_compiler/binary_emitter.h", -"inkcpp_compiler/binary_stream.cpp", -"inkcpp_compiler/binary_stream.h", -"inkcpp_compiler/command.cpp", -"inkcpp_compiler/compiler.cpp", -"inkcpp_compiler/emitter.cpp", -"inkcpp_compiler/emitter.h", -"inkcpp_compiler/include", -"inkcpp_compiler/include/compilation_results.h", -"inkcpp_compiler/include/compiler.h", -"inkcpp_compiler/json.hpp", -"inkcpp_compiler/json_compiler.cpp", -"inkcpp_compiler/json_compiler.h", -"inkcpp_compiler/list_data.cpp", -"inkcpp_compiler/list_data.h", -"inkcpp_compiler/reporter.cpp", -"inkcpp_compiler/reporter.h", -"inkcpp_compiler/text_emitter.cpp", -"inkcpp_compiler/text_emitter.h", -"inkcpp_py/", -"inkcpp_py/CMakeLists.txt", -"inkcpp_py/example.py", -"inkcpp_py/module.cpp", -"inkcpp_py/pybind11", -"inkcpp_py/pybind11/CMakeLists.txt", -"inkcpp_py/pybind11/LICENSE", -"inkcpp_py/pybind11/MANIFEST.in", -"inkcpp_py/pybind11/README.rst", -"inkcpp_py/pybind11/SECURITY.md", -"inkcpp_py/pybind11/docs", -"inkcpp_py/pybind11/docs/Doxyfile", -"inkcpp_py/pybind11/docs/Makefile", -"inkcpp_py/pybind11/docs/_static", -"inkcpp_py/pybind11/docs/_static/css", -"inkcpp_py/pybind11/docs/_static/css/custom.css", -"inkcpp_py/pybind11/docs/advanced", -"inkcpp_py/pybind11/docs/advanced/cast", -"inkcpp_py/pybind11/docs/advanced/cast/chrono.rst", -"inkcpp_py/pybind11/docs/advanced/cast/custom.rst", -"inkcpp_py/pybind11/docs/advanced/cast/eigen.rst", -"inkcpp_py/pybind11/docs/advanced/cast/functional.rst", -"inkcpp_py/pybind11/docs/advanced/cast/index.rst", -"inkcpp_py/pybind11/docs/advanced/cast/overview.rst", -"inkcpp_py/pybind11/docs/advanced/cast/stl.rst", -"inkcpp_py/pybind11/docs/advanced/cast/strings.rst", -"inkcpp_py/pybind11/docs/advanced/classes.rst", -"inkcpp_py/pybind11/docs/advanced/embedding.rst", -"inkcpp_py/pybind11/docs/advanced/exceptions.rst", -"inkcpp_py/pybind11/docs/advanced/functions.rst", -"inkcpp_py/pybind11/docs/advanced/misc.rst", -"inkcpp_py/pybind11/docs/advanced/pycpp", -"inkcpp_py/pybind11/docs/advanced/pycpp/index.rst", -"inkcpp_py/pybind11/docs/advanced/pycpp/numpy.rst", -"inkcpp_py/pybind11/docs/advanced/pycpp/object.rst", -"inkcpp_py/pybind11/docs/advanced/pycpp/utilities.rst", -"inkcpp_py/pybind11/docs/advanced/smart_ptrs.rst", -"inkcpp_py/pybind11/docs/basics.rst", -"inkcpp_py/pybind11/docs/benchmark.py", -"inkcpp_py/pybind11/docs/benchmark.rst", -"inkcpp_py/pybind11/docs/changelog.rst", -"inkcpp_py/pybind11/docs/classes.rst", -"inkcpp_py/pybind11/docs/cmake", -"inkcpp_py/pybind11/docs/cmake/index.rst", -"inkcpp_py/pybind11/docs/compiling.rst", -"inkcpp_py/pybind11/docs/conf.py", -"inkcpp_py/pybind11/docs/faq.rst", -"inkcpp_py/pybind11/docs/index.rst", -"inkcpp_py/pybind11/docs/installing.rst", -"inkcpp_py/pybind11/docs/limitations.rst", -"inkcpp_py/pybind11/docs/pybind11-logo.png", -"inkcpp_py/pybind11/docs/pybind11_vs_boost_python1.png", -"inkcpp_py/pybind11/docs/pybind11_vs_boost_python1.svg", -"inkcpp_py/pybind11/docs/pybind11_vs_boost_python2.png", -"inkcpp_py/pybind11/docs/pybind11_vs_boost_python2.svg", -"inkcpp_py/pybind11/docs/reference.rst", -"inkcpp_py/pybind11/docs/release.rst", -"inkcpp_py/pybind11/docs/requirements.txt", -"inkcpp_py/pybind11/docs/upgrade.rst", -"inkcpp_py/pybind11/include", -"inkcpp_py/pybind11/include/pybind11", -"inkcpp_py/pybind11/include/pybind11/attr.h", -"inkcpp_py/pybind11/include/pybind11/buffer_info.h", -"inkcpp_py/pybind11/include/pybind11/cast.h", -"inkcpp_py/pybind11/include/pybind11/chrono.h", -"inkcpp_py/pybind11/include/pybind11/common.h", -"inkcpp_py/pybind11/include/pybind11/complex.h", -"inkcpp_py/pybind11/include/pybind11/detail", -"inkcpp_py/pybind11/include/pybind11/detail/class.h", -"inkcpp_py/pybind11/include/pybind11/detail/common.h", -"inkcpp_py/pybind11/include/pybind11/detail/descr.h", -"inkcpp_py/pybind11/include/pybind11/detail/init.h", -"inkcpp_py/pybind11/include/pybind11/detail/internals.h", -"inkcpp_py/pybind11/include/pybind11/detail/type_caster_base.h", -"inkcpp_py/pybind11/include/pybind11/detail/typeid.h", -"inkcpp_py/pybind11/include/pybind11/eigen.h", -"inkcpp_py/pybind11/include/pybind11/eigen", -"inkcpp_py/pybind11/include/pybind11/eigen/common.h", -"inkcpp_py/pybind11/include/pybind11/eigen/matrix.h", -"inkcpp_py/pybind11/include/pybind11/eigen/tensor.h", -"inkcpp_py/pybind11/include/pybind11/embed.h", -"inkcpp_py/pybind11/include/pybind11/eval.h", -"inkcpp_py/pybind11/include/pybind11/functional.h", -"inkcpp_py/pybind11/include/pybind11/gil.h", -"inkcpp_py/pybind11/include/pybind11/iostream.h", -"inkcpp_py/pybind11/include/pybind11/numpy.h", -"inkcpp_py/pybind11/include/pybind11/operators.h", -"inkcpp_py/pybind11/include/pybind11/options.h", -"inkcpp_py/pybind11/include/pybind11/pybind11.h", -"inkcpp_py/pybind11/include/pybind11/pytypes.h", -"inkcpp_py/pybind11/include/pybind11/stl.h", -"inkcpp_py/pybind11/include/pybind11/stl", -"inkcpp_py/pybind11/include/pybind11/stl/filesystem.h", -"inkcpp_py/pybind11/include/pybind11/stl_bind.h", -"inkcpp_py/pybind11/include/pybind11/type_caster_pyobject_ptr.h", -"inkcpp_py/pybind11/noxfile.py", -"inkcpp_py/pybind11/pybind11", -"inkcpp_py/pybind11/pybind11/__init__.py", -"inkcpp_py/pybind11/pybind11/__main__.py", -"inkcpp_py/pybind11/pybind11/_version.py", -"inkcpp_py/pybind11/pybind11/commands.py", -"inkcpp_py/pybind11/pybind11/py.typed", -"inkcpp_py/pybind11/pybind11/setup_helpers.py", -"inkcpp_py/pybind11/pyproject.toml", -"inkcpp_py/pybind11/setup.cfg", -"inkcpp_py/pybind11/setup.py", -"inkcpp_py/pybind11/tests", -"inkcpp_py/pybind11/tests/CMakeLists.txt", -"inkcpp_py/pybind11/tests/conftest.py", -"inkcpp_py/pybind11/tests/constructor_stats.h", -"inkcpp_py/pybind11/tests/cross_module_gil_utils.cpp", -"inkcpp_py/pybind11/tests/cross_module_interleaved_error_already_set.cpp", -"inkcpp_py/pybind11/tests/eigen_tensor_avoid_stl_array.cpp", -"inkcpp_py/pybind11/tests/env.py", -"inkcpp_py/pybind11/tests/extra_python_package", -"inkcpp_py/pybind11/tests/extra_python_package/pytest.ini", -"inkcpp_py/pybind11/tests/extra_python_package/test_files.py", -"inkcpp_py/pybind11/tests/extra_setuptools", -"inkcpp_py/pybind11/tests/extra_setuptools/pytest.ini", -"inkcpp_py/pybind11/tests/extra_setuptools/test_setuphelper.py", -"inkcpp_py/pybind11/tests/local_bindings.h", -"inkcpp_py/pybind11/tests/object.h", -"inkcpp_py/pybind11/tests/pybind11_cross_module_tests.cpp", -"inkcpp_py/pybind11/tests/pybind11_tests.cpp", -"inkcpp_py/pybind11/tests/pybind11_tests.h", -"inkcpp_py/pybind11/tests/pytest.ini", -"inkcpp_py/pybind11/tests/requirements.txt", -"inkcpp_py/pybind11/tests/test_async.cpp", -"inkcpp_py/pybind11/tests/test_async.py", -"inkcpp_py/pybind11/tests/test_buffers.cpp", -"inkcpp_py/pybind11/tests/test_buffers.py", -"inkcpp_py/pybind11/tests/test_builtin_casters.cpp", -"inkcpp_py/pybind11/tests/test_builtin_casters.py", -"inkcpp_py/pybind11/tests/test_call_policies.cpp", -"inkcpp_py/pybind11/tests/test_call_policies.py", -"inkcpp_py/pybind11/tests/test_callbacks.cpp", -"inkcpp_py/pybind11/tests/test_callbacks.py", -"inkcpp_py/pybind11/tests/test_chrono.cpp", -"inkcpp_py/pybind11/tests/test_chrono.py", -"inkcpp_py/pybind11/tests/test_class.cpp", -"inkcpp_py/pybind11/tests/test_class.py", -"inkcpp_py/pybind11/tests/test_cmake_build", -"inkcpp_py/pybind11/tests/test_cmake_build/CMakeLists.txt", -"inkcpp_py/pybind11/tests/test_cmake_build/embed.cpp", -"inkcpp_py/pybind11/tests/test_cmake_build/installed_embed", -"inkcpp_py/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt", -"inkcpp_py/pybind11/tests/test_cmake_build/installed_function", -"inkcpp_py/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt", -"inkcpp_py/pybind11/tests/test_cmake_build/installed_target", -"inkcpp_py/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt", -"inkcpp_py/pybind11/tests/test_cmake_build/main.cpp", -"inkcpp_py/pybind11/tests/test_cmake_build/subdirectory_embed", -"inkcpp_py/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt", -"inkcpp_py/pybind11/tests/test_cmake_build/subdirectory_function", -"inkcpp_py/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt", -"inkcpp_py/pybind11/tests/test_cmake_build/subdirectory_target", -"inkcpp_py/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt", -"inkcpp_py/pybind11/tests/test_cmake_build/test.py", -"inkcpp_py/pybind11/tests/test_const_name.cpp", -"inkcpp_py/pybind11/tests/test_const_name.py", -"inkcpp_py/pybind11/tests/test_constants_and_functions.cpp", -"inkcpp_py/pybind11/tests/test_constants_and_functions.py", -"inkcpp_py/pybind11/tests/test_copy_move.cpp", -"inkcpp_py/pybind11/tests/test_copy_move.py", -"inkcpp_py/pybind11/tests/test_custom_type_casters.cpp", -"inkcpp_py/pybind11/tests/test_custom_type_casters.py", -"inkcpp_py/pybind11/tests/test_custom_type_setup.cpp", -"inkcpp_py/pybind11/tests/test_custom_type_setup.py", -"inkcpp_py/pybind11/tests/test_docstring_options.cpp", -"inkcpp_py/pybind11/tests/test_docstring_options.py", -"inkcpp_py/pybind11/tests/test_eigen_matrix.cpp", -"inkcpp_py/pybind11/tests/test_eigen_matrix.py", -"inkcpp_py/pybind11/tests/test_eigen_tensor.cpp", -"inkcpp_py/pybind11/tests/test_eigen_tensor.inl", -"inkcpp_py/pybind11/tests/test_eigen_tensor.py", -"inkcpp_py/pybind11/tests/test_embed", -"inkcpp_py/pybind11/tests/test_embed/CMakeLists.txt", -"inkcpp_py/pybind11/tests/test_embed/catch.cpp", -"inkcpp_py/pybind11/tests/test_embed/external_module.cpp", -"inkcpp_py/pybind11/tests/test_embed/test_interpreter.cpp", -"inkcpp_py/pybind11/tests/test_embed/test_interpreter.py", -"inkcpp_py/pybind11/tests/test_embed/test_trampoline.py", -"inkcpp_py/pybind11/tests/test_enum.cpp", -"inkcpp_py/pybind11/tests/test_enum.py", -"inkcpp_py/pybind11/tests/test_eval.cpp", -"inkcpp_py/pybind11/tests/test_eval.py", -"inkcpp_py/pybind11/tests/test_eval_call.py", -"inkcpp_py/pybind11/tests/test_exceptions.cpp", -"inkcpp_py/pybind11/tests/test_exceptions.h", -"inkcpp_py/pybind11/tests/test_exceptions.py", -"inkcpp_py/pybind11/tests/test_factory_constructors.cpp", -"inkcpp_py/pybind11/tests/test_factory_constructors.py", -"inkcpp_py/pybind11/tests/test_gil_scoped.cpp", -"inkcpp_py/pybind11/tests/test_gil_scoped.py", -"inkcpp_py/pybind11/tests/test_iostream.cpp", -"inkcpp_py/pybind11/tests/test_iostream.py", -"inkcpp_py/pybind11/tests/test_kwargs_and_defaults.cpp", -"inkcpp_py/pybind11/tests/test_kwargs_and_defaults.py", -"inkcpp_py/pybind11/tests/test_local_bindings.cpp", -"inkcpp_py/pybind11/tests/test_local_bindings.py", -"inkcpp_py/pybind11/tests/test_methods_and_attributes.cpp", -"inkcpp_py/pybind11/tests/test_methods_and_attributes.py", -"inkcpp_py/pybind11/tests/test_modules.cpp", -"inkcpp_py/pybind11/tests/test_modules.py", -"inkcpp_py/pybind11/tests/test_multiple_inheritance.cpp", -"inkcpp_py/pybind11/tests/test_multiple_inheritance.py", -"inkcpp_py/pybind11/tests/test_numpy_array.cpp", -"inkcpp_py/pybind11/tests/test_numpy_array.py", -"inkcpp_py/pybind11/tests/test_numpy_dtypes.cpp", -"inkcpp_py/pybind11/tests/test_numpy_dtypes.py", -"inkcpp_py/pybind11/tests/test_numpy_vectorize.cpp", -"inkcpp_py/pybind11/tests/test_numpy_vectorize.py", -"inkcpp_py/pybind11/tests/test_opaque_types.cpp", -"inkcpp_py/pybind11/tests/test_opaque_types.py", -"inkcpp_py/pybind11/tests/test_operator_overloading.cpp", -"inkcpp_py/pybind11/tests/test_operator_overloading.py", -"inkcpp_py/pybind11/tests/test_pickling.cpp", -"inkcpp_py/pybind11/tests/test_pickling.py", -"inkcpp_py/pybind11/tests/test_pytypes.cpp", -"inkcpp_py/pybind11/tests/test_pytypes.py", -"inkcpp_py/pybind11/tests/test_sequences_and_iterators.cpp", -"inkcpp_py/pybind11/tests/test_sequences_and_iterators.py", -"inkcpp_py/pybind11/tests/test_smart_ptr.cpp", -"inkcpp_py/pybind11/tests/test_smart_ptr.py", -"inkcpp_py/pybind11/tests/test_stl.cpp", -"inkcpp_py/pybind11/tests/test_stl.py", -"inkcpp_py/pybind11/tests/test_stl_binders.cpp", -"inkcpp_py/pybind11/tests/test_stl_binders.py", -"inkcpp_py/pybind11/tests/test_tagbased_polymorphic.cpp", -"inkcpp_py/pybind11/tests/test_tagbased_polymorphic.py", -"inkcpp_py/pybind11/tests/test_thread.cpp", -"inkcpp_py/pybind11/tests/test_thread.py", -"inkcpp_py/pybind11/tests/test_type_caster_pyobject_ptr.cpp", -"inkcpp_py/pybind11/tests/test_type_caster_pyobject_ptr.py", -"inkcpp_py/pybind11/tests/test_union.cpp", -"inkcpp_py/pybind11/tests/test_union.py", -"inkcpp_py/pybind11/tests/test_unnamed_namespace_a.cpp", -"inkcpp_py/pybind11/tests/test_unnamed_namespace_a.py", -"inkcpp_py/pybind11/tests/test_unnamed_namespace_b.cpp", -"inkcpp_py/pybind11/tests/test_unnamed_namespace_b.py", -"inkcpp_py/pybind11/tests/test_vector_unique_ptr_member.cpp", -"inkcpp_py/pybind11/tests/test_vector_unique_ptr_member.py", -"inkcpp_py/pybind11/tests/test_virtual_functions.cpp", -"inkcpp_py/pybind11/tests/test_virtual_functions.py", -"inkcpp_py/pybind11/tests/valgrind-numpy-scipy.supp", -"inkcpp_py/pybind11/tests/valgrind-python.supp", -"inkcpp_py/pybind11/tools", -"inkcpp_py/pybind11/tools/FindCatch.cmake", -"inkcpp_py/pybind11/tools/FindEigen3.cmake", -"inkcpp_py/pybind11/tools/FindPythonLibsNew.cmake", -"inkcpp_py/pybind11/tools/JoinPaths.cmake", -"inkcpp_py/pybind11/tools/check-style.sh", -"inkcpp_py/pybind11/tools/cmake_uninstall.cmake.in", -"inkcpp_py/pybind11/tools/codespell_ignore_lines_from_errors.py", -"inkcpp_py/pybind11/tools/libsize.py", -"inkcpp_py/pybind11/tools/make_changelog.py", -"inkcpp_py/pybind11/tools/pybind11.pc.in", -"inkcpp_py/pybind11/tools/pybind11Common.cmake", -"inkcpp_py/pybind11/tools/pybind11Config.cmake.in", -"inkcpp_py/pybind11/tools/pybind11NewTools.cmake", -"inkcpp_py/pybind11/tools/pybind11Tools.cmake", -"inkcpp_py/pybind11/tools/pyproject.toml", -"inkcpp_py/pybind11/tools/setup_global.py.in", -"inkcpp_py/pybind11/tools/setup_main.py.in", -"inkcpp_py/unreal_example.ink", -"inkcpp_py/sources.json" -] diff --git a/inkcpp_py/module.cpp b/inkcpp_py/src/module.cpp similarity index 94% rename from inkcpp_py/module.cpp rename to inkcpp_py/src/module.cpp index 7bde7e27..7e4e13a9 100644 --- a/inkcpp_py/module.cpp +++ b/inkcpp_py/src/module.cpp @@ -226,22 +226,30 @@ PYBIND11_MODULE(inkcpp_py, m) .def("choose", &runner::choose, "Select a choice to continue") .def( "bind_void", - [](runner& self, const char* function_name, std::function)> f) { - self.bind(function_name, [f](size_t len, const value* vals) { - std::vector args(vals, vals + len); - f(args); - }); + [](runner& self, const char* function_name, std::function)> f, + bool lookaheadSafe) { + self.bind( + function_name, + [f](size_t len, const value* vals) { + std::vector args(vals, vals + len); + f(args); + }, + lookaheadSafe + ); }, + py::arg("function_name"), py::arg("function"), py::arg_v("lookaheadSafe", false), "Bind function which void result" ) .def( "bind", - [](runner& self, const char* function_name, std::function)> f) { + [](runner& self, const char* function_name, std::function)> f, + bool lookaheadSafe) { self.bind(function_name, [f](size_t len, const value* vals) { std::vector args(vals, vals + len); return f(args); }); }, + py::arg("function_name"), py::arg("function"), py::arg_v("lookaheadSafe", false), "Bind a function with return value" ); py::class_(m, "Choice") diff --git a/inkcpp_py/tests/test_ExternalFunctions.py b/inkcpp_py/tests/test_ExternalFunctions.py new file mode 100644 index 00000000..c56518e6 --- /dev/null +++ b/inkcpp_py/tests/test_ExternalFunctions.py @@ -0,0 +1,58 @@ +import inkcpp_py as ink +import pytest +import os + +@pytest.fixture +def inklecate_cmd(): + res = os.getenv("INKLECATE") + if res is None or res == "": + return "inklecate" + return res + +@pytest.fixture +def story_path(tmpdir): + return list(map(lambda x: tmpdir / ("LookaheadSafe" + x), [".bin", ".tmp"])) + ["inkcpp_test/ink/LookaheadSafe.ink"] + +@pytest.fixture +def assets(story_path, inklecate_cmd): + if not os.path.exists(story_path[0]): + if not os.path.exists(story_path[1]): + os.system('{} -o {} {}'.format(inklecate_cmd, story_path[1], story_path[2])) + ink.compile_json(str(story_path[1]), str(story_path[0])) + ink_story = ink.Story.from_file(str(story_path[0])) + ink_globals = ink_story.new_globals(); + ink_runner = ink_story.new_runner(ink_globals) + return [ink_story, ink_globals, ink_runner] + +class Cnt: + def __init__(self): + self.cnt = 0 + def __call__(self, _): + self.cnt += 1 +class TestExternalFunctions: + def test_lookaheadSafe(self, assets): + cnt = Cnt() + assert len(assets) == 3 + [story, globals, runner] = assets + runner.bind_void("foo", cnt, True) + out = runner.getline() + assert out == "Call1 glued to Call 2\n" + assert cnt.cnt == 3 + out = runner.getline() + assert out == "Call 3 is seperated\n" + assert cnt.cnt == 4 + + def test_lookahadeUnsafe(self, assets): + cnt = Cnt() + [story, globals, runner] = assets + runner.bind_void("foo", cnt) + out = runner.getline() + assert out == "Call1\n" + assert cnt.cnt == 1 + out = runner.getline() + assert out == "glued to Call 2\n" + assert cnt.cnt == 2 + out = runner.getline() + assert out == "Call 3 is seperated\n" + assert cnt.cnt == 3 + diff --git a/inkcpp_test/CMakeLists.txt b/inkcpp_test/CMakeLists.txt index 40efe207..c515fd48 100644 --- a/inkcpp_test/CMakeLists.txt +++ b/inkcpp_test/CMakeLists.txt @@ -15,6 +15,9 @@ add_executable(inkcpp_test catch.hpp Main.cpp InkyJson.cpp SpaceAfterBracketChoice.cpp ThirdTierChoiceAfterBrackets.cpp + NoEarlyTags.cpp + ExternalFunctionsExecuteProperly.cpp + LookaheadSafe.cpp ) target_link_libraries(inkcpp_test PUBLIC inkcpp inkcpp_compiler inkcpp_shared) @@ -38,6 +41,6 @@ set (destination "${CMAKE_CURRENT_BINARY_DIR}/ink") add_custom_command( TARGET inkcpp_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink ${source} ${destination} - DEPENDS ${destination} + DEPENDS ${source} COMMENT "symbolic link resources folder from ${source} => ${destination}" ) diff --git a/inkcpp_test/ExternalFunctionsExecuteProperly.cpp b/inkcpp_test/ExternalFunctionsExecuteProperly.cpp new file mode 100644 index 00000000..d2b1187d --- /dev/null +++ b/inkcpp_test/ExternalFunctionsExecuteProperly.cpp @@ -0,0 +1,39 @@ +#include "catch.hpp" +#include "../inkcpp_cl/test.h" + +#include +#include +#include +#include + +using namespace ink::runtime; + +SCENARIO("a story with an external function evaluates the function at the right time", "[story]") +{ + GIVEN("a story with an external function") + { + inklecate("ink/ExternalFunctionsExecuteProperly.ink", "ExternalFunctionsExecuteProperly.tmp"); + ink::compiler::run( + "ExternalFunctionsExecuteProperly.tmp", "ExternalFunctionsExecuteProperly.bin" + ); + auto ink = story::from_file("ExternalFunctionsExecuteProperly.bin"); + runner thread = ink->new_runner(); + + int line_count = 0; + + + thread->bind("GET_LINE_COUNT", [&line_count]() { return line_count; }); + + WHEN("run thread") + { + THEN("thread has correct line counts") + { + while (thread->can_continue()) { + auto line = thread->getline(); + REQUIRE(line == "Line count: " + std::to_string(line_count) + "\n"); + line_count++; + } + } + } + } +} diff --git a/inkcpp_test/FallbackFunction.cpp b/inkcpp_test/FallbackFunction.cpp index 56a07af5..d1a5acca 100644 --- a/inkcpp_test/FallbackFunction.cpp +++ b/inkcpp_test/FallbackFunction.cpp @@ -13,51 +13,68 @@ using namespace ink::runtime; SCENARIO("run a story with external function and fallback function", "[external function]") { - GIVEN("story with two external functions, one with fallback") - { - inklecate("ink/FallBack.ink", "FallBack.tmp"); - ink::compiler::run("FallBack.tmp", "FallBack.bin"); - auto ink = story::from_file("FallBack.bin"); - runner thread = ink->new_runner(); - - WHEN("bind both external functions") - { - int cnt_sqrt = 0; - auto fn_sqrt = [&cnt_sqrt](int x)->int{ ++cnt_sqrt; return sqrt(x); }; - int cnt_greeting = 0; - auto fn_greeting = [&cnt_greeting]()->const char*{++cnt_greeting; return "Hohooh"; }; - - thread->bind("sqrt", fn_sqrt); - thread->bind("greeting", fn_greeting); - - std::string out; - REQUIRE_NOTHROW(out = thread->getall()); - THEN("Both function should be called the correct amount of times") - { - REQUIRE(out == "Hohooh ! A small demonstraion of my power:\n4 * 4 = 16, stunning i would say\n"); - REQUIRE(cnt_sqrt == 2); - REQUIRE(cnt_greeting == 1); - } - } - WHEN("only bind function without fallback") - { - int cnt_sqrt = 0; - auto fn_sqrt = [&cnt_sqrt](int x)->int{++cnt_sqrt; return sqrt(x); }; - - thread ->bind("sqrt", fn_sqrt); - - std::string out; - REQUIRE_NOTHROW(out = thread->getall());; - THEN("Sqrt should be falled twice, and uses default greeting") - { - REQUIRE(out == "Hello ! A small demonstraion of my power:\n4 * 4 = 16, stunning i would say\n"); - REQUIRE(cnt_sqrt == 2); - } - } - WHEN("bind no function") - { - std::string out; - REQUIRE_THROWS_AS(out = thread->getall(), ink::ink_exception); - } - } + GIVEN("story with two external functions, one with fallback") + { + inklecate("ink/FallBack.ink", "FallBack.tmp"); + ink::compiler::run("FallBack.tmp", "FallBack.bin"); + auto ink = story::from_file("FallBack.bin"); + runner thread = ink->new_runner(); + + WHEN("bind both external functions") + { + int cnt_sqrt = 0; + auto fn_sqrt = [&cnt_sqrt](int x) -> int { + ++cnt_sqrt; + return sqrt(x); + }; + int cnt_greeting = 0; + auto fn_greeting = [&cnt_greeting]() -> const char* { + ++cnt_greeting; + return "Hohooh"; + }; + + thread->bind("sqrt", fn_sqrt); + thread->bind("greeting", fn_greeting); + + std::string out; + REQUIRE_NOTHROW(out = thread->getall()); + THEN("Both function should be called the correct amount of times") + { + REQUIRE( + out + == "Hohooh ! A small demonstration of my power:\nMath 4 * 4 = 16, stunning i would " + "say\n" + ); + REQUIRE(cnt_sqrt == 2); + REQUIRE(cnt_greeting == 1); + } + } + WHEN("only bind function without fallback") + { + int cnt_sqrt = 0; + auto fn_sqrt = [&cnt_sqrt](int x) -> int { + ++cnt_sqrt; + return sqrt(x); + }; + + thread->bind("sqrt", fn_sqrt); + + std::string out; + REQUIRE_NOTHROW(out = thread->getall()); + ; + THEN("Sqrt should be falled twice, and uses default greeting") + { + REQUIRE( + out + == "Hello ! A small demonstration of my power:\nMath 4 * 4 = 16, stunning i would say\n" + ); + REQUIRE(cnt_sqrt == 2); + } + } + WHEN("bind no function") + { + std::string out; + REQUIRE_THROWS_AS(out = thread->getall(), ink::ink_exception); + } + } } diff --git a/inkcpp_test/LookaheadSafe.cpp b/inkcpp_test/LookaheadSafe.cpp new file mode 100644 index 00000000..74dbac60 --- /dev/null +++ b/inkcpp_test/LookaheadSafe.cpp @@ -0,0 +1,49 @@ +#include "catch.hpp" +#include "../inkcpp_cl/test.h" + +#include +#include +#include +#include + +using namespace ink::runtime; + +SCENARIO("A story with external functions and glue", "[external]") +{ + GIVEN("The story") + { + inklecate("ink/LookaheadSafe.ink", "LookaheadSafe.tmp"); + ink::compiler::run("LookaheadSafe.tmp", "LookaheadSafe.bin"); + auto ink = story::from_file("LookaheadSafe.bin"); + + int cnt = 0; + auto foo = [&cnt]() { + cnt += 1; + }; + WHEN("the function in lookahead save") + { + auto thread = ink->new_runner(); + thread->bind("foo", foo, true); + std::string out = thread->getline(); + REQUIRE(cnt == 3); + REQUIRE(out == "Call1 glued to Call 2\n"); + out = thread->getline(); + REQUIRE(out == "Call 3 is seperated\n"); + REQUIRE(cnt == 4); + } + WHEN("the function is not lookahead save") + { + auto thread = ink->new_runner(); + thread->bind("foo", foo, false); + std::string out = thread->getline(); + REQUIRE(out == "Call1\n"); + REQUIRE(cnt == 1); + out = thread->getline(); + REQUIRE(out == "glued to Call 2\n"); + REQUIRE(cnt == 2); + out = thread->getline(); + REQUIRE(out == "Call 3 is seperated\n"); + REQUIRE(cnt == 3); + } + } +} diff --git a/inkcpp_test/NoEarlyTags.cpp b/inkcpp_test/NoEarlyTags.cpp new file mode 100644 index 00000000..6351cbc9 --- /dev/null +++ b/inkcpp_test/NoEarlyTags.cpp @@ -0,0 +1,36 @@ +#include "../inkcpp_cl/test.h" +#include "catch.hpp" + +#include +#include +#include +#include + +using namespace ink::runtime; + +SCENARIO("Story with tags and glues", "[glue, tags]") +{ + GIVEN("lines intersected with tags and glue") + { + inklecate("ink/NoEarlyTags.ink", "NoEarlyTags.tmp"); + ink::compiler::run("NoEarlyTags.tmp", "NoEarlyTags.bin"); + auto ink = story::from_file("NoEarlyTags.bin"); + auto thread = ink->new_runner(); + WHEN("no glue") + { + std::string out = thread->getline(); + REQUIRE(out == "Hey there, nice to meet you!\n"); + REQUIRE(thread->num_tags() == 2); + } + WHEN("glue: tags will stop lookahead") + { + thread->getline(); + std::string out = thread->getline(); + REQUIRE(out == "Hey, I'm Hey and this is YOU, nice to meet you too!\n"); + REQUIRE(thread->num_tags() == 3); + out = thread->getline(); + REQUIRE(out == "I'm Do! Most people can't pronounce it, just think 'Kee-vah\".\n"); + REQUIRE(thread->num_tags() == 5); + } + } +} diff --git a/inkcpp_test/Observer.cpp b/inkcpp_test/Observer.cpp index 3b20a51c..f8522308 100644 --- a/inkcpp_test/Observer.cpp +++ b/inkcpp_test/Observer.cpp @@ -9,285 +9,240 @@ using namespace ink::runtime; -SCENARIO( "Observer", "[variables]" ) +SCENARIO("Observer", "[variables]") { - GIVEN( "a story which changes variables" ) + GIVEN("a story which changes variables") { - inklecate( "ink/ObserverStory.ink", "ObserverStory.tmp" ); - ink::compiler::run( "ObserverStory.tmp", "ObserverStory.bin" ); - auto ink = story::from_file( "ObserverStory.bin" ); + inklecate("ink/ObserverStory.ink", "ObserverStory.tmp"); + ink::compiler::run("ObserverStory.tmp", "ObserverStory.bin"); + auto ink = story::from_file("ObserverStory.bin"); auto globals = ink->new_globals(); - runner thread = ink->new_runner( globals ); - WHEN( "Run without observers" ) + runner thread = ink->new_runner(globals); + WHEN("Run without observers") { std::string out = thread->getall(); - REQUIRE( out == R"V(hello line 1 1 -hello line 2 5 -test line 3 5 -)V" ); + REQUIRE(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); } - WHEN( "Run with observers read only, with specific type" ) + WHEN("Run with observers read only, with specific type") { int var1_cnt = 0; - auto var1 = [&var1_cnt]( int32_t i ) { - if ( var1_cnt++ == 0 ) - { - REQUIRE( i == 1 ); - } - else - { - REQUIRE( i == 5 ); - } + auto var1 = [&var1_cnt](int32_t i) { + if (var1_cnt++ == 0) { + REQUIRE(i == 1); + } else { + REQUIRE(i == 5); + } }; int var2_cnt = 0; - auto var2 = [&var2_cnt]( const char* s ) { - std::string str( s ); - if ( var2_cnt++ == 0 ) - { - REQUIRE( str == "hello" ); - } - else - { - REQUIRE( str == "test" ); - } + auto var2 = [&var2_cnt](const char* s) { + std::string str(s); + if (var2_cnt++ == 0) { + REQUIRE(str == "hello"); + } else { + REQUIRE(str == "test"); + } }; - globals->observe( "var1", var1 ); - globals->observe( "var2", var2 ); + globals->observe("var1", var1); + globals->observe("var2", var2); std::string out = thread->getall(); - REQUIRE( out == R"V(hello line 1 1 -hello line 2 5 -test line 3 5 -)V" ); - REQUIRE( var1_cnt == 2 ); - REQUIRE( var2_cnt == 2 ); + REQUIRE(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + REQUIRE(var1_cnt == 2); + REQUIRE(var2_cnt == 2); } - WHEN( "Run with generic observer" ) + WHEN("Run with generic observer") { int var1_cnt = 0; - auto var1 = [&var1_cnt]( value v ) { - REQUIRE(v.type == value::Type::Int32); - if ( var1_cnt++ == 0 ) - { - REQUIRE( v.v_int32 == 1 ); - } - else - { - REQUIRE( v.v_int32 == 5 ); - } + auto var1 = [&var1_cnt](value v) { + REQUIRE(v.type == value::Type::Int32); + if (var1_cnt++ == 0) { + REQUIRE(v.v_int32 == 1); + } else { + REQUIRE(v.v_int32 == 5); + } }; int var2_cnt = 0; - auto var2 = [&var2_cnt]( value v ) { - REQUIRE(v.type == value::Type::String); - std::string str( v.v_string ); - if ( var2_cnt++ == 0 ) - { - REQUIRE( str == "hello" ); - } - else - { - REQUIRE( str == "test" ); - } + auto var2 = [&var2_cnt](value v) { + REQUIRE(v.type == value::Type::String); + std::string str(v.v_string); + if (var2_cnt++ == 0) { + REQUIRE(str == "hello"); + } else { + REQUIRE(str == "test"); + } }; - globals->observe( "var1", var1 ); - globals->observe( "var2", var2 ); + globals->observe("var1", var1); + globals->observe("var2", var2); std::string out = thread->getall(); - REQUIRE( out == R"V(hello line 1 1 -hello line 2 5 -test line 3 5 -)V" ); - REQUIRE( var1_cnt == 2 ); - REQUIRE( var2_cnt == 2 ); + REQUIRE(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + REQUIRE(var1_cnt == 2); + REQUIRE(var2_cnt == 2); } - WHEN( "Bind multiple observer to same variables" ) + WHEN("Bind multiple observer to same variables") { int var1_cnt = 0; - auto var1 = [&var1_cnt]( int32_t i ) { - if ( var1_cnt++ < 2 ) - { - REQUIRE( i == 1 ); - } - else - { - REQUIRE( i == 5 ); - } + auto var1 = [&var1_cnt](int32_t i) { + if (var1_cnt++ < 2) { + REQUIRE(i == 1); + } else { + REQUIRE(i == 5); + } }; - globals->observe( "var1", var1 ); - globals->observe( "var1", var1 ); + globals->observe("var1", var1); + globals->observe("var1", var1); std::string out = thread->getall(); - REQUIRE( out == R"V(hello line 1 1 -hello line 2 5 -test line 3 5 -)V" ); - REQUIRE( var1_cnt == 4 ); + REQUIRE(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + REQUIRE(var1_cnt == 4); } - WHEN( "Run with missmatching type" ) + WHEN("Run with missmatching type") { - auto var1 = []( uint32_t i ) { + auto var1 = [](uint32_t i) { }; - CHECK_THROWS( globals->observe( "var1", var1 ) ); + CHECK_THROWS(globals->observe("var1", var1)); } - WHEN( "Just get pinged" ) + WHEN("Just get pinged") { - int var1_cnt = 0; - auto var1=[&var1_cnt]() { - var1_cnt++; + int var1_cnt = 0; + auto var1 = [&var1_cnt]() { + var1_cnt++; }; globals->observe("var1", var1); std::string out = thread->getall(); - REQUIRE( out == R"V(hello line 1 1 -hello line 2 5 -test line 3 5 -)V" ); - REQUIRE(var1_cnt==2); + REQUIRE(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + REQUIRE(var1_cnt == 2); } WHEN("call with new and old value") { - int var1_cnt = 0; - auto var1=[&var1_cnt](int32_t i, ink::optional o_i) { - if(var1_cnt++==0) { - REQUIRE(i == 1); - REQUIRE_FALSE(o_i.has_value()); - } else { - REQUIRE(i == 5); - REQUIRE(o_i.has_value()); - REQUIRE(o_i.value() == 1); - } + int var1_cnt = 0; + auto var1 = [&var1_cnt](int32_t i, ink::optional o_i) { + if (var1_cnt++ == 0) { + REQUIRE(i == 1); + REQUIRE_FALSE(o_i.has_value()); + } else { + REQUIRE(i == 5); + REQUIRE(o_i.has_value()); + REQUIRE(o_i.value() == 1); + } }; - int var2_cnt = 0; - auto var2 = [&var2_cnt](value v, ink::optional o_v) { - REQUIRE(v.type == value::Type::String); - std::string str(v.v_string); - if(var2_cnt++==0) { - REQUIRE(str == "hello"); - REQUIRE_FALSE(o_v.has_value()); - } else { - REQUIRE(str == "test"); - REQUIRE(o_v.has_value()); - REQUIRE(o_v.value().type == value::Type::String); - std::string str2(o_v.value().v_string); - REQUIRE(str2 == "hello"); - } + int var2_cnt = 0; + auto var2 = [&var2_cnt](value v, ink::optional o_v) { + REQUIRE(v.type == value::Type::String); + std::string str(v.v_string); + if (var2_cnt++ == 0) { + REQUIRE(str == "hello"); + REQUIRE_FALSE(o_v.has_value()); + } else { + REQUIRE(str == "test"); + REQUIRE(o_v.has_value()); + REQUIRE(o_v.value().type == value::Type::String); + std::string str2(o_v.value().v_string); + REQUIRE(str2 == "hello"); + } }; globals->observe("var1", var1); globals->observe("var2", var2); std::string out = thread->getall(); - REQUIRE( out == R"V(hello line 1 1 -hello line 2 5 -test line 3 5 -)V" ); + REQUIRE(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); REQUIRE(var1_cnt == 2); REQUIRE(var2_cnt == 2); } WHEN("Changing Same value at runtime") { - int var1_cnt = 0; - auto var1 = [&var1_cnt,&globals](int32_t i){ - ++var1_cnt; - if(var1_cnt==1) { - REQUIRE(i == 1); - } else if (var1_cnt==2) { - REQUIRE(i == 5); - globals->set("var1", 8); - }else if (var1_cnt==3) { - REQUIRE(i == 8); - } + int var1_cnt = 0; + auto var1 = [&var1_cnt, &globals](int32_t i) { + ++var1_cnt; + if (var1_cnt == 1) { + REQUIRE(i == 1); + } else if (var1_cnt == 2) { + REQUIRE(i == 5); + globals->set("var1", 8); + } else if (var1_cnt == 3) { + REQUIRE(i == 8); + } }; globals->observe("var1", var1); std::string out = thread->getall(); REQUIRE(8 == globals->get("var1").value()); - REQUIRE( out == R"V(hello line 1 1 -hello line 2 8 -test line 3 8 -)V" ); + REQUIRE(out == "hello line 1 1 hello line 2 8 test line 3 8\n"); REQUIRE(var1_cnt == 3); } WHEN("Changing Sam value at bind time") { - int var1_cnt = 0; - auto var1 = [&var1_cnt,&globals](int32_t i){ - ++var1_cnt; - if(var1_cnt==1) { - REQUIRE(i == 1); - globals->set("var1", 8); - } else if (var1_cnt==2) { - REQUIRE(i == 8); - }else if (var1_cnt==3) { - REQUIRE(i == 5); - } + int var1_cnt = 0; + auto var1 = [&var1_cnt, &globals](int32_t i) { + ++var1_cnt; + if (var1_cnt == 1) { + REQUIRE(i == 1); + globals->set("var1", 8); + } else if (var1_cnt == 2) { + REQUIRE(i == 8); + } else if (var1_cnt == 3) { + REQUIRE(i == 5); + } }; globals->observe("var1", var1); std::string out = thread->getall(); REQUIRE(5 == globals->get("var1").value()); - REQUIRE( out == R"V(hello line 1 8 -hello line 2 5 -test line 3 5 -)V" ); + REQUIRE(out == "hello line 1 8 hello line 2 5 test line 3 5\n"); REQUIRE(var1_cnt == 3); - } WHEN("Changing Same value multiple times") { - int var1_cnt = 0; - auto var1 = [&var1_cnt,&globals](int32_t i){ - ++var1_cnt; - if(var1_cnt==1) { - REQUIRE(i == 1); - globals->set("var1", 8); - } else if (var1_cnt==2) { - REQUIRE(i == 8); - globals->set("var1", 10); - } else if (var1_cnt==3){ - REQUIRE(i == 10); - }else if (var1_cnt==4) { - REQUIRE(i == 5); - } + int var1_cnt = 0; + auto var1 = [&var1_cnt, &globals](int32_t i) { + ++var1_cnt; + if (var1_cnt == 1) { + REQUIRE(i == 1); + globals->set("var1", 8); + } else if (var1_cnt == 2) { + REQUIRE(i == 8); + globals->set("var1", 10); + } else if (var1_cnt == 3) { + REQUIRE(i == 10); + } else if (var1_cnt == 4) { + REQUIRE(i == 5); + } }; globals->observe("var1", var1); std::string out = thread->getall(); REQUIRE(5 == globals->get("var1").value()); - REQUIRE( out == R"V(hello line 1 10 -hello line 2 5 -test line 3 5 -)V" ); + REQUIRE(out == "hello line 1 10 hello line 2 5 test line 3 5\n"); REQUIRE(var1_cnt == 4); - } WHEN("Changing Other value") { - int var1_cnt = 0; - auto var1 = [&var1_cnt,&globals](int32_t i) { - if(var1_cnt++==0){ - REQUIRE(i == 1); - } else { - REQUIRE(i == 5); - globals->set("var2", "didum"); - } + int var1_cnt = 0; + auto var1 = [&var1_cnt, &globals](int32_t i) { + if (var1_cnt++ == 0) { + REQUIRE(i == 1); + } else { + REQUIRE(i == 5); + globals->set("var2", "didum"); + } + }; + int var2_cnt = 0; + auto var2 = [&var2_cnt]() { + ++var2_cnt; }; - int var2_cnt = 0; - auto var2 = [&var2_cnt](){++var2_cnt;}; globals->observe("var1", var1); globals->observe("var2", var2); std::string out = thread->getall(); - REQUIRE( out == R"V(hello line 1 1 -didum line 2 5 -test line 3 5 -)V" ); + REQUIRE(out == "hello line 1 1 didum line 2 5 test line 3 5\n"); REQUIRE(var1_cnt == 2); REQUIRE(var2_cnt == 3); } } -} \ No newline at end of file +} diff --git a/inkcpp_test/ink/ExternalFunctionsExecuteProperly.ink b/inkcpp_test/ink/ExternalFunctionsExecuteProperly.ink new file mode 100644 index 00000000..37d8e5e6 --- /dev/null +++ b/inkcpp_test/ink/ExternalFunctionsExecuteProperly.ink @@ -0,0 +1,11 @@ +EXTERNAL GET_LINE_COUNT() + +Line count: {GET_LINE_COUNT()} + +Line count: {GET_LINE_COUNT()} + +~ temp should_execute_before = GET_LINE_COUNT() + +Line count: {should_execute_before} + +-> DONE \ No newline at end of file diff --git a/inkcpp_test/ink/FallBack.ink b/inkcpp_test/ink/FallBack.ink index 5371ad47..032f92bc 100644 --- a/inkcpp_test/ink/FallBack.ink +++ b/inkcpp_test/ink/FallBack.ink @@ -8,6 +8,8 @@ EXTERNAL greeting() ~ return "Hello" == Start == -{greeting()} ! A small demonstraion of my power: -{sqrt(16)} * {sqrt(16)} = 16, stunning i would say --> DONE \ No newline at end of file +{greeting()} ! A small demonstration of my power: + +Math {sqrt(16)} * {sqrt(16)} = 16, stunning i would say + +-> DONE diff --git a/inkcpp_test/ink/LookaheadSafe.ink b/inkcpp_test/ink/LookaheadSafe.ink new file mode 100644 index 00000000..0abcbdde --- /dev/null +++ b/inkcpp_test/ink/LookaheadSafe.ink @@ -0,0 +1,14 @@ +-> start + +EXTERNAL foo() +=== function foo() === +~ temp x = 5 + +== start +~ foo() +Call1 +~ foo() +<> glued to Call 2 +~ foo() +Call 3 is seperated +-> DONE diff --git a/inkcpp_test/ink/NoEarlyTags.ink b/inkcpp_test/ink/NoEarlyTags.ink new file mode 100644 index 00000000..a5d4bc60 --- /dev/null +++ b/inkcpp_test/ink/NoEarlyTags.ink @@ -0,0 +1,19 @@ +VAR fae00_name = "Hey" +VAR fae03_name = "Do" +VAR player_name = "YOU" +#name fae03_name +#bb CaoimheGenericProgress +Hey there, nice to meet you! + +#name fae00_name +Hey, I'm {fae00_name} and this is {player_name}, nice to meet you too! + +#name fae03_name +#meet-character 5 +<>I'm {fae03_name}! Most people can't pronounce it, just think 'Kee-vah". + +* A +* B +- C + +->END diff --git a/inkcpp_test/ink/ObserverStory.ink b/inkcpp_test/ink/ObserverStory.ink index 72e6ae99..e927713c 100644 --- a/inkcpp_test/ink/ObserverStory.ink +++ b/inkcpp_test/ink/ObserverStory.ink @@ -3,6 +3,6 @@ VAR var2 = "hello" {var2} line 1 {var1} ~ var1 = 5 -{var2} line 2 {var1} +<> {var2} line 2 {var1} ~ var2 = "test" -{var2} line 3 {var1} \ No newline at end of file +<> {var2} line 3 {var1} diff --git a/pyproject.toml b/pyproject.toml index dd44b0d7..d00f62fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,8 +30,7 @@ warn_unreachable = true module = ["ninja"] ignore_missing_imports = true - -# [tool.pytest.ini_options] +[tool.pytest.ini_options] # minversion = "6.0" # addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] # xfail_strict = true @@ -39,7 +38,10 @@ ignore_missing_imports = true # "error", # "ignore:(ast.Str|Attribute s|ast.NameConstant|ast.Num) is deprecated:DeprecationWarning:_pytest", # ] -# testpaths = ["tests"] +testpaths = ["inkcpp_py/tests"] +addopts = [ + "--import-mode=importlib", +] # [tool.cibuildwheel] # test-command = "pytest {project}/tests" diff --git a/setup.py b/setup.py index 9190f702..22230858 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from setuptools.command.build_ext import build_ext -# find shared/** inkcpp/** inkcpp_compiler/** inkcpp_py/** -not -path '*/.*' | sed -e 's/[^ ]*/"\0"/g' -e '/""/d' -e 's/ /,\n/g' +# echo "[\n$(find shared/** inkcpp/** inkcpp_compiler/** inkcpp_py/** -not -path '*/.*' | tr '\n' ' ' | sed -e 's/[^ ]\+/"\0"/g' -e 's/[[:blank:]]*$//' -e 's/ /,\n/g')\n]" # + "CMakeLists.txt" # Convert distutils Windows platform specifiers to CMake -A arguments @@ -22,9 +22,31 @@ "win-arm64": "ARM64", } +def src_files(dir): + dir = os.fsencode(dir) + files = [] + for file in os.listdir(dir): + if file[0] == '.': + continue + file = os.path.join(dir, file) + if os.path.isdir(file): + files += src_files(file) + else: + files.append(file.decode("utf-8")) + return files + +def glob_src_files(): + files = [] + dirs = ['./inkcpp', './inkcpp_compiler', './inkcpp_py', './shared'] + for dir in dirs: + files += src_files(dir) + return files + + class CMakeExtension(Extension): def __init__(self, name: str, sourcedir: str = "") -> None: - src_files = json.load(open('inkcpp_py/sources.json')) + src_files = glob_src_files() + # src_files = json.load(open('inkcpp_py/sources.json')) src_files += ["CMakeLists.txt", "Config.cmake.in"] super().__init__(name, sources=src_files) self.sourcedir = os.fspath(Path(sourcedir).resolve()) diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 561b5e13..4643e5ed 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -11,49 +11,52 @@ // Unreal includes #include "Internationalization/Regex.h" -UInkThread::UInkThread() : mbHasRun(false), mnChoiceToChoose(-1), mnYieldCounter(0), mbKill(false) { } -UInkThread::~UInkThread() {} -void UInkThread::Yield() +UInkThread::UInkThread() + : mbHasRun(false) + , mnChoiceToChoose(-1) + , mnYieldCounter(0) + , mbKill(false) { - mnYieldCounter++; } -bool UInkThread::IsYielding() -{ - return mnYieldCounter > 0; -} +UInkThread::~UInkThread() {} -void UInkThread::Resume() -{ - mnYieldCounter--; -} +void UInkThread::Yield() { mnYieldCounter++; } + +bool UInkThread::IsYielding() { return mnYieldCounter > 0; } -void UInkThread::RegisterTagFunction(FName functionName, const FTagFunctionDelegate & function) +void UInkThread::Resume() { mnYieldCounter--; } + +void UInkThread::RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function) { // Register tag function mTagFunctions.FindOrAdd(functionName).Add(function); } -void UInkThread::RegisterExternalFunction(const FString& functionName, const FExternalFunctionDelegate& function) +void UInkThread::RegisterExternalFunction( + const FString& functionName, const FExternalFunctionDelegate& function, bool lookaheadSafe +) { - mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function); + mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function, lookaheadSafe); } -void UInkThread::RegisterExternalEvent(const FString& functionName, const FExternalFunctionVoidDelegate& function) +void UInkThread::RegisterExternalEvent( + const FString& functionName, const FExternalFunctionVoidDelegate& function, bool lookaheadSafe +) { - mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function); + mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function, lookaheadSafe); } void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::runner thread) { - mStartPath = path; - mpRuntime = runtime; + mStartPath = path; + mpRuntime = runtime; mbInitialized = true; - mpRunner = thread; - mpTags = NewObject(); + mpRunner = thread; + mpTags = NewObject(); mTagFunctions.Reset(); mCurrentChoices.Reset(); - mbHasRun = false; + mbHasRun = false; mbInChoice = false; OnStartup(); @@ -66,9 +69,8 @@ bool UInkThread::ExecuteInternal() return true; // If this is the first time we're running, start us off at our starting path - if (!mbHasRun) - { - if (!ensureMsgf(mbInitialized, TEXT("Thread executing without call to ::Initialize"))) + if (! mbHasRun) { + if (! ensureMsgf(mbInitialized, TEXT("Thread executing without call to ::Initialize"))) return true; if (mStartPath.Len()) { mpRunner->move_to(ink::hash_string(TCHAR_TO_ANSI(*mStartPath))); @@ -77,13 +79,10 @@ bool UInkThread::ExecuteInternal() } // Execution loop - while (true) - { + while (true) { // Handle pending choice - if (mnChoiceToChoose != -1) - { - if (ensure(mpRunner->num_choices() > 0)) - { + if (mnChoiceToChoose != -1) { + if (ensure(mpRunner->num_choices() > 0)) { mpRunner->choose(mnChoiceToChoose); } mnChoiceToChoose = -1; @@ -91,19 +90,17 @@ bool UInkThread::ExecuteInternal() } // Execute until story yields or finishes - while (mnYieldCounter == 0 && mpRunner->can_continue()) - { + while (mnYieldCounter == 0 && mpRunner->can_continue()) { // Handle text FString line = mpRunner->getline(); // Special: Line begins with >> marker - if (line.StartsWith(TEXT(">>"))) - { + if (line.StartsWith(TEXT(">>"))) { // This is a special version of the tag function call // Expected: >> MyTagFunction(Arg1, Arg2, Arg3) - FRegexPattern pattern = FRegexPattern(TEXT("^>>\\s*(\\w+)(\\((\\s*(\\w+)\\s*(,\\s*(\\w+)\\s*)*)?\\))?$")); + FRegexPattern pattern + = FRegexPattern(TEXT("^>>\\s*(\\w+)(\\((\\s*(\\w+)\\s*(,\\s*(\\w+)\\s*)*)?\\))?$")); FRegexMatcher matcher = FRegexMatcher(pattern, line); - if (matcher.FindNext()) - { + if (matcher.FindNext()) { // Get name of function FString functionName = matcher.GetCaptureGroup(1); @@ -111,8 +108,7 @@ bool UInkThread::ExecuteInternal() TArray Arguments; Arguments.Add(functionName); int groupIndex = 4; - while (matcher.GetCaptureGroupBeginning(groupIndex) != -1) - { + while (matcher.GetCaptureGroupBeginning(groupIndex) != -1) { Arguments.Add(matcher.GetCaptureGroup(groupIndex).TrimStartAndEnd()); groupIndex += 2; } @@ -120,21 +116,18 @@ bool UInkThread::ExecuteInternal() // Call tag method ExecuteTagMethod(Arguments); } - } - else - { + } else { // Forward to handler // Get tags TArray tags; - for(size_t i = 0; i < mpRunner->num_tags(); ++i) { + for (size_t i = 0; i < mpRunner->num_tags(); ++i) { tags.Add(FString(mpRunner->get_tag(i))); } mpTags->Initialize(tags); OnLineWritten(line, mpTags); - + // Handle tags/tag methods post-line - for (auto it = tags.CreateConstIterator(); it; ++it) - { + for (auto it = tags.CreateConstIterator(); it; ++it) { // Generic tag handler OnTag(*it); @@ -147,13 +140,11 @@ bool UInkThread::ExecuteInternal() } // Handle choice block - if (mnYieldCounter == 0 && mpRunner->num_choices() > 0) - { + if (mnYieldCounter == 0 && mpRunner->num_choices() > 0) { mbInChoice = true; // Forward to handler - for (ink::size_t i = 0; i < mpRunner->num_choices(); i++) - { + for (ink::size_t i = 0; i < mpRunner->num_choices(); i++) { UInkChoice* choice = NewObject(this); choice->Initialize(mpRunner->get_choice(i)); mCurrentChoices.Add(choice); @@ -169,8 +160,7 @@ bool UInkThread::ExecuteInternal() } // Have we reached the end? If so, destroy the thread - if (!mpRunner->can_continue() && mnYieldCounter == 0 && mpRunner->num_choices() == 0) - { + if (! mpRunner->can_continue() && mnYieldCounter == 0 && mpRunner->num_choices() == 0) { UE_LOG(InkCpp, Display, TEXT("Destroying thread")); // TODO: Event for ending? @@ -187,8 +177,7 @@ void UInkThread::ExecuteTagMethod(const TArray& Params) { // Look for method and execute with parameters FTagFunctionMulticastDelegate* function = mTagFunctions.Find(FName(*Params[0])); - if (function != nullptr) - { + if (function != nullptr) { function->Broadcast(this, Params); } @@ -202,8 +191,7 @@ bool UInkThread::Execute() bool finished = ExecuteInternal(); // If we've finished, run callback - if (finished) - { + if (finished) { // Allow outsiders to subscribe // TODO: OnThreadShutdown.Broadcast(); OnShutdown(); @@ -216,23 +204,17 @@ bool UInkThread::Execute() bool UInkThread::PickChoice(int index) { if (index >= mCurrentChoices.Num()) { - UE_LOG(InkCpp, Warning, - TEXT("PickChoice: index(%i) out of range [0-%i)"), - index, - mCurrentChoices.Num()); + UE_LOG( + InkCpp, Warning, TEXT("PickChoice: index(%i) out of range [0-%i)"), index, + mCurrentChoices.Num() + ); return false; } mnChoiceToChoose = index; - mbInChoice = false; + mbInChoice = false; return true; } -bool UInkThread::CanExecute() const -{ - return mnYieldCounter == 0 && !mbInChoice; -} +bool UInkThread::CanExecute() const { return mnYieldCounter == 0 && ! mbInChoice; } -void UInkThread::Stop() -{ - mbKill = true; -} \ No newline at end of file +void UInkThread::Stop() { mbKill = true; } diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index d97892fc..076704d3 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -22,6 +22,7 @@ class UInkChoice; * Base class for all ink threads */ UCLASS(Blueprintable) + class INKCPP_API UInkThread : public UObject { GENERATED_BODY() @@ -31,66 +32,77 @@ class INKCPP_API UInkThread : public UObject ~UInkThread(); // Yields the thread immediately. Will wait until Resume(). - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") void Yield(); - UFUNCTION(BlueprintPure, Category="Ink") + UFUNCTION(BlueprintPure, Category = "Ink") bool IsYielding(); // Causes the thread to resume if yielded. - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") void Resume(); // Kills the thread, regardless of state - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") void Stop(); // Returns the runtime which owns this thread. - UFUNCTION(BlueprintPure, Category="Ink") + UFUNCTION(BlueprintPure, Category = "Ink") + AInkRuntime* GetRuntime() const { return mpRuntime; } // Called before the thread begins executing - UFUNCTION(BlueprintImplementableEvent , Category="Ink") + UFUNCTION(BlueprintImplementableEvent, Category = "Ink") void OnStartup(); // Called when the thread has printed a new line - UFUNCTION(BlueprintImplementableEvent , Category="Ink") + UFUNCTION(BlueprintImplementableEvent, Category = "Ink") void OnLineWritten(const FString& line, const UTagList* tags); // Called when a tag has been processed on the current line - UFUNCTION(BlueprintImplementableEvent , Category="Ink") + UFUNCTION(BlueprintImplementableEvent, Category = "Ink") void OnTag(const FString& line); // Called when the thread has requested a branch - UFUNCTION(BlueprintImplementableEvent , Category="Ink") + UFUNCTION(BlueprintImplementableEvent, Category = "Ink") void OnChoice(const TArray& choices); // Called before the thread is destroyed - UFUNCTION(BlueprintImplementableEvent , Category="Ink") + UFUNCTION(BlueprintImplementableEvent, Category = "Ink") void OnShutdown(); // Picks a choice by index at the current branch - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") bool PickChoice(int index); // Registers a callback for a named "tag function" - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") void RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function); - UFUNCTION(BlueprintCallable, Category="Ink") - void RegisterExternalFunction(const FString& functionName, const FExternalFunctionDelegate& function); - - UFUNCTION(BlueprintCallable, Category="Ink") - void RegisterExternalEvent(const FString& functionName, const FExternalFunctionVoidDelegate& function); - + UFUNCTION(BlueprintCallable, Category = "Ink") + void RegisterExternalFunction( + const FString& functionName, const FExternalFunctionDelegate& function, + bool lookaheadSafe = false + ); + + UFUNCTION(BlueprintCallable, Category = "Ink") + void RegisterExternalEvent( + const FString& functionName, const FExternalFunctionVoidDelegate& function, + bool lookaheadSafe = false + ); + protected: - virtual void OnStartup_Implementation() { } - virtual void OnLineWritten_Implementation(const FString& line, UTagList* tags) { } - virtual void OnTag_Implementation(const FString& line) { } - virtual void OnChoice_Implementation(const TArray& choices) { } - virtual void OnShutdown_Implementation() { } - + virtual void OnStartup_Implementation() {} + + virtual void OnLineWritten_Implementation(const FString& line, UTagList* tags) {} + + virtual void OnTag_Implementation(const FString& line) {} + + virtual void OnChoice_Implementation(const TArray& choices) {} + + virtual void OnShutdown_Implementation() {} + private: friend class AInkRuntime; @@ -104,16 +116,16 @@ class INKCPP_API UInkThread : public UObject private: ink::runtime::runner mpRunner; - UTagList* mpTags; - TArray mCurrentChoices; /// @TODO: make accassible? + UTagList* mpTags; + TArray mCurrentChoices; /// @TODO: make accassible? TMap mTagFunctions; - + FString mStartPath; - bool mbHasRun; + bool mbHasRun; - int mnChoiceToChoose; - int mnYieldCounter; + int mnChoiceToChoose; + int mnYieldCounter; bool mbInChoice; bool mbKill; bool mbInitialized;