From ae1e1c4c43c290551a6b174427481cae9faba0db Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 11 Feb 2021 15:55:19 +0100 Subject: [PATCH 01/23] Add implementation note scratch --- inkcpp_compiler/json_compiler.cpp | 10 ++++- notes/ListNotes.md | 63 +++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 notes/ListNotes.md diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index 1bd65808..15548af2 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -318,5 +318,13 @@ 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); } + + // list initialisation + else if (has(command, "list")) + { + for ( const auto& entry : command["list"]) { + + } + } } -} \ No newline at end of file +} diff --git a/notes/ListNotes.md b/notes/ListNotes.md new file mode 100644 index 00000000..ec897e1c --- /dev/null +++ b/notes/ListNotes.md @@ -0,0 +1,63 @@ +## general construction: + ++ A list is a bit vector! ++ Further we will call each bit vector which represents a list a _flag_. ++ Each flag is tide to a number, which is equal to the position in the bit vector, which we will call _flag value_ ++ Each flag has a string representation, further be called _key_. ++ Each multi list is associated to zero ore more list types + + when adding a new element the list type of that element get added to associated lists + + when removing the last element of an list type then the type get removed from associated lists + + __except:__ the list of associated types will be empty, than nothing happen + + -> only an empty initialized list has no d list ++ each list key will be an element variable at global scope -> no two keys can have the same name ++ a list_type is a variable defined with `LIST` therefore mentioned in the `declList` section of the InkJson + +## List operators + ++ `+(list lh,list rh)` lh = lh ∪ rh ++ `+(list, element)` add the element to the list ++ `-(list lh, list rh)` remove elements lh = lh / (lh ∩ rh) ++ `-(list lh, list rh)` removes element from list ++ `++(list l)` `l = l << 1` (bits get shifted out) ++ `--(list l)` `l = l >> 1` (bits get shifted out) ++ `LIST_COUNT(list l)` returns the number of flags sets ++ `LIST_MIN(list l)` returns the flag with the lowest numerical representation which is set ++ `LIST_MAX(list l)` return the flag with the highest numerical representation which is set ++ `lrnd(list l)` return a random flag which is set + + for the user it is `LIST_RANDOM`, but in InkJson it is called `lrnd` ++ `LIST_ALL(list l)` returns a list which all elements of associated list of l ++ `LIST_INVERT(list l)` returns a list which each flag is toggeld of all associated lists ++ `<(list lh, list rh)` return true if `LIST_MAX(lh) < LIST_MIN(rh)` ++ `>(list lh, list rh)` returns true if `LIST_MIN(lh) > LIST_MAX(rh)` ++ `==(list lh, list rh)` returns true if the setted flags of both are equal ++ `!=(list lh, list rh)` return true if `lh == rh` returns false ++ `>=(list lh, list rh)` returns true if `LIST_MAX(lh) >= LIST_MAX(rh) && LIST_MIN(lh) >= LIST_MIN(rh)` ++ `<=(list lh, list rh)` returns true if `LIST_MAX(lh) <= LIST_MAX(rh) && LIST_MIN(lh) <= LIST_MIN(rh)` + +## Datatype + ++ Each list has a id(`listId`), this mapping is arbitrary. To allow usage as array index we start with 0 an increased it for each list_type ++ A flag is identified by `flagId`, it's correspond to the flag value. (flagId - min flagId of that list = flag value) + +### list_element + +`uint32_t` with bit[0-15] are the listId and bit[16-31] the flag value. + +### list + +`uint32_t` position in list_table + +### list_table + +Datatype to manage lists (similar to string_table) + +It contains: + ++ a reference to the string_table ++ A array `keys` which maps each flagId to an key, who lives in string_table ++ A array `flag_start` which maps each listId to an offset, which leads to value 0 for that list in the entry bitmap. ++ A array `fids` which maps each listId to the flagId for the value 0 of that list (used to get key from keys). ++ A array of entries, each entry contains: + + a bitmap, where 1 represent that the list is associated to the corresponding list + + a bitmap, where 1 represent that the list contains the flag + + the two bitmaps are WORD align for better memory access From 56f1709f3b790d222b93dfa90e4786638267748f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 16 Feb 2021 22:17:21 +0100 Subject: [PATCH 02/23] Add list_table class --- inkcpp/list_table.h | 57 +++++++++++++++++++++++++++++++++++++++++++++ inkcpp/value.h | 18 ++++++++++++-- 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 inkcpp/list_table.h diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h new file mode 100644 index 00000000..653b5e80 --- /dev/null +++ b/inkcpp/list_table.h @@ -0,0 +1,57 @@ +#pragma once + +#include "system.h" + +namespace ink::runtime::internal { + using handel_t = uint32_t; + + class list_table; + struct list_element; + class list_flags { + public: + list_flags(const list_table& table, handel_t list); + class iterator { + handel_t operator*() const; + bool operator==(const iterator& rh) const; + iterator& operator++(); + }; + iterator begin() const; + iterator end() const; + private: + handel_t _list; + const list_table& _table; + }; + + class list_table { + public: + list_table(size_t num_lists, size_t* list_lengths); + list_flags get(handel_t list) const; + // get list_id as input and returns list_id of result list + handel_t add(handel_t lh, handel_t rh); + handel_t add(handel_t list, list_element el); + handel_t subtract(handel_t lh, handel_t rh); + handel_t subtract(handel_t list, list_element el); + handel_t inc(handel_t list); + handel_t dec(handel_t list); + handel_t count(handel_t list); + list_element min(handel_t list); + list_element max(handel_t list); + handel_t all(handel_t list); + handel_t invert(handel_t list); + + list_element lrnd(handel_t list); + + // list_id of lh and rh, returns result + bool is_equal(handel_t lh, handel_t rh); + bool less_then(handel_t lh, handel_t rh); + bool greater(handel_t lh, handel_t rh); + bool less_equal(handel_t lh, handel_t rh); + bool greater_equal(handel_t lh, handel_t rh); + + private: + size_t* _list_start; //< maps list id -> first entry + uint32_t* _start_masks; + uint32_t* _end_masks; + uint32_t _entries; + }; +} diff --git a/inkcpp/value.h b/inkcpp/value.h index 0bc32ae0..9303dd25 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -8,11 +8,17 @@ namespace ink { - namespace runtime + namespace runtime { - namespace internal + namespace internal { class string_table; + class list_table; + + struct list_element { + uint16_t list_id; + uint16_t flag_id; + }; // Data types that can be held internally within the ink runtime enum class data_type @@ -23,6 +29,8 @@ namespace ink uint32, // 32-bit unsigned integer value string_table_pointer, // Represents an offset within the story's constant string table allocated_string_pointer, // Represents an offset within the runner's allocated string table + list, // list: referenced in list_table + list_element, // one flag of one list marker, // Special marker (used in output stream) glue, // Glue. newline, // \n @@ -49,6 +57,7 @@ namespace ink uint32_t uint_value; float float_value; const char* string_val; + list_element list_element_value; // TODO: Do we need a marker type? }; @@ -57,6 +66,7 @@ namespace ink inline void set_int(int val) { type = data_type::int32; integer_value = val; } inline void set_uint(uint32_t val) { type = data_type::uint32; uint_value = val; } inline void set_float(float val) { type = data_type::float32; float_value = val; } + inline void set_list_element(list_element elm) { type = data_type::list_element; list_element_value = elm; } inline void set_string(const char* val, bool allocated) { type = allocated ? data_type::allocated_string_pointer : data_type::string_table_pointer; string_val = val; } }; @@ -70,6 +80,8 @@ namespace ink divert, integer, decimal, + list_element, + list, string, }; @@ -83,6 +95,7 @@ namespace ink value(int); // Create a new int value value(float); // Create a new float value value(uint32_t); // Create a new divert value + value(list_element); value(const data&); // Create value from data value(uint32_t, data_type); // Create divert with type @@ -107,6 +120,7 @@ namespace ink const uint32_t* as_uint_ptr() const { return &_first.uint_value; } const char* as_str(string_table&) const; const char* const * as_str_ptr(string_table&) const; + uint32_t as_list_id() const { return _first.uint_value; } template T get() const { static_assert(always_false::value, "Type not supported by value class"); } From fcaed9a008f65d4f585efd4d4fcb6982769a1779 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 18 Feb 2021 14:30:30 +0100 Subject: [PATCH 03/23] Add first scratch --- inkcpp/CMakeLists.txt | 2 +- inkcpp/casting.h | 13 + inkcpp/choice.cpp | 9 +- inkcpp/default_operations.h | 173 +++++++++++ inkcpp/executioner.h | 99 +++++++ inkcpp/functional.cpp | 30 +- inkcpp/globals_impl.cpp | 69 ++--- inkcpp/globals_impl.h | 8 +- inkcpp/include/globals.h | 24 +- inkcpp/operation_bases.h | 29 ++ inkcpp/operations.h | 5 + inkcpp/output.cpp | 116 +++----- inkcpp/output.h | 12 +- inkcpp/runner_impl.cpp | 157 +++------- inkcpp/runner_impl.h | 5 +- inkcpp/stack.cpp | 145 ++++++---- inkcpp/stack.h | 6 +- inkcpp/string_operations.h | 15 + inkcpp/value.cpp | 554 ++---------------------------------- inkcpp/value.h | 456 +++++++++++++++-------------- shared/private/command.h | 6 +- 21 files changed, 845 insertions(+), 1088 deletions(-) create mode 100644 inkcpp/casting.h create mode 100644 inkcpp/default_operations.h create mode 100644 inkcpp/executioner.h create mode 100644 inkcpp/operation_bases.h create mode 100644 inkcpp/operations.h create mode 100644 inkcpp/string_operations.h diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index 305db42a..6e3137ca 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -15,7 +15,7 @@ list(APPEND SOURCES story_impl.h story_impl.cpp story_ptr.cpp system.cpp - value.h value.cpp + value.cpp value.h string_table.h string_table.cpp avl_array.h header.cpp ) diff --git a/inkcpp/casting.h b/inkcpp/casting.h new file mode 100644 index 00000000..740f2ee8 --- /dev/null +++ b/inkcpp/casting.h @@ -0,0 +1,13 @@ +#pragma once + +#include "value.h" + +namespace ink::runtime::internal { + namespace casting { + template + value_type common_base(const value* vs) { + if constexpr (N == 0) { return value_type::none; } + else { return vs->type(); } + } + }; +} diff --git a/inkcpp/choice.cpp b/inkcpp/choice.cpp index 028e8656..93110937 100644 --- a/inkcpp/choice.cpp +++ b/inkcpp/choice.cpp @@ -12,11 +12,10 @@ namespace ink { // If it's a string, just grab it. Otherwise, use allocation const internal::data& data = in.peek(); - switch (data.type) + switch (data.type()) { - case internal::data_type::string_table_pointer: - case internal::data_type::allocated_string_pointer: - _text = data.string_val; + case internal::data_type::string: + _text = data.get(); in.discard(2); break; default: @@ -35,4 +34,4 @@ namespace ink _thread = thread; } } -} \ No newline at end of file +} diff --git a/inkcpp/default_operations.h b/inkcpp/default_operations.h new file mode 100644 index 00000000..9a0e2c1e --- /dev/null +++ b/inkcpp/default_operations.h @@ -0,0 +1,173 @@ +#pragma once + +#include "executioner.h" +#include "operation_bases.h" + +namespace ink::runtime::internal { + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() + vals[1].get() )); + } + } + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() - vals[1].get() )); + } + } + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() / vals[1].get() )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() * vals[1].get() )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() % vals[1].get() )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() == vals[1].get() + )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() > vals[1].get() + )); + } + }; + + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() < vals[1].get() + )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() >= vals[1].get() + )); + } + }; + + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() <= vals[1].get() + )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() != vals[1].get() + )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() && vals[1].get() )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() || vals[1].get() )); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(vals[0].get() < vals[1].get() ? vals[0] : vals[1]); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(vals[0].get() > vals[1].get() ? vals[0] : vals[1]); + } + }; + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set(!vals[0].get())); + } + } + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set(-vals[0].get())); + } + } +} diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h new file mode 100644 index 00000000..37719a5c --- /dev/null +++ b/inkcpp/executioner.h @@ -0,0 +1,99 @@ +#pragma once + +#include "value.h" +#include "casting.h" +#include "stack.h" + +#include + +namespace ink::runtime::internal { + + constexpr size_t command_num_args(Command cmd) { + if (cmd >= Command::BINARY_OPERATORS_START && cmd <= Command::BINARY_OPERATORS_END) { + return 2; + } else if (cmd >= Command::UNARY_OPERATORS_START && cmd <= Command::UNARY_OPERATORS_END) { + return 1; + } else { + return 0; + } + } + template + static constexpr size_t CommandNumArguments = command_num_args(cmd); + + template + class operation { + public: + template + operation(const T& t) {} + void operator()(eval_stack&, value*) { + throw ink_exception("operation not implemented!"); + } + }; + + template + class typed_executer { + public: + template + typed_executer(const T& t) : _typed_exe{t}, _op{t} {} + + void operator()(value_type t, eval_stack& s, value* v) { + if (t == ty) { _op(s, v); } + else { _typed_exe(t, s, v); } + } + private: + typed_executer _typed_exe; + operation _op; + }; + template + class typed_executer { + public: + template + typed_executer(const T& t) {} + + void operator()(value_type, eval_stack&, value*) { + throw ink_exception("Operation for value not supported!"); + } + }; + + template + class executer_imp { + public: + template + executer_imp(const T& t) : _exe{t}, _typed_exe{t}{} + + void operator()(Command c, eval_stack& s) { + if (c == cmd) { + static constexpr size_t N = CommandNumArguments; + value args[N]; + for (int i = CommandNumArguments-1; i >= 0 ; --i) { + args[i] = s.pop(); + } + value_type ty = casting::common_base(args); + _typed_exe(ty, s, args); + } else { _exe(c, s); } + } + private: + executer_imp _exe; + typed_executer _typed_exe; + }; + template<> + class executer_imp { + public: + template + executer_imp(const T& t) {} + void operator()(Command, eval_stack&) { + throw ink_exception("requested command was not found!"); + } + }; + + class executer { + public: + template + executer(Args& ... args) : _executer{std::tuple(&args...)} {} + void operator()(Command cmd, eval_stack& stack) { + _executer(cmd, stack); + } + private: + executer_imp _executer; + }; +} diff --git a/inkcpp/functional.cpp b/inkcpp/functional.cpp index 573bfa20..0fe4cc69 100644 --- a/inkcpp/functional.cpp +++ b/inkcpp/functional.cpp @@ -10,21 +10,23 @@ namespace ink::runtime::internal { - template - T function_base::pop(basic_eval_stack* stack) + template<> + int32_t function_base::pop(basic_eval_stack* stack) { - return stack->pop().get(); + value val = stack->pop(); + inkAssert(val.type() == value_type::int32, "Type missmatch!"); + return val.get(); } - template - void function_base::push(basic_eval_stack* stack, const T& value) + template<> + void function_base::push(basic_eval_stack* stack, const int32_t& v) { - stack->push(value); + stack->push(value{}.set(v)); } void function_base::push_string(basic_eval_stack* stack, const char* dynamic_string) { - stack->push(value(dynamic_string, true)); + stack->push(value{}.set(dynamic_string, true)); } char* function_base::allocate(string_table& strings, size_t len) @@ -34,17 +36,11 @@ namespace ink::runtime::internal // Generate template implementations for all significant types -#define SUPPORT_TYPE(TYPE) template TYPE function_base::pop(basic_eval_stack*); template void function_base::push(basic_eval_stack*, const TYPE&) -#define SUPPORT_TYPE_PARAMETER_ONLY(TYPE) template TYPE function_base::pop(basic_eval_stack*) - - SUPPORT_TYPE(int); - SUPPORT_TYPE(float); - SUPPORT_TYPE(uint32_t); - - // TODO - Support string return values - #ifdef INK_ENABLE_STL - SUPPORT_TYPE_PARAMETER_ONLY(std::string); + template<> + std::string function_base::pop(basic_eval_stack* stack) { + return std::string(pop(stack)); + } #endif #ifdef INK_ENABLE_UNREAL SUPPORT_TYPE_PARAMETER_ONLY(FString); diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 8637eb8a..94369cb7 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -72,53 +72,44 @@ namespace ink::runtime::internal return _variables.get(name); } - template - auto fetch_variable( const value* v, TYPES ... types) { - return v && ((v->get_data_type() == types) || ...) - ? (v->*FN)() - : nullptr; + template + optional fetch_variable(const value* val) { + if (val && val->type() == ty) { + return optional(val->get()); + } + return {nullopt}; } - template - auto fetch_variable(value* v, TYPES ... types) { - return v && ((v->get_data_type() == types) || ...) - ? (v->*FN)() - : nullptr; + + template + bool try_set_value(value* val, T x) { + if (val && val->type() == ty) { + val->set(x); + return true; + } + return false; } - const uint32_t* globals_impl::get_uint(hash_t name) const { - return fetch_variable(get_variable(name), data_type::uint32); + optional globals_impl::get_int(hash_t name) const { + return fetch_variable(get_variable(name)); } - bool globals_impl::set_uint(hash_t name, uint32_t val) { - uint32_t* p = fetch_variable(get_variable(name), data_type::uint32); - if (p == nullptr) { return false; } - *p = val; - return true; + bool globals_impl::set_int(hash_t name, int32_t i) { + return try_set_value(get_variable(name), i); } - - const int32_t* globals_impl::get_int(hash_t name) const { - return fetch_variable(get_variable(name), data_type::int32); + optional globals_impl::get_uint(hash_t name) const { + return fetch_variable(get_variable(name)); } - bool globals_impl::set_int(hash_t name, int32_t val) { - int32_t* p = fetch_variable(get_variable(name), data_type::int32); - if (p == nullptr) { return false; } - *p = val; - return true; + bool globals_impl::set_uint(hash_t name, uint32_t i) { + return try_set_value(get_variable(name), i); } - - const float* globals_impl::get_float(hash_t name) const { - return fetch_variable(get_variable(name), data_type::float32); + optional globals_impl::get_float(hash_t name) const { + return fetch_variable(get_variable(name)); } - bool globals_impl::set_float(hash_t name, float val) { - float* p = fetch_variable(get_variable(name), data_type::float32); - if (p == nullptr) { return false; } - *p = val; - return true; + bool globals_impl::set_float(hash_t name, float i) { + return try_set_value(get_variable(name), i); } - const char * const * globals_impl::get_str(hash_t name) const { - const value* v = get_variable(name); - if (v->type() != value_type::string) { return nullptr; } - return v->as_str_ptr(_strings); + optional globals_impl::get_str(hash_t name) const { + return fetch_variable(get_variable(name)); } bool globals_impl::set_str(hash_t name, const char* val) { value* v = get_variable(name); @@ -134,9 +125,7 @@ namespace ink::runtime::internal *ptr++ = *i; } *ptr = 0; - internal::data d; - d.set_string(new_string, true); - *v = internal::value(d); + *v = value{}.set(static_cast(new_string), true); return true; } return false; diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index bf6ce785..292bd920 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -20,16 +20,16 @@ namespace ink::runtime::internal virtual ~globals_impl() { } protected: - const uint32_t* get_uint(hash_t name) const override; + optional get_uint(hash_t name) const override; bool set_uint(hash_t name, uint32_t value) override; - const int32_t* get_int(hash_t name) const override; + optional get_int(hash_t name) const override; bool set_int(hash_t name, int32_t value) override; - const float* get_float(hash_t name) const override; + optional get_float(hash_t name) const override; bool set_float(hash_t name, float value) override; - const char * const * get_str(hash_t name) const override; + optional get_str(hash_t name) const override; bool set_str(hash_t name, const char* value) override; public: diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index 231fc368..ae6b1a97 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -45,21 +45,19 @@ namespace ink::runtime virtual ~globals_interface() = default; protected: - virtual const uint32_t* get_uint(hash_t name) const = 0; + virtual optional get_uint(hash_t name) const = 0; virtual bool set_uint(hash_t name, uint32_t val) = 0; - virtual const int32_t* get_int(hash_t name) const = 0; + virtual optional get_int(hash_t name) const = 0; virtual bool set_int(hash_t name, int32_t val) = 0; - virtual const float* get_float(hash_t name) const = 0; + virtual optional get_float(hash_t name) const = 0; virtual bool set_float(hash_t name, float val) = 0; - virtual const char* const * get_str(hash_t name) const = 0; + virtual optional get_str(hash_t name) const = 0; virtual bool set_str(hash_t name, const char* val) = 0; }; template<> inline optional globals_interface::get(const char* name) const { - const uint32_t* p = get_uint(hash_string(name)); - if (p) { return {*p}; } - return {nullopt}; + return get_uint(hash_string(name)); } template<> inline bool globals_interface::set(const char* name, const uint32_t& val) { @@ -68,9 +66,7 @@ namespace ink::runtime template<> inline optional globals_interface::get(const char* name) const { - const int32_t* p = get_int(hash_string(name)); - if (p) { return {*p}; } - return {nullopt}; + return get_int(hash_string(name)); } template<> inline bool globals_interface::set(const char* name, const int32_t& val) { @@ -79,9 +75,7 @@ namespace ink::runtime template<> inline optional globals_interface::get(const char* name) const { - const float* p = get_float(hash_string(name)); - if (p) { return {*p}; } - return {nullopt}; + return get_float(hash_string(name)); } template<> inline bool globals_interface::set(const char* name, const float& val) { @@ -90,9 +84,7 @@ namespace ink::runtime template<> inline optionalglobals_interface::get(const char* name) const { - const char * const * p = get_str(hash_string(name)); - if (p) { return {*p}; } - return {nullopt}; + return get_str(hash_string(name)); } template<> inline bool globals_interface::set(const char* name, const char * const & val) { diff --git a/inkcpp/operation_bases.h b/inkcpp/operation_bases.h new file mode 100644 index 00000000..0fa89521 --- /dev/null +++ b/inkcpp/operation_bases.h @@ -0,0 +1,29 @@ +#pragma once + +namespace ink::runtime::internal { + class string_table; + + template + class operation_base { + public: + template + operation_base(const T&) { static_assert(always_false::value, "use undefined base!"); } + }; + + template<> + class operation_base { + public: + template + operation_base(const T&) {} + }; + + template<> + class operation_base { + public: + template + operation_base(const T& t) : _string_table{*std::get(t)} {} + + private: + string_table& _string_table; + }; +} diff --git a/inkcpp/operations.h b/inkcpp/operations.h new file mode 100644 index 00000000..bfc8593e --- /dev/null +++ b/inkcpp/operations.h @@ -0,0 +1,5 @@ +#pragma once + +#include "executioner.h" +#include "default_operations.h" +#include "string_operations.h" diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 54742edc..ab68bc0c 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -20,16 +20,16 @@ namespace ink void basic_stream::append(const data& in) { // SPECIAL: Incoming newline - if (in.type == data_type::newline && _size > 1) + if (in.type() == data_type::newline && _size > 1) { // If the end of the stream is a function start marker, we actually // want to ignore this. Function start trimming. - if (_data[_size - 1].type == data_type::func_start) + if (_data[_size - 1].type() == data_type::func_start) return; } // Ignore leading newlines - if (in.type == data_type::newline && _size == 0) + if (in.type() == data_type::newline && _size == 0) return; // Add to data stream @@ -38,7 +38,7 @@ namespace ink // Special: Incoming glue. Trim whitespace/newlines prior // This also applies when a function ends to trim trailing whitespace. - if ((in.type == data_type::glue || in.type == data_type::func_end) && _size > 1) + if ((in.type() == data_type::glue || in.type() == data_type::func_end) && _size > 1) { // Run backwards size_t i = _size - 2; @@ -47,14 +47,14 @@ namespace ink data& d = _data[i]; // Nullify newlines - if (d.type == data_type::newline) - d.type = data_type::none; + if (d.type() == data_type::newline) { + d = value{}; + } // Nullify whitespace - else if ( - (d.type == data_type::string_table_pointer || d.type == data_type::allocated_string_pointer) - && is_whitespace(d.string_val)) - d.type = data_type::none; + else if ( d.type() == data_type::string + && is_whitespace(d.get())) + d = value{}; // If it's not a newline or whitespace, stop else break; @@ -99,14 +99,14 @@ namespace ink while (i + 1 < size) { *next = &list[i + 1]; - data_type type = (*next)->type; + data_type type = (*next)->type(); switch (type) { + // FIXME: should be all printable symbols? case data_type::int32: case data_type::float32: case data_type::uint32: - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: + case data_type::string: case data_type::newline: return true; } @@ -145,14 +145,13 @@ namespace ink if (get_next(_data, dataIter, _size, &next)) { // If it's a newline, ignore all our whitespace - if (next->type == data_type::newline) + if (next->type() == data_type::newline) return; // If it's another string, check if it starts with whitespace - if (next->type == data_type::allocated_string_pointer || - next->type == data_type::string_table_pointer) + if (next->type() == data_type::string ) { - if (is_whitespace(next->string_val[0])) + if (is_whitespace(next->get()[0])) return; } @@ -190,23 +189,10 @@ namespace ink { if (should_skip(i, hasGlue, lastNewline)) continue; - - switch (_data[i].type) - { - case data_type::int32: - str << _data[i].integer_value; - break; - case data_type::float32: - str << std::setprecision(7) << _data[i].float_value; - break; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: - copy_string(_data[i].string_val, i, str); - break; - case data_type::newline: - str << std::endl; - break; + if (_data[i].printable()){ + str << _data[i]; } + } // Reset stream size to where we last held the marker @@ -298,15 +284,8 @@ namespace ink inkAssert(ptr < end, "Insufficient space in data array to store stream contents!"); // Copy any value elements - switch (_data[i].type) - { - case data_type::int32: - case data_type::float32: - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: - case data_type::newline: + if (_data[i].printable()) { *(ptr++) = _data[i]; - break; } } @@ -319,7 +298,7 @@ namespace ink // TODO: Cache? for (size_t i = 0; i < _size; i++) { - if (_data[i].type == data_type::marker) + if (_data[i].type() == data_type::marker) return true; } @@ -331,7 +310,7 @@ namespace ink if (_size == 0) return false; - return _data[_size - 1].type == type; + return _data[_size - 1].type() == type; } bool basic_stream::saved_ends_with(data_type type) const @@ -341,7 +320,7 @@ namespace ink if (_save == 0) return false; - return _data[_save - 1].type == type; + return _data[_save - 1].type() == type; } void basic_stream::save() @@ -383,17 +362,20 @@ namespace ink if (should_skip(i, hasGlue, lastNewline)) continue; - switch (_data[i].type) + if (_data[i].printable()) { + + } + // FIXME: not the right place + switch (_data[i].type()) { case data_type::int32: - length += decimal_digits(_data[i].integer_value); + length += decimal_digits(_data[i].get()); break; case data_type::float32: - length += decimal_digits(_data[i].float_value); + length += decimal_digits(_data[i].get()); break; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: - length += strlen(_data[i].string_val); + case data_type::string: + length += strlen(_data[i].get()); break; case data_type::newline: length += 1; @@ -410,25 +392,24 @@ namespace ink { if (should_skip(i, hasGlue, lastNewline)) continue; - - switch (_data[i].type) + // FIXME: right place? + switch (_data[i].type()) { case data_type::int32: // Convert to string and advance - toStr(ptr, end - ptr, _data[i].integer_value); + toStr(ptr, end - ptr, _data[i].get()); while (*ptr != 0) ptr++; break; case data_type::float32: // Convert to string and advance - toStr(ptr, end - ptr, _data[i].float_value); + toStr(ptr, end - ptr, _data[i].get()); while (*ptr != 0) ptr++; break; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: + case data_type::string: // Copy string and advance - copy_string(_data[i].string_val, i, ptr); + copy_string(_data[i].get(), i, ptr); break; case data_type::newline: *ptr = '\n'; ptr++; @@ -454,7 +435,7 @@ namespace ink while (start > 0) { start--; - if (_data[start].type == data_type::marker) + if (_data[start].type() == data_type::marker) break; } @@ -467,12 +448,11 @@ namespace ink bool basic_stream::should_skip(size_t iter, bool& hasGlue, bool& lastNewline) const { - switch (_data[iter].type) + switch (_data[iter].type()) { case data_type::int32: case data_type::float32: - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: + case data_type::string: hasGlue = false; lastNewline = false; break; @@ -497,10 +477,10 @@ namespace ink for (size_t i = _save; i < _size; i++) { const data& d = _data[i]; - if (d.type == data_type::allocated_string_pointer || d.type == data_type::string_table_pointer) + if (d.type() == value_type::string) { // TODO: Cache what counts as whitespace? - if (!is_whitespace(d.string_val, false)) + if (!is_whitespace(d.get(), false)) return true; } } @@ -520,8 +500,9 @@ namespace ink // Find all allocated strings and mark them as used for (int i = 0; i < _size; i++) { - if (_data[i].type == internal::data_type::allocated_string_pointer) - strings.mark_used(_data[i].string_val); + // FIXME: only allocated strings!! + if (_data[i].type() == value_type::string) + strings.mark_used(_data[i].get()); } } @@ -546,11 +527,6 @@ namespace ink } #endif - basic_stream& operator<<(basic_stream& out, const data& in) - { - out.append(in); - return out; - } } } diff --git a/inkcpp/output.h b/inkcpp/output.h index a2e27dae..ddb57a70 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -9,6 +9,10 @@ namespace ink { namespace internal { + class string_table; + using data = value; + using data_type = value_type; + class basic_stream { protected: @@ -105,14 +109,6 @@ namespace ink basic_stream& operator >>(basic_stream&, FString&); #endif - basic_stream& operator<<(basic_stream&, const data&); - - const data marker = { data_type::marker, 0 }; - const data newline = { data_type::newline, 0 }; - const data glue = { data_type::glue, 0 }; - const data func_start = { data_type::func_start, 0 }; - const data func_end = { data_type::func_end, 0 }; - template class stream : public basic_stream { diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 0dc95e7d..a7430a73 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -138,86 +138,6 @@ namespace ink::runtime::internal _ptr = dest; } - void runner_impl::run_binary_operator(unsigned char cmd) - { - // Pop - value rhs = _eval.pop(), lhs = _eval.pop(); - value result; - - switch ((Command)cmd) - { - case Command::ADD: - result = value::add(lhs, rhs, _output, _globals->strings()); - break; - case Command::SUBTRACT: - result = lhs - rhs; - break; - case Command::DIVIDE: - result = lhs / rhs; - break; - case Command::MULTIPLY: - result = lhs * rhs; - break; - case Command::MOD: - result = lhs % rhs; - break; - case Command::IS_EQUAL: - result = lhs == rhs; - break; - case Command::GREATER_THAN: - result = lhs > rhs; - break; - case Command::LESS_THAN: - result = lhs < rhs; - break; - case Command::GREATER_THAN_EQUALS: - result = lhs >= rhs; - break; - case Command::LESS_THAN_EQUALS: - result = lhs <= rhs; - break; - case Command::NOT_EQUAL: - result = lhs != rhs; - break; - case Command::AND: - result = lhs && rhs; - break; - case Command::OR: - result = lhs || rhs; - break; - case Command::MIN: - result = lhs < rhs ? lhs : rhs; - break; - case Command::MAX: - result = lhs > rhs ? lhs : rhs; - break; - } - - // Push result onto the stack - _eval.push(result); - } - - void runner_impl::run_unary_operator(unsigned char cmd) - { - // Pop - value v = _eval.pop(); - value result; - - // Run command - switch ((Command)cmd) - { - case Command::NEGATE: - result = -v; - break; - case Command::NOT: - result = !v; - break; - } - - // Push to the stack - _eval.push(result); - } - frame_type runner_impl::execute_return() { // Pop the callstack @@ -226,7 +146,7 @@ namespace ink::runtime::internal // SPECIAL: On function, do a trim if (type == frame_type::function) - _output << func_end; + _output << values::func_end; // Jump to the old offset inkAssert(_story->instructions() + offset < _story->end(), "Callstack return is outside bounds of story!"); @@ -237,7 +157,9 @@ namespace ink::runtime::internal } runner_impl::runner_impl(const story_impl* data, globals global) - : _story(data), _globals(global.cast()), _container(~0), _threads(~0), + : + _story(data), _globals(global.cast()), _container(~0), _threads(~0), + _operations(_globals->strings()), _threadDone(nullptr, (ip_t)~0), _backup(nullptr), _done(nullptr), _choices() { _ptr = _story->instructions(); @@ -520,13 +442,9 @@ namespace ink::runtime::internal set_done_ptr(nullptr); } - if (cmd >= Command::BINARY_OPERATORS_START && cmd <= Command::BINARY_OPERATORS_END) - { - run_binary_operator((unsigned char)cmd); - } - else if (cmd >= Command::UNARY_OPERATORS_START && cmd <= Command::UNARY_OPERATORS_END) + if (cmd >= Command::OP_BEGIN && cmd < Command::OP_END) { - run_unary_operator((unsigned char)cmd); + _operations(cmd, _eval); } else switch (cmd) { @@ -535,16 +453,16 @@ namespace ink::runtime::internal { const char* str = read(); if (bEvaluationMode) - _eval.push(str); + _eval.push(value{}.set(str)); else - _output << str; + _output << value{}.set(str); } break; case Command::INT: { int val = read(); if (bEvaluationMode) - _eval.push(val); + _eval.push(value{}.set(val)); // TEST-CASE B006 don't print integers } break; @@ -552,7 +470,7 @@ namespace ink::runtime::internal { float val = read(); if (bEvaluationMode) - _eval.push(val); + _eval.push(value{}.set(val)); // TEST-CASE B006 don't print floats } break; @@ -563,29 +481,29 @@ namespace ink::runtime::internal // Push the divert target onto the stack uint32_t target = read(); - _eval.push(value(target)); + _eval.push(value{}.set(target)); } break; case Command::NEWLINE: { if (bEvaluationMode) - _eval.push(newline); + _eval.push(values::newline); else - _output << newline; + _output << values::newline; } break; case Command::GLUE: { if (bEvaluationMode) - _eval.push(glue); + _eval.push(values::glue); else - _output << glue; + _output << values::glue; } break; case Command::VOID: { if (bEvaluationMode) - _eval.push(0); // TODO: void type? + _eval.push(values::null); // TODO: void type? } break; @@ -596,7 +514,7 @@ namespace ink::runtime::internal uint32_t target = read(); // Check for condition - if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop()) + if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().get()) break; // SPECIAL: Fallthrough divert. We're starting to fall out of containers @@ -616,7 +534,7 @@ namespace ink::runtime::internal if (_stack.has_frame(&type) && type == frame_type::function) // implicit return is only for functions { // push null and return - _eval.push(value()); + _eval.push(values::null); // HACK _ptr += sizeof(Command) + sizeof(CommandFlag); @@ -640,13 +558,13 @@ namespace ink::runtime::internal hash_t variable = read(); // Check for condition - if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop()) + if (flag & CommandFlag::DIVERT_HAS_CONDITION && !_eval.pop().get()) break; const value& val = *_stack.get(variable); // Move to location - jump(_story->instructions() + val.as_divert()); + jump(_story->instructions() + val.get()); inkAssert(_ptr < _story->end(), "Diverted past end of story data!"); } break; @@ -666,14 +584,20 @@ namespace ink::runtime::internal { // add a function start marker if (cmd == Command::FUNCTION) - _output << func_start; + _output << values::func_start; // Find divert address uint32_t target = read(); // Push next address onto the callstack - _stack.push_frame(_ptr - _story->instructions(), - cmd == Command::FUNCTION ? frame_type::function : frame_type::tunnel); + { + size_t address = _ptr - _story->instructions(); + if (cmd == Command::FUNCTION) { + _stack.push_frame(address); + } else { + _stack.push_frame(address); + } + } // Do the jump inkAssert(_story->instructions() + target < _story->end(), "Diverting past end of story data!"); @@ -692,7 +616,7 @@ namespace ink::runtime::internal // Push a thread frame so we can return easily // TODO We push ahead of a single divert. Is that correct in all cases....????? auto returnTo = _ptr + CommandSize; - _stack.push_frame(returnTo - _story->instructions(), frame_type::thread); + _stack.push_frame(returnTo - _story->instructions()); // Fork a new thread on the callstack thread_t thread = _stack.fork_thread(); @@ -803,7 +727,7 @@ namespace ink::runtime::internal { inkAssert(bEvaluationMode, "Can not enter string mode while not in evaluation mode!"); bEvaluationMode = false; - _output << marker; + _output << values::marker; } break; case Command::END_STR: { @@ -845,12 +769,12 @@ namespace ink::runtime::internal // Choice is conditional if (flag & CommandFlag::CHOICE_HAS_CONDITION) { // Only show if the top of the eval stack is 'truthy' - if (!_eval.pop()) + if (!_eval.pop().get()) break; } // Use a marker to start compiling the choice text - _output << marker; + _output << values::marker; value stack[2]; int sc = 0; @@ -918,7 +842,7 @@ namespace ink::runtime::internal { // Push the visit count for the current container to the top // is 0-indexed for some reason. idk why but this is what ink expects - _eval.push((int)_globals->visits(_container.top()) - 1); + _eval.push(value{}.set((int)_globals->visits(_container.top()) - 1)); } break; case Command::SEQUENCE: { @@ -926,19 +850,18 @@ namespace ink::runtime::internal // to make sure each element is picked at least once in every // iteration loop. I don't feel like replicating that right now. // So, let's just return a random number and *shrug* - int sequenceLength = _eval.pop(); - int index = _eval.pop(); + int sequenceLength = _eval.pop().get(); + int index = _eval.pop().get(); - _eval.push(_rng.rand(sequenceLength)); + _eval.push(value{}.set(_rng.rand(sequenceLength))); } break; case Command::SEED: { // TODO: Platform independance - int32_t seed = _eval.pop(); + int32_t seed = _eval.pop().get(); _rng.srand(seed); - // push void (TODO) - _eval.push(0); + _eval.push(values::null); } break; case Command::READ_COUNT: { @@ -946,7 +869,7 @@ namespace ink::runtime::internal container_t container = read(); // Push the read count for the requested container index - _eval.push((int)_globals->visits(container)); + _eval.push(value{}.set((int)_globals->visits(container))); } break; default: inkAssert(false, "Unrecognized command!"); diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 3b32dc14..6565ddf5 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -16,6 +16,8 @@ #include "runner.h" #include "choice.h" +#include "executioner.h" + namespace ink::runtime::internal { class story_impl; @@ -126,6 +128,7 @@ namespace ink::runtime::internal inline thread_t current_thread() const { return _threads.empty() ? ~0 : _threads.top(); } private: + executer _operations; const story_impl* const _story; story_ptr _globals; @@ -144,7 +147,7 @@ namespace ink::runtime::internal // Evaluation stack bool bEvaluationMode = false; - internal::eval_stack<20> _eval; + internal::eval_stack _eval; bool bSavedEvaluationMode = false; // Keeps track of what threads we're inside diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index fac96cd5..f30bcccb 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -1,7 +1,10 @@ #include "stack.h" +#include "string_table.h" namespace ink::runtime::internal { + using data = value; + using data_type = value_type; basic_stack::basic_stack(entry* data, size_t size) : base(data, size) { @@ -33,14 +36,14 @@ namespace ink::runtime::internal } // If this is an end thread marker, skip over it - if (skip == ~0 && e.data.get_data_type() == data_type::thread_end) { - skip = e.data.as_divert(); + if (skip == ~0 && e.data.type() == value_type::thread_end) { + skip = e.data.get(); } // If we're skipping if (skip != ~0) { // Stop if we get to the start of the thread block - if (e.data.get_data_type() == data_type::thread_start && skip == e.data.as_divert()) { + if (e.data.type() == value_type::thread_start && skip == e.data.get()) { skip = ~0; } @@ -49,10 +52,11 @@ namespace ink::runtime::internal } // Is it a thread start or a jump marker - if (e.name == InvalidHash && (e.data.get_data_type() == data_type::thread_start || e.data.get_data_type() == data_type::jump_marker)) + if (e.name == InvalidHash && (e.data.type() == value_type::thread_start || e.data.type() == value_type::jump_marker)) { // If this thread start has a jump value - uint32_t jump = e.data.thread_jump(); + // FIXME: used thread_jump before + uint32_t jump = e.data.get(); // Then we need to do some jumping. Skip if (jump > 0) { @@ -100,24 +104,20 @@ namespace ink::runtime::internal return nullptr; } - void basic_stack::push_frame(offset_t return_to, frame_type type) + template<> + void basic_stack::push_frame(offset_t return_to) { - data_type frameDataType; - switch (type) - { - case frame_type::tunnel: - frameDataType = data_type::tunnel_frame; - break; - case frame_type::function: - frameDataType = data_type::function_frame; - break; - case frame_type::thread: - frameDataType = data_type::thread_frame; - break; - } - - // Add to top of stack - add(InvalidHash, value(return_to, frameDataType)); + add(InvalidHash, value{}.set(return_to)); + } + template<> + void basic_stack::push_frame(offset_t return_to) + { + add(InvalidHash, value{}.set(return_to)); + } + template<> + void basic_stack::push_frame(offset_t return_to) + { + add(InvalidHash, value{}.set(return_to)); } const entry* basic_stack::pop() @@ -131,7 +131,10 @@ namespace ink::runtime::internal iterator threadIter = jumpStart; // Get a reference to its jump count - uint32_t& jump = threadIter.get()->data.thread_jump(); + // FIXME: used thread_jump before + // FIXME: reintrudce refernce? + value_type vt = threadIter.get()->data.type(); + uint32_t jump = threadIter.get()->data.get(); // Move over it threadIter.next(); @@ -141,15 +144,19 @@ namespace ink::runtime::internal threadIter.next(); // Now keep iterating back until we get to a frame marker - while (!threadIter.done() && (threadIter.get()->name != InvalidHash || threadIter.get()->data.is_thread_marker())) + // FIXME: meta types or subtypes? + while (!threadIter.done() && (threadIter.get()->name != InvalidHash + || threadIter.get()->data.type() == value_type::thread_start + || threadIter.get()->data.type() == value_type::thread_end)) { // If we've hit an end of thread marker auto e = threadIter.get(); - if (e->data.is_thread_end()) + if (e->data.type() == value_type::thread_end) { // We basically want to skip until we get to the start of this thread (leave the block alone) - thread_t tid = e->data.as_thread_id(); - while (!threadIter.get()->data.is_thread_start() || threadIter.get()->data.as_thread_id() != tid) + thread_t tid = e->data.get(); + while (threadIter.get()->data.type() != value_type::thread_start + || threadIter.get()->data.get() != tid) { jump++; threadIter.next(); @@ -166,6 +173,13 @@ namespace ink::runtime::internal jump++; // Now that thread marker is set to the correct jump value. + if (vt == value_type::jump_marker) { + threadIter.get()->data.set(jump); + } else if (vt == value_type::thread_start) { + threadIter.get()->data.set(jump); + } else { + throw ink_exception("unknown jump type"); + } return threadIter.get(); } @@ -206,14 +220,18 @@ namespace ink::runtime::internal // We now have a frame marker. Check if it's a thread // Thread handling - if (frame->data.is_thread_marker() || frame->data.is_jump_marker()) + if ( + // FIXME: is_tghead_marker, is_jump_marker + frame->data.type() == value_type::thread_start + || frame->data.type() == value_type::thread_end + || frame->data.type() == value_type::jump_marker + ) { // End of thread marker, we need to create a jump marker - if (frame->data.get_data_type() == data_type::thread_end) + if (frame->data.type() == data_type::thread_end) { // Push a new jump marker after the thread end - entry& jump = push({ InvalidHash, value(0, data_type::jump_marker) }); - jump.data.thread_jump() = 0; + entry& jump = push({ InvalidHash, value{}.set(0u) }); // Do a pop back returnedFrame = do_thread_jump_pop(base::begin()); @@ -221,7 +239,7 @@ namespace ink::runtime::internal } // If this is a jump marker, we actually want to extend it to the next frame - if (frame->data.is_jump_marker()) + if (frame->data.type() == value_type::jump_marker) { // Use the thread jump pop method using this jump marker returnedFrame = do_thread_jump_pop(iter); @@ -229,7 +247,7 @@ namespace ink::runtime::internal } // Popping past thread start - if (frame->data.get_data_type() == data_type::thread_start) + if (frame->data.type() == data_type::thread_start) { returnedFrame = do_thread_jump_pop(iter); break; @@ -245,17 +263,18 @@ namespace ink::runtime::internal inkAssert(returnedFrame, "Attempting to pop_frame when no frames exist! Stack reset."); // Make sure we're not somehow trying to "return" from a thread - inkAssert(returnedFrame->data.get_data_type() != data_type::thread_start && returnedFrame->data.get_data_type() != data_type::thread_end, + inkAssert(returnedFrame->data.type() != data_type::thread_start && returnedFrame->data.type() != data_type::thread_end, "Can not return from a thread! How did this happen?"); // Store frame type if (type != nullptr) { - *type = get_frame_type(returnedFrame->data.get_data_type()); + *type = get_frame_type(returnedFrame->data.type()); } // Return the offset stored in the frame record - return returnedFrame->data.as_divert(); + // FIXME: correct type? + return returnedFrame->data.get(); } bool basic_stack::has_frame(frame_type* returnType) const @@ -280,21 +299,22 @@ namespace ink::runtime::internal // If we're skipping over a thread, wait until we hit its start before checking if (thread != ~0) { - if (elem.data.is_thread_start() && elem.data.as_thread_id() == thread) + if (elem.data.type() == value_type::thread_start && elem.data.get() == thread) thread = ~0; return false; } // If it's a jump marker or a thread start - if (elem.data.is_jump_marker() || elem.data.is_thread_start()) { - jumping = elem.data.thread_jump(); + if (elem.data.type() == value_type::jump_marker || elem.data.type() == value_type::thread_start) { + // FIXME: used thread_jump before + jumping = elem.data.get(); return false; } // If it's a thread end, we need to skip to the matching thread start - if (elem.data.is_thread_end()) { - thread = elem.data.as_thread_id(); + if (elem.data.type() == value_type::thread_end) { + thread = elem.data.get(); return false; } @@ -302,7 +322,7 @@ namespace ink::runtime::internal }); if (frame != nullptr && returnType != nullptr) - *returnType = get_frame_type(frame->data.get_data_type()); + *returnType = get_frame_type(frame->data.type()); // Return true if a frame was found return frame != nullptr; @@ -316,7 +336,12 @@ namespace ink::runtime::internal void basic_stack::mark_strings(string_table& strings) const { // Mark all strings - base::for_each_all([&strings](const entry& elem) { elem.data.mark_strings(strings); }); + base::for_each_all( + [&strings](const entry& elem) { + if (elem.data.type() == value_type::string) { + strings.mark_used(elem.data.get()); + } + }); } thread_t basic_stack::fork_thread() @@ -325,11 +350,11 @@ namespace ink::runtime::internal thread_t new_thread = _next_thread++; // Push a thread start marker here - entry& thread_entry = add(InvalidHash, value(new_thread, data_type::thread_start)); + entry& thread_entry = add(InvalidHash, value{}.set(new_thread)); // Set stack jump counter for thread to 0. This number is used if the thread ever // tries to pop past its origin. It keeps track of how much of the preceeding stack it's popped back - thread_entry.data.thread_jump() = 0; + // FIXME: second value thread_entry.data.thread_jump() = 0; return new_thread; } @@ -337,7 +362,7 @@ namespace ink::runtime::internal void basic_stack::complete_thread(thread_t thread) { // Add a thread complete marker - add(InvalidHash, value(thread, data_type::thread_end)); + add(InvalidHash, value{}.set(thread)); } void basic_stack::collapse_to_thread(thread_t thread) @@ -351,8 +376,8 @@ namespace ink::runtime::internal // Keep popping until we find the requested thread's end marker const entry* top = pop(); while (!( - top->data.get_data_type() == data_type::thread_end && - top->data.as_divert() == thread)) + top->data.type() == data_type::thread_end && + top->data.get() == thread)) { inkAssert(!is_empty(), "Ran out of stack while searching for end of thread marker. Did you call complete_thread?"); top = pop(); @@ -373,14 +398,14 @@ namespace ink::runtime::internal } // Thread end. We just need to delete this whole block - if (nulling == ~0 && elem.data.is_thread_end() && elem.name == InvalidHash) { - nulling = elem.data.as_divert(); + if (nulling == ~0 && elem.data.type() == value_type::thread_end && elem.name == InvalidHash) { + nulling = elem.data.get(); } // If we're deleting a useless thread block if (nulling != ~0) { // If this is the start of the block, stop deleting - if (elem.name == InvalidHash && elem.data.get_data_type() == data_type::thread_start && elem.data.as_divert() == nulling) { + if (elem.name == InvalidHash && elem.data.type() == data_type::thread_start && elem.data.get() == nulling) { nulling = ~0; } @@ -390,16 +415,18 @@ namespace ink::runtime::internal else { // Clear thread start markers. We don't need or want them anymore - if (elem.name == InvalidHash && (elem.data.is_thread_start() || elem.data.is_jump_marker())) { + if (elem.name == InvalidHash && + (elem.data.type() == value_type::thread_start || elem.data.type() == value_type::jump_marker)) { // Clear it out elem.name = NulledHashId; // Check if this is a jump, if so we need to ignore even more data - jumping = elem.data.thread_jump(); + // FIXME: used thread_jump before + jumping = elem.data.get(); } // Clear thread frame markers. We can't use them anymore - if (elem.name == InvalidHash && elem.data.get_data_type() == data_type::thread_frame) { + if (elem.name == InvalidHash && elem.data.type() == data_type::thread_frame) { elem.name = NulledHashId; } } @@ -449,7 +476,7 @@ namespace ink::runtime::internal value basic_eval_stack::pop() { - return base::pop([](const value& v) { return v.is_none(); }); + return base::pop([](const value& v) { return v.type() == value_type::none; }); } const value& basic_eval_stack::top() const @@ -470,7 +497,11 @@ namespace ink::runtime::internal void basic_eval_stack::mark_strings(string_table& strings) const { // Iterate everything (including what we have saved) and mark strings - base::for_each_all([&strings](const value& elem) { elem.mark_strings(strings); }); + base::for_each_all([&strings](const value& elem) { + if (elem.type() == value_type::string) { + strings.mark_used(elem.get()); + } + }); } void basic_eval_stack::save() @@ -486,7 +517,7 @@ namespace ink::runtime::internal void basic_eval_stack::forget() { // Clear out - data x; x.set_none(); + data x; x.set(); value none = value(x); base::forget([&none](value& elem) { elem = none; }); } diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 29292bb9..5bb062cc 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -9,6 +9,7 @@ namespace ink { namespace internal { + class string_table; struct entry { hash_t name; @@ -39,7 +40,8 @@ namespace ink value* get(hash_t name); // pushes a new frame onto the stack - void push_frame(offset_t return_to, frame_type type); + template + void push_frame(offset_t return_to); // Pops a frame (and all temporary variables) from the callstack. offset_t pop_frame(frame_type* type); @@ -125,9 +127,9 @@ namespace ink void forget(); }; - template class eval_stack : public basic_eval_stack { + static constexpr size_t N = 20; public: eval_stack() : basic_eval_stack(&_stack[0], N) { } private: diff --git a/inkcpp/string_operations.h b/inkcpp/string_operations.h new file mode 100644 index 00000000..a4777b0e --- /dev/null +++ b/inkcpp/string_operations.h @@ -0,0 +1,15 @@ +#pragma once + +#include "executioner.h" +#include "operation_bases.h" + +namespace ink::runtime::internal { + + template + class operation : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + } + } +} diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 6da899c5..397a7333 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -1,537 +1,35 @@ #include "value.h" #include "output.h" -#include "string_table.h" -#include "string_utils.h" -namespace ink +namespace ink::runtime::internal { - namespace runtime - { - namespace internal - { - value::value() - { - _data[0].set_void(); - for (size_t i = 1; i < VALUE_DATA_LENGTH; i++) - _data[i].set_none(); - } - - value::value(int val) : value() - { - _first.set_int(val); - } - - value::value(float val) : value() - { - _first.set_float(val); - } - - value::value(const char* str, bool allocated) : value() - { - _first.set_string(str, allocated); - } - - value::value(uint32_t val) : value() - { - _first.set_uint(val); - } - - value::value(const data& d) : value() - { - _first = d; - } - - value::value(uint32_t val, internal::data_type t) - { - _first.set_uint(val); - _first.type = t; - } - - value_type value::type() const - { - // If we have multiple values set, then we are a string - if (_second.type != data_type::none) - return value_type::string; - - switch (_first.type) - { - case data_type::int32: - return value_type::integer; - case data_type::uint32: - return value_type::divert; - case data_type::float32: - return value_type::decimal; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: - return value_type::string; - case data_type::null: - return value_type::null; - default: - inkFail("Invalid data in value container!"); - } - } - - void value::cast(value& val, value_type old_type, value_type new_type) - { - if (old_type == new_type) - return; - - inkAssert(old_type < new_type, "Can only cast values upwards!"); - inkAssert(new_type != value_type::null && old_type != value_type::null, "Can not cast void values"); - inkAssert(new_type != value_type::divert && old_type != value_type::divert, "Can not cast divert values"); - - // We do not actually convert floats/ints to strings here. - // Instead, we just pass along the float and it is appended to - // another value's data stream - if (new_type == value_type::string) - return; - - switch (old_type) - { - case value_type::integer: - if (new_type == value_type::decimal) - val = value((float)val._first.integer_value); - return; - case value_type::decimal: - if (new_type == value_type::integer) - val = value((int)val._first.float_value); - return; - } - - inkAssert(false, "Invalid value cast"); - } - - value_type value::maybe_cast(value& left, value& right) - { - // Check the types of the two values. If they're the same, nothing to do. - auto l_type = left.type(), r_type = right.type(); - if (l_type == r_type) - return l_type; - - // Find the "largest" type - value_type type = l_type > r_type ? l_type : r_type; - - // Cast - value::cast(left, l_type, type); - value::cast(right, r_type, type); - - // Return new type - return type; - } - - void value::mark_strings(string_table& strings) const - { - // mark any allocated strings we're using - for (int i = 0; i < VALUE_DATA_LENGTH; i++) - { - switch (_data[i].type) - { - case data_type::allocated_string_pointer: - strings.mark_used(_data[i].string_val); - break; - case data_type::none: - return; - } - } - } - - bool value::is_truthy() const - { - // Concatenated strings are true - if (_second.type != data_type::none) - return true; - - switch (_first.type) - { - case data_type::int32: - return _first.integer_value != 0; - case data_type::float32: - return _first.float_value != 0.0f; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: - return _first.string_val[0] != '\0'; - } - - inkFail("Invalid type to check for truthy"); - } - - void value::append_to(basic_stream& out) const - { - size_t i = 0; - for (; i < VALUE_DATA_LENGTH; i++) - { - if (_data[i].type == data_type::none) - break; - } - out.append(&_first, i);; - } - - void value::load_from(basic_stream& in) - { - in.get(&_first, VALUE_DATA_LENGTH); - } - - value value::add(value left, value right, basic_stream& stream, string_table& table) - { - // Cast as needed - value_type new_type = maybe_cast(left, right); - - switch (new_type) - { - case value_type::integer: - return left.as_int() + right.as_int(); - case value_type::decimal: - return left.as_float() + right.as_float(); - case value_type::string: - { - // Create empty value - value new_value = value(); - - // Copy left values into new - int i = 0, j = 0; - bool overflow = false; - while (j < VALUE_DATA_LENGTH && left._data[j].type != data_type::none && !overflow) - { - if (i >= VALUE_DATA_LENGTH) - { - overflow = true; - break; - } - new_value._data[i++] = left._data[j++]; - } - - // Copy right values into new - j = 0; - while (j < VALUE_DATA_LENGTH && right._data[j].type != data_type::none && !overflow) - { - if (i >= VALUE_DATA_LENGTH) - { - overflow = true; - break; - } - new_value._data[i++] = right._data[j++]; - } - - // Todo: Use string buffer for dynamic allocation! - if (overflow) - { - // Add a marker - stream << marker; - - // Push everything into the stream - j = 0; - while (j < VALUE_DATA_LENGTH && left._data[j].type != data_type::none) - stream << left._data[j++]; - j = 0; - while (j < VALUE_DATA_LENGTH && right._data[j].type != data_type::none) - stream << right._data[j++]; - - // Pull out into a new string - return value(stream.get_alloc(table), true); - } - - return new_value; - } - } - - inkFail("Invalid type for add"); - } - - // TODO: Macro to make defining these easier when there's no string involvement? - - value value::subtract(value left, value right) - { - // Cast as needed - value_type new_type = maybe_cast(left, right); - - switch (new_type) - { - case value_type::integer: - return left.as_int() - right.as_int(); - case value_type::decimal: - return left.as_float() - right.as_float(); - } - - inkFail("Invalid type for subtract"); - } - - value value::multiply(value left, value right) - { - // Cast as needed - value_type new_type = maybe_cast(left, right); - - switch (new_type) - { - case value_type::integer: - return left.as_int() * right.as_int(); - case value_type::decimal: - return left.as_float() * right.as_float(); - } - - inkFail("Invalid type for multiply"); - } - - value value::divide(value left, value right) - { - // Cast as needed - value_type new_type = maybe_cast(left, right); - - switch (new_type) - { - case value_type::integer: - return left.as_int() / right.as_int(); - case value_type::decimal: - return left.as_float() / right.as_float(); - } - - inkFail("Invalid type for divide"); - } - - value value::mod(value left, value right) - { - // Cast as needed - value_type new_type = maybe_cast(left, right); - - switch (new_type) - { - case value_type::integer: - return left.as_int() % right.as_int(); - } - - inkFail("Invalid type for mod"); - } - - void convert_to_string(const char*& c, const data& d, char* number) { - c = number; - switch(d.type) { - case data_type::int32: - snprintf(number, 32, "%d", d.integer_value); - break; - case data_type::uint32: - snprintf(number, 32, "%d", d.uint_value); - break; - case data_type::float32: - snprintf(number, 32, "%f", d.float_value); - break; - case data_type::newline: - number[0] = '\n'; - number[1] = 0; - break; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: - c = d.string_val; - break; - default: number[0] = 0; - } - } - - void value::finalize_string(string_table& table) const { - constexpr size_t MaxSize = 256; // max size for no string element - char buffer[VALUE_DATA_LENGTH][MaxSize]; - const char* strs[VALUE_DATA_LENGTH]; - char null = 0; - - size_t len = 0; - for (int i = 0; i < VALUE_DATA_LENGTH; ++i) { - switch(_data[i].type) { - case data_type::float32: - strs[i] = buffer[i]; - toStr(buffer[i], MaxSize, _data[i].float_value); - break; - case data_type::uint32: - strs[i] = buffer[i]; - toStr(buffer[i], MaxSize, _data[i].uint_value); - break; - case data_type::int32: - strs[i] = buffer[i]; - toStr(buffer[i], MaxSize, _data[i].integer_value); - break; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: - strs[i] = _data[i].string_val; - break; - default: strs[i] = &null; - } - _data[i].set_none(); - len += strlen(strs[i]); - } - char* str = table.create(len+1); - table.mark_used(str); - - char* ptr = str; - for (int i = 0; i < VALUE_DATA_LENGTH; ++i) { - for(const char* c = strs[i]; *c; ++c){ - *ptr++ = *c; - } - } - *ptr = 0; - _data[0].set_string(str, true); - } - - const char* value::as_str(string_table& table) const { - finalize_string(table); - return _first.string_val; - } - - const char * const * value::as_str_ptr(string_table& table) const { - finalize_string(table); - return &_first.string_val; - } - - bool value::compare_string(const value& left, const value& right) { - // convert fields to string representation and start comparison - // when the end of one field is reached, the other still has - // bytes left -> convert the next field and continue comparison - - // iterator for data fields of left and right value - size_t l_i = 0; - size_t r_i = 0; - // buffer to store string representation of numeric fields - char l_number[32]; l_number[0] = 0; - char r_number[32]; r_number[0] = 0; - // current compare position, if *l = 0 -> field compare finish - const char* l_c = l_number; - const char* r_c = r_number; - bool res = true; - // while no different found and fields to check remain - while(res && l_i < VALUE_DATA_LENGTH && r_i < VALUE_DATA_LENGTH) { - // if one field has left overs - if (*l_c || *r_c) - { - // fetch the next field of the value without leftover - if (*l_c) { - convert_to_string(r_c, right._data[r_i], r_number); - } else { - convert_to_string(l_c, left._data[l_i], l_number); - } - } - // if both values are aligned: check if both have the same type - else if (left._data[l_i].type == right._data[r_i].type) - { - bool tmp_res = true; - switch(left._data[l_i].type) { - case data_type::int32: - tmp_res = left._data[l_i].integer_value == right._data[r_i].integer_value; - break; - case data_type::uint32: - tmp_res = left._data[l_i].uint_value == right._data[r_i].uint_value; - break; - case data_type::float32: - tmp_res = left._data[l_i].float_value == right._data[r_i].float_value; - break; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: - l_c = left._data[l_i].string_val; - r_c = right._data[r_i].string_val; - break; - default: break; - } - // check if maybe the missing part is in next data - if (!tmp_res) { - convert_to_string(r_c, right._data[r_i], r_number); - convert_to_string(l_c, left._data[l_i], l_number); - } - } - // convert both to string and compare - else - { - convert_to_string(r_c, right._data[r_i], r_number); - convert_to_string(l_c, left._data[l_i], l_number); - } - // compare string representation until one reaches the end - while(*l_c && *r_c) { - // if different found: stop and set result to false - if (*l_c != *r_c) { - res = false; break; - } - ++l_c; ++r_c; - } - // if field is finished advance to the next - if (!*l_c){ ++l_i; } - if (!*r_c){ ++r_i; } - } - // if one value not complete compared -> leftover witch not match - if (res && l_i != r_i) { - const value& v = l_i < r_i ? left : right; - int i = l_i < r_i ? l_i : r_i; - // check if leftover fields all empty - while(v._data[i].type != data_type::none) { - if (v._data[i].type != data_type::string_table_pointer - && v._data[i].type != data_type::allocated_string_pointer) { - if (*v._data[i].string_val != 0) { - return false; - } - } else { - return false; - } - ++i; - } - } - return res; - } - - value value::is_equal(value left, value right) - { - // Cast as needed - value_type new_type = maybe_cast(left, right); - - switch (new_type) - { - case value_type::integer: - return left.as_int() == right.as_int(); - case value_type::decimal: - return left.as_float() == right.as_float(); - case value_type::string: - return compare_string(left, right); - case value_type::divert: - return left.as_divert() == right.as_divert(); - } - - inkFail("Invalid type for is_equal"); - } - - value value::less_than(value left, value right) - { - // Cast as needed - value_type new_type = maybe_cast(left, right); - - switch (new_type) - { - case value_type::integer: - return left.as_int() < right.as_int(); - case value_type::decimal: - return left.as_float() < right.as_float(); - } - - inkFail("Invalid type for less_than"); - } - - value value::negate(const value& val) - { - inkAssert(val._second.type == data_type::none, "Can not negate strings"); - - switch (val._first.type) - { - case data_type::int32: - return -val._first.integer_value; - case data_type::float32: - return -val._first.float_value; - } - - inkFail("Invalid type for negate"); +#ifdef INK_ENABLE_STL + template + void append(std::ostream& os, const value& val) { + if constexpr (ty != value_type::PRINT_END) { + if (ty == val.type()) { + os << val.get(); + } else { + append(os, val); } + } + } + std::ostream& operator<<(std::ostream& os, const value& val) { + if (val.type() < value_type::PRINT_BEGIN || val.type() >= value_type::PRINT_END) { + throw ink_exception("printing this type is not supported"); + } + append(os, val); + return os; + } +#endif - basic_stream& operator>>(basic_stream& in, value& out) - { - out.load_from(in); - return in; - } + basic_stream& operator<<(basic_stream& os, value val) { + os.append(val); + return os; + } - basic_stream& operator<<(basic_stream& out, const value& in) - { - in.append_to(out); - return out; - } - } + basic_stream& operator>>(basic_stream& is, value val) { + is.get(&val, 1); + return is; } } diff --git a/inkcpp/value.h b/inkcpp/value.h index 9303dd25..ffd2a4b2 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -1,232 +1,248 @@ #pragma once #include "system.h" +#include "../shared/private/command.h" #ifdef INK_ENABLE_STL -#include +#include #endif -namespace ink -{ - namespace runtime - { - namespace internal - { - class string_table; - class list_table; - - struct list_element { - uint16_t list_id; - uint16_t flag_id; - }; - - // Data types that can be held internally within the ink runtime - enum class data_type - { - none, // blank. used when data is in a fixed array - int32, // 32-bit integer value - float32, // 32-bit floating point value - uint32, // 32-bit unsigned integer value - string_table_pointer, // Represents an offset within the story's constant string table - allocated_string_pointer, // Represents an offset within the runner's allocated string table - list, // list: referenced in list_table - list_element, // one flag of one list - marker, // Special marker (used in output stream) - glue, // Glue. - newline, // \n - func_start, // Start of function marker - func_end, // End of function marker - null, // void/null (used for void function returns) - tunnel_frame, // Return from tunnel - function_frame, // Return from function - thread_frame, // Special tunnel marker for returning from threads - thread_start, // Start of a new thread frame - thread_end, // End of a thread frame - jump_marker, // Used to mark a callstack jump - }; - - // Container for any data used as part of the runtime (variable values, output streams, evaluation stack, etc.) - struct data - { - // Type of data - data_type type; - - union - { - int integer_value; - uint32_t uint_value; - float float_value; - const char* string_val; - list_element list_element_value; - // TODO: Do we need a marker type? - }; - - inline void set_none() { type = data_type::none; } - inline void set_void() { type = data_type::null; } - inline void set_int(int val) { type = data_type::int32; integer_value = val; } - inline void set_uint(uint32_t val) { type = data_type::uint32; uint_value = val; } - inline void set_float(float val) { type = data_type::float32; float_value = val; } - inline void set_list_element(list_element elm) { type = data_type::list_element; list_element_value = elm; } - inline void set_string(const char* val, bool allocated) { type = allocated ? data_type::allocated_string_pointer : data_type::string_table_pointer; string_val = val; } - }; - - // TODO: Re-implement. Failed on 64-bit builds. - //static_assert(sizeof(data) == sizeof(data_type) + sizeof(offset_t), "No data type should take up more than 32 bits"); - - // Types of values - enum class value_type - { - null, - divert, - integer, - decimal, - list_element, - list, - string, - }; - - class basic_stream; - - // Used to store values on the evaluation stack or variables. - class value - { - public: - value(); // Creates a value with the "none" type - value(int); // Create a new int value - value(float); // Create a new float value - value(uint32_t); // Create a new divert value - value(list_element); - value(const data&); // Create value from data - value(uint32_t, data_type); // Create divert with type - - // Create a new string value (must specify whether or not it's an allocated or story string) - value(const char*, bool allocated = false); - - // Check the value's current type - value_type type() const; - bool is_none() const { return _first.type == data_type::none; } - data_type get_data_type() const { return _first.type; } - - // == Getters == - int as_int() const { return _first.integer_value; } - int* as_int_ptr() { return &_first.integer_value; } - const int* as_int_ptr() const { return &_first.integer_value; } - float as_float() const { return _first.float_value; } - float* as_float_ptr() { return &_first.float_value; } - const float* as_float_ptr() const { return &_first.float_value; } - uint32_t as_divert() const { return _first.uint_value; } - uint32_t as_thread_id() const { return _first.uint_value; } - uint32_t* as_uint_ptr() { return &_first.uint_value; } - const uint32_t* as_uint_ptr() const { return &_first.uint_value; } - const char* as_str(string_table&) const; - const char* const * as_str_ptr(string_table&) const; - uint32_t as_list_id() const { return _first.uint_value; } - - template - T get() const { static_assert(always_false::value, "Type not supported by value class"); } - - // Garbage collection - void mark_strings(string_table&) const; - - inline operator int() const { return as_int(); } - inline operator float() const { return as_float(); } - inline operator uint32_t() const { return as_divert(); } - - // == Threading == - inline bool is_thread_marker() const { return _first.type == data_type::thread_start || _first.type == data_type::thread_end; } - inline bool is_thread_end() const { return _first.type == data_type::thread_end; } - inline bool is_thread_start() const { return _first.type == data_type::thread_start; } - inline bool is_jump_marker() const { return _first.type == data_type::jump_marker; } - inline uint32_t& thread_jump() { return _second.uint_value; } - inline uint32_t thread_jump() const { return _second.uint_value; } - - // Is this value "true" - bool is_truthy() const; - inline operator bool() const { return is_truthy(); } - - void append_to(basic_stream&) const; - void load_from(basic_stream&); - - // == Binary operations == - static value add(value, value, basic_stream&, string_table&); - static value subtract(value, value); - static value multiply(value, value); - static value divide(value, value); - static value mod(value, value); - static value is_equal(value, value); - static value less_than(value, value); - - // == Unary operations - static value negate(const value&); - - private: - static void cast(value&, value_type, value_type); - static value_type maybe_cast(value& left, value& right); - /** - * @brief compare if the string representation of values are equal. - */ - static bool compare_string(const value& left, const value& right); - /** - * @brief compress string values to one data field - */ - void finalize_string(string_table&) const; - - private: - // Maximum sequential data a value can have - static const size_t VALUE_DATA_LENGTH = 4; - - union - { - // Quick access struct - struct - { - data _first; - data _second; - }; - - // Data array - mutable data _data[VALUE_DATA_LENGTH]; - }; - - }; - - // == Binary Operators == - // inline value operator+(const value& lhs, const value& rhs) { return value::add(lhs, rhs); } - inline value operator-(const value& lhs, const value& rhs) { return value::subtract(lhs, rhs); } - inline value operator*(const value& lhs, const value& rhs) { return value::multiply(lhs, rhs); } - inline value operator/(const value& lhs, const value& rhs) { return value::divide(lhs, rhs); } - inline value operator%(const value& lhs, const value& rhs) { return value::mod(lhs, rhs); } - inline value operator-(const value& val) { return value::negate(val); } - - inline value operator==(const value& lhs, const value& rhs) { return value::is_equal(lhs, rhs); } - inline value operator!=(const value& lhs, const value& rhs) { return !value::is_equal(lhs, rhs); } - inline value operator<(const value& lhs, const value& rhs) { return value::less_than(lhs, rhs); } - inline value operator<=(const value& lhs, const value& rhs) { return value::less_than(lhs, rhs) || value::is_equal(lhs, rhs); } - inline value operator>(const value& lhs, const value& rhs) { return !(lhs <= rhs); } - inline value operator>=(const value& lhs, const value& rhs) { return !value::less_than(lhs,rhs); } - - // == Stream Operators == - basic_stream& operator <<(basic_stream&, const value&); - basic_stream& operator >>(basic_stream&, value&); - - // == Specialized get functions == - // TODO: Should these assert? - - template<> - inline int value::get() const { return as_int(); } - template<> - inline float value::get() const { return as_float(); } - template<> - inline uint32_t value::get() const { return as_divert(); } + +namespace ink::runtime::internal { + class basic_stream; + enum class value_type { + BEGIN, + none = BEGIN, + divert, + uint32, + PRINT_BEGIN, + boolean = PRINT_BEGIN, + int32, + float32, + string, + PRINT_END, + marker = PRINT_END, + glue, + newline, + func_start, + func_end, + null, + tunnel_frame, + function_frame, + thread_start, + thread_frame, + thread_end, + jump_marker, + END}; + constexpr value_type operator+(value_type t, int i) { + return static_cast(static_cast(t)+i); + } + constexpr Command operator+(Command c, int i) { + return static_cast(static_cast(c)+i); + } + + + class value { + public: + template struct ret { using type = void; }; + + constexpr value() : _type{value_type::none}, uint32_value{0}{} + template + typename ret::type + get() const { static_assert(ty != ty, "No getter for this type defined!"); } + + template + constexpr value& set(Args ...args) { + static_assert(sizeof...(Args)!=sizeof...(Args), "No setter for this type defined!"); + return *this; + } + + value_type type() const { return _type; } + friend basic_stream& operator<<(basic_stream& os, value); + friend basic_stream& operator>>(basic_stream& is, value); + + constexpr bool printable() const { return _type >= value_type::PRINT_BEGIN && _type < value_type::PRINT_END; } + private: + union { + bool bool_value; + int32_t int32_value; + const char* str_value; + uint32_t uint32_value; + float float_value; + }; + value_type _type; + }; #ifdef INK_ENABLE_STL - template<> - inline std::string value::get() const { return _first.string_val; } // TODO: Missing amalgamate? + std::ostream& operator<<(std::ostream&,const value&); #endif -#ifdef INK_ENABLE_UNREAL - template<> - inline FString value::get() const { return _first.string_val; } // TODO: Missing amalgamate? -#endif - } + template<> struct value::ret { using type = int32_t; }; + template<> inline int32_t value::get() const { return int32_value; } + template<> + inline constexpr value& value::set(int32_t v) { + int32_value = v; + _type = value_type::int32; + return *this; + } + + template<> struct value::ret { using type = uint32_t; }; + template<> inline uint32_t value::get() const { return uint32_value; } + template<> + inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::divert; + return *this; + } + + template<> struct value::ret { using type = uint32_t; }; + template<> inline uint32_t value::get() const { return uint32_value; } + template<> + inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::divert; + return *this; + } + + template<> struct value::ret { using type = float; }; + template<> inline float value::get() const { return float_value; } + template<> + inline constexpr value& value::set(float v) { + float_value = v; + _type = value_type::float32; + return *this; + } + + template<> struct value::ret { using type = bool; }; + template<> inline bool value::get() const { return bool_value; } + template<> + inline constexpr value& value::set(bool v) { + bool_value = v; + _type = value_type::boolean; + return *this; + } + + template<> struct value::ret { using type = const char*; }; + template<> inline const char* value::get() const { return str_value; } + template<> + inline constexpr value& value::set(const char* v) { + // TODO: decode allocw + str_value = v; + _type = value_type::string; + return *this; + } + template<> + inline constexpr value& value::set(const char* v, bool allocated) { + // TODO use bool + str_value = v; + _type = value_type::string; + return *this; + } + + template<> struct value::ret { using type = uint32_t; }; + template<> inline uint32_t value::get() const { return uint32_value; } + template<> + inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::jump_marker; + return *this; + } + + template<> struct value::ret { using type = uint32_t; }; + template<> inline uint32_t value::get() const { return uint32_value; } + template<> + inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::thread_start; + return *this; + } + + template<> struct value::ret { using type = uint32_t; }; + template<> inline uint32_t value::get() const { return uint32_value; } + template<> + inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::thread_end; + return *this; + } + + template<> + inline constexpr value& value::set() { + _type = value_type::marker; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::glue; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::newline; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::func_start; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::func_end; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::null; + return *this; + } + + template<> + inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::function_frame; + return *this; + } + template<> + inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::tunnel_frame; + return *this; + } + template<> + inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::thread_frame; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::tunnel_frame; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::function_frame; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::thread_end; + return *this; + } + template<> + inline constexpr value& value::set() { + _type = value_type::none; + return *this; + } + namespace values { + static constexpr value marker = value{}.set(); + static constexpr value glue = value{}.set(); + static constexpr value newline = value{}.set(); + static constexpr value func_start = value{}.set(); + static constexpr value func_end = value{}.set(); + static constexpr value null = value{}.set(); + static constexpr value tunnel_frame = value{}.set(); + static constexpr value function_frame = value{}.set(); + static constexpr value thread_end = value{}.set(); } } diff --git a/shared/private/command.h b/shared/private/command.h index a164be23..d93a4154 100644 --- a/shared/private/command.h +++ b/shared/private/command.h @@ -53,7 +53,8 @@ namespace ink THREAD, // == Binary operators - BINARY_OPERATORS_START, + OP_BEGIN, + BINARY_OPERATORS_START = OP_BEGIN, ADD = BINARY_OPERATORS_START, SUBTRACT, DIVIDE, @@ -76,9 +77,10 @@ namespace ink NOT = UNARY_OPERATORS_START, NEGATE, UNARY_OPERATORS_END = NEGATE, + OP_END = NEGATE + 1, // == Container tracking - START_CONTAINER_MARKER, + START_CONTAINER_MARKER = OP_END, END_CONTAINER_MARKER, // == Function calls From 1ea4e67eecdab27bac1446d7233251d457f65a84 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 21 Feb 2021 00:58:03 +0100 Subject: [PATCH 04/23] Compiling, thread handling error! --- inkcpp/CMakeLists.txt | 1 + inkcpp/casting.h | 9 +- inkcpp/default_operations.h | 42 ++++----- inkcpp/executioner.h | 24 +---- inkcpp/marker_operations.h | 8 ++ inkcpp/numeric_operations.h | 170 +++++++++++++++++++++++++++++++++++ inkcpp/operation_bases.h | 4 +- inkcpp/operations.h | 32 ++++++- inkcpp/runner_impl.cpp | 2 +- inkcpp/runner_impl.h | 2 +- inkcpp/string_operations.cpp | 88 ++++++++++++++++++ inkcpp/string_operations.h | 18 ++-- inkcpp/value.h | 47 ++++++++-- inkcpp_test/Callstack.cpp | 139 ++++++++++++++++------------ inkcpp_test/Value.cpp | 83 ++++++++++------- 15 files changed, 516 insertions(+), 153 deletions(-) create mode 100644 inkcpp/marker_operations.h create mode 100644 inkcpp/numeric_operations.h create mode 100644 inkcpp/string_operations.cpp diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index 6e3137ca..abda8535 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -17,6 +17,7 @@ list(APPEND SOURCES system.cpp value.cpp value.h string_table.h string_table.cpp avl_array.h + string_operations.cpp header.cpp ) source_group(Collections REGULAR_EXPRESSION collections/.*) diff --git a/inkcpp/casting.h b/inkcpp/casting.h index 740f2ee8..22a266f0 100644 --- a/inkcpp/casting.h +++ b/inkcpp/casting.h @@ -7,7 +7,14 @@ namespace ink::runtime::internal { template value_type common_base(const value* vs) { if constexpr (N == 0) { return value_type::none; } - else { return vs->type(); } + else if constexpr (N == 1) { return vs->type(); } + else { + if (vs[0].type() == value_type::string || vs[1].type() == value_type::string) { + return value_type::string; + } else { + return vs[0].type(); + } + } } }; } diff --git a/inkcpp/default_operations.h b/inkcpp/default_operations.h index 9a0e2c1e..922b9a66 100644 --- a/inkcpp/default_operations.h +++ b/inkcpp/default_operations.h @@ -4,25 +4,25 @@ #include "operation_bases.h" namespace ink::runtime::internal { - template + template = true> class operation : operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set( vals[0].get() + vals[1].get() )); } - } + }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set( vals[0].get() - vals[1].get() )); } - } + }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -31,7 +31,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -40,7 +40,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -49,7 +49,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -60,7 +60,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -72,7 +72,7 @@ namespace ink::runtime::internal { }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -83,7 +83,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -95,7 +95,7 @@ namespace ink::runtime::internal { }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -106,7 +106,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -117,7 +117,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -126,7 +126,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -135,7 +135,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -144,7 +144,7 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; @@ -153,21 +153,21 @@ namespace ink::runtime::internal { } }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set(!vals[0].get())); } - } + }; - template + template = true> class operation : operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set(-vals[0].get())); } - } + }; } diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h index 37719a5c..ea3e47e8 100644 --- a/inkcpp/executioner.h +++ b/inkcpp/executioner.h @@ -3,32 +3,13 @@ #include "value.h" #include "casting.h" #include "stack.h" +#include "operations.h" #include -namespace ink::runtime::internal { - constexpr size_t command_num_args(Command cmd) { - if (cmd >= Command::BINARY_OPERATORS_START && cmd <= Command::BINARY_OPERATORS_END) { - return 2; - } else if (cmd >= Command::UNARY_OPERATORS_START && cmd <= Command::UNARY_OPERATORS_END) { - return 1; - } else { - return 0; - } - } - template - static constexpr size_t CommandNumArguments = command_num_args(cmd); - template - class operation { - public: - template - operation(const T& t) {} - void operator()(eval_stack&, value*) { - throw ink_exception("operation not implemented!"); - } - }; +namespace ink::runtime::internal { template class typed_executer { @@ -97,3 +78,4 @@ namespace ink::runtime::internal { executer_imp _executer; }; } + diff --git a/inkcpp/marker_operations.h b/inkcpp/marker_operations.h new file mode 100644 index 00000000..c70736d4 --- /dev/null +++ b/inkcpp/marker_operations.h @@ -0,0 +1,8 @@ +#pragma once + +#include "value.h" +#include "operations.h" +#include "operation_bases.h" + +namespace ink::runtime::internal { +} diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h new file mode 100644 index 00000000..b007a60a --- /dev/null +++ b/inkcpp/numeric_operations.h @@ -0,0 +1,170 @@ +#pragma once + +namespace ink::runtime::internal { + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() + vals[1].get() )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() - vals[1].get() )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() / vals[1].get() )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() * vals[1].get() )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() % vals[1].get() )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() == vals[1].get() + )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() > vals[1].get() + )); + } + }; + + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() < vals[1].get() + )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() >= vals[1].get() + )); + } + }; + + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() <= vals[1].get() + )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( + vals[0].get() != vals[1].get() + )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() && vals[1].get() )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set( vals[0].get() || vals[1].get() )); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(vals[0].get() < vals[1].get() ? vals[0] : vals[1]); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(vals[0].get() > vals[1].get() ? vals[0] : vals[1]); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set(!vals[0].get())); + } + }; + + template + class operation> : operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals) { + stack.push(value{}.set(-vals[0].get())); + } + }; +} diff --git a/inkcpp/operation_bases.h b/inkcpp/operation_bases.h index 0fa89521..ae622728 100644 --- a/inkcpp/operation_bases.h +++ b/inkcpp/operation_bases.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace ink::runtime::internal { class string_table; @@ -23,7 +25,7 @@ namespace ink::runtime::internal { template operation_base(const T& t) : _string_table{*std::get(t)} {} - private: + protected: string_table& _string_table; }; } diff --git a/inkcpp/operations.h b/inkcpp/operations.h index bfc8593e..f84e6521 100644 --- a/inkcpp/operations.h +++ b/inkcpp/operations.h @@ -1,5 +1,33 @@ #pragma once -#include "executioner.h" -#include "default_operations.h" +#include "../shared/private/command.h" + +namespace ink::runtime::internal { + + constexpr size_t command_num_args(Command cmd) { + if (cmd >= Command::BINARY_OPERATORS_START && cmd <= Command::BINARY_OPERATORS_END) { + return 2; + } else if (cmd >= Command::UNARY_OPERATORS_START && cmd <= Command::UNARY_OPERATORS_END) { + return 1; + } else { + return 0; + } + } + template + static constexpr size_t CommandNumArguments = command_num_args(cmd); + + template + class operation { + public: + template + operation(const T& t) {} + void operator()(eval_stack&, value*) { + throw ink_exception("operation not implemented!"); + } + }; +} + +#include "operation_bases.h" +#include "numeric_operations.h" +/* #include "marker_operations.h" */ #include "string_operations.h" diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index a7430a73..fbc7c635 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -159,7 +159,7 @@ namespace ink::runtime::internal runner_impl::runner_impl(const story_impl* data, globals global) : _story(data), _globals(global.cast()), _container(~0), _threads(~0), - _operations(_globals->strings()), + _operations(global.cast()->strings()), _threadDone(nullptr, (ip_t)~0), _backup(nullptr), _done(nullptr), _choices() { _ptr = _story->instructions(); diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 6565ddf5..2c33e346 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -128,9 +128,9 @@ namespace ink::runtime::internal inline thread_t current_thread() const { return _threads.empty() ? ~0 : _threads.top(); } private: - executer _operations; const story_impl* const _story; story_ptr _globals; + executer _operations; // == State == diff --git a/inkcpp/string_operations.cpp b/inkcpp/string_operations.cpp new file mode 100644 index 00000000..f23c4f6b --- /dev/null +++ b/inkcpp/string_operations.cpp @@ -0,0 +1,88 @@ +#include "stack.h" +#include "value.h" +#include "string_utils.h" +#include "operations.h" +#include "string_table.h" + +namespace ink::runtime::internal { + namespace casting { + class string_cast { + public: + string_cast(const value& val); + const char* get() const { return _str; } + private: + const value& _val; + const char* _str; + char _data[512]; //TODO define central + }; + + template + int cast_to_string(char* buffer, size_t n, const value& v){ + ink_exception("cast not implemented"); + return -1; + } + + template + int to_string(char data[N], const value& v) { + if (v.type() == ty) { + return cast_to_string(data, N, v); + } else if constexpr (ty < value_type::END) { + return to_string(data, v); + } else { + throw ink_exception("cast target not exist!"); + } + } + + template<> + int cast_to_string(char* data, size_t size, const value& v) { + return toStr(data, size, v.get()); + } + template<> + int cast_to_string(char* data, size_t size, const value& v) { + return toStr(data, size, v.get()); + } + template<> + int cast_to_string(char* data, size_t size, const value& v) { + return toStr(data, size, v.get()); + } + + + string_cast::string_cast(const value& val) : _val{val}, _str{nullptr} { + if (val.type() == value_type::string) { + _str = val.get(); + } else { + _str = _data; + to_string<512>(_data, val); + } + } + } + void operation::operator()(eval_stack& stack, value* vals) { + casting::string_cast lh(vals[0]); + casting::string_cast rh (vals[1]); + char* str = _string_table.create(strlen(lh.get()) + strlen(rh.get()) + 1); + char* dst = str; + for(const char* src = lh.get(); *src; ++src) { *dst++ = *src; } + for(const char* src = rh.get(); *src; ++src) { *dst++ = *src; } + *dst = 0; + stack.push(value{}.set(str)); + } + + void operation::operator()(eval_stack& stack, value* vals) { + casting::string_cast lh (vals[0]); + casting::string_cast rh(vals[1]); + const char* li = lh.get(); + const char* ri = rh.get(); + bool res; + while(*li && *ri) { + ++li; + ++ri; + } + if (*li != 0 || *ri != 0) { + res = false; + } else { + res = true; + } + stack.push(value{}.set(res)); + } + +} diff --git a/inkcpp/string_operations.h b/inkcpp/string_operations.h index a4777b0e..b42b4dc4 100644 --- a/inkcpp/string_operations.h +++ b/inkcpp/string_operations.h @@ -1,15 +1,19 @@ #pragma once -#include "executioner.h" -#include "operation_bases.h" namespace ink::runtime::internal { - template - class operation : operation_base { + template<> + class operation : public operation_base { public: using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - } - } + void operator()(eval_stack& stack, value* vals); + }; + + template<> + class operation : public operation_base { + public: + using operation_base::operation_base; + void operator()(eval_stack& stack, value* vals); + }; } diff --git a/inkcpp/value.h b/inkcpp/value.h index ffd2a4b2..bb52a39f 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -20,10 +20,10 @@ namespace ink::runtime::internal { int32, float32, string, + newline, PRINT_END, marker = PRINT_END, glue, - newline, func_start, func_end, null, @@ -34,6 +34,15 @@ namespace ink::runtime::internal { thread_end, jump_marker, END}; + template + using is_numeric_t = typename std::enable_if< + ty == value_type::int32 + || ty == value_type::uint32 + || ty == value_type::float32, void>::type; + template + using is_integral_t = typename std::enable_if< + ty == value_type::int32 + || ty == value_type::uint32, void>::type; constexpr value_type operator+(value_type t, int i) { return static_cast(static_cast(t)+i); } @@ -85,19 +94,19 @@ namespace ink::runtime::internal { return *this; } - template<> struct value::ret { using type = uint32_t; }; - template<> inline uint32_t value::get() const { return uint32_value; } - template<> - inline constexpr value& value::set(uint32_t v) { - uint32_value = v; - _type = value_type::divert; - return *this; - } template<> struct value::ret { using type = uint32_t; }; template<> inline uint32_t value::get() const { return uint32_value; } template<> inline constexpr value& value::set(uint32_t v) { + uint32_value = v; + _type = value_type::uint32; + return *this; + } + template<> struct value::ret { using type = uint32_t; }; + template<> inline uint32_t value::get() const { return uint32_value; } + template<> + inline constexpr value& value::set(uint32_t v) { uint32_value = v; _type = value_type::divert; return *this; @@ -131,12 +140,26 @@ namespace ink::runtime::internal { return *this; } template<> + inline constexpr value& value::set(char* v) { + // TODO: decode allocw + str_value = v; + _type = value_type::string; + return *this; + } + template<> inline constexpr value& value::set(const char* v, bool allocated) { // TODO use bool str_value = v; _type = value_type::string; return *this; } + template<> + inline constexpr value& value::set( char* v, bool allocated) { + // TODO use bool + str_value = v; + _type = value_type::string; + return *this; + } template<> struct value::ret { using type = uint32_t; }; template<> inline uint32_t value::get() const { return uint32_value; } @@ -180,6 +203,12 @@ namespace ink::runtime::internal { _type = value_type::newline; return *this; } + template<> struct value::ret { using type = const char*; }; + template<> + inline const char* value::get() const { + static const char line_break[] = "\n"; + return line_break; + } template<> inline constexpr value& value::set() { _type = value_type::func_start; diff --git a/inkcpp_test/Callstack.cpp b/inkcpp_test/Callstack.cpp index 39ab64ad..4dc89032 100644 --- a/inkcpp_test/Callstack.cpp +++ b/inkcpp_test/Callstack.cpp @@ -5,12 +5,17 @@ using ink::hash_t; using ink::thread_t; using ink::runtime::internal::value; +using ink::runtime::internal::value_type; using ink::runtime::internal::frame_type; const hash_t X = ink::hash_string("X"); const hash_t Y = ink::hash_string("Y"); const hash_t Z = ink::hash_string("Z"); +value operator "" _v(unsigned long long i) { + return value{}.set(static_cast(i)); +} + SCENARIO("threading with the callstack", "[callstack]") { GIVEN("a callstack with a few temporary variables") @@ -19,8 +24,8 @@ SCENARIO("threading with the callstack", "[callstack]") auto stack = ink::runtime::internal::stack<50>(); // Set X and Y temporary variables - stack.set(X, 100); - stack.set(Y, 200); + stack.set(X, 100_v); + stack.set(Y, 200_v); WHEN("there is a fork and more is pushed") { @@ -33,8 +38,8 @@ SCENARIO("threading with the callstack", "[callstack]") } // Push something onto the thread - stack.set(X, 200); - REQUIRE((int)*stack.get(X) == 200); + stack.set(X, 200_v); + REQUIRE(stack.get(X)->get() == 200); WHEN("when that thread ends") { @@ -46,7 +51,7 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("we should have the value from that thread") { - REQUIRE((int)*stack.get(X) == 200); + REQUIRE(stack.get(X)->get() == 200); } } @@ -56,20 +61,20 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("we should have the value from the original thread") { - REQUIRE((int)*stack.get(X) == 100); - REQUIRE((int)*stack.get(Y) == 200); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->get() == 200); } } THEN("we should be able to access original thread values") { - REQUIRE((int)*stack.get(X) == 100); - REQUIRE((int)*stack.get(Y) == 200); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->get() == 200); } WHEN("we push more on the main thread") { - stack.set(Z, 500); + stack.set(Z, 500_v); THEN("collapsing to the thread shouldn't have the values anymore") { @@ -80,20 +85,20 @@ SCENARIO("threading with the callstack", "[callstack]") WHEN("we start a second thread that closes") { thread_t thread2 = stack.fork_thread(); - stack.set(X, 999); + stack.set(X, 999_v); stack.complete_thread(thread2); THEN("we can still collapse to the main thread") { stack.collapse_to_thread(~0); - REQUIRE((int)*stack.get(X) == 100); - REQUIRE((int)*stack.get(Y) == 200); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->get() == 200); } THEN("we can still collapse to the first thread") { stack.collapse_to_thread(thread); - REQUIRE((int)*stack.get(X) == 200); + REQUIRE(stack.get(Y)->get() == 100); REQUIRE(stack.get(Z) == nullptr); } } @@ -105,7 +110,7 @@ SCENARIO("threading with the callstack", "[callstack]") thread_t thread2 = stack.fork_thread(); // Put something on this thread - stack.set(X, 999); + stack.set(X, 999_v); WHEN("that inner thread and outer thread complete") { @@ -118,7 +123,7 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("we should have the value from that thread") { - REQUIRE((int)*stack.get(X) == 999); + REQUIRE(stack.get(X)->get() == 999); } } @@ -128,7 +133,7 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("we should have the value from that thread") { - REQUIRE((int)*stack.get(X) == 200); + REQUIRE(stack.get(X)->get() == 200); } } @@ -138,8 +143,8 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("we should have the value from the original thread") { - REQUIRE((int)*stack.get(X) == 100); - REQUIRE((int)*stack.get(Y) == 200); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->get() == 200); } } } @@ -149,7 +154,7 @@ SCENARIO("threading with the callstack", "[callstack]") WHEN("there is a fork with a tunnel that finishes") { thread_t thread = stack.fork_thread(); - stack.push_frame(555, frame_type::tunnel); + stack.push_frame(555); stack.complete_thread(thread); THEN("there should be no frames on the stack") @@ -165,15 +170,15 @@ SCENARIO("threading with the callstack", "[callstack]") auto stack = ink::runtime::internal::stack<50>(); // Set X and Y temporary variables - stack.set(X, 100); - stack.set(Y, 200); + stack.set(X, 100_v); + stack.set(Y, 200_v); // Push a tunnel - stack.push_frame(505, frame_type::tunnel); + stack.push_frame(505); // Push some more temps - stack.set(X, 101); - stack.set(Y, 201); + stack.set(X, 101_v); + stack.set(Y, 201_v); WHEN("a thread is forked") { @@ -189,8 +194,8 @@ SCENARIO("threading with the callstack", "[callstack]") REQUIRE(type == frame_type::tunnel); REQUIRE(offset == 505); - REQUIRE(*stack.get(X) == value(100)); - REQUIRE(*stack.get(Y) == value(200)); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->get() == 200); } stack.complete_thread(thread); @@ -200,8 +205,10 @@ SCENARIO("threading with the callstack", "[callstack]") stack.collapse_to_thread(thread); THEN("the stack should be outside the tunnel") { - REQUIRE(*stack.get(X) == value(100)); - REQUIRE(*stack.get(Y) == value(200)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 200); } } @@ -210,8 +217,10 @@ SCENARIO("threading with the callstack", "[callstack]") stack.collapse_to_thread(~0); THEN("the stack should be inside the tunnel") { - REQUIRE(*stack.get(X) == value(101)); - REQUIRE(*stack.get(Y) == value(201)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 101); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 201); } WHEN("we do a tunnel return") @@ -223,8 +232,10 @@ SCENARIO("threading with the callstack", "[callstack]") { REQUIRE(type == frame_type::tunnel); REQUIRE(offset == 505); - REQUIRE(*stack.get(X) == value(100)); - REQUIRE(*stack.get(Y) == value(200)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 200); } } } @@ -238,16 +249,20 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("we should be outside the tunnel") { - REQUIRE(*stack.get(X) == value(100)); - REQUIRE(*stack.get(Y) == value(200)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 200); } THEN("collapsing to the thread will have the correct callstack") { stack.collapse_to_thread(thread); - REQUIRE(*stack.get(X) == value(101)); - REQUIRE(*stack.get(Y) == value(201)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 101); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 201); } } } @@ -262,15 +277,15 @@ SCENARIO("threading with the callstack", "[callstack]") thread_t thread = stack.fork_thread(); // Set X and Y temporary variables - stack.set(X, 100); - stack.set(Y, 200); + stack.set(X, 100_v); + stack.set(Y, 200_v); // Push a tunnel - stack.push_frame(505, frame_type::tunnel); + stack.push_frame(505); // Push some more temps - stack.set(X, 101); - stack.set(Y, 201); + stack.set(X, 101_v); + stack.set(Y, 201_v); WHEN("a second thread is forked off the first") { @@ -287,8 +302,10 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("accessing the variable should return the original") { - REQUIRE(*stack.get(X) == value(100)); - REQUIRE(*stack.get(Y) == value(200)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 200); } WHEN("the first thread ends") @@ -299,8 +316,10 @@ SCENARIO("threading with the callstack", "[callstack]") { stack.collapse_to_thread(thread); - REQUIRE(*stack.get(X) == value(100)); - REQUIRE(*stack.get(Y) == value(200)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 200); } } } @@ -317,22 +336,22 @@ SCENARIO("threading with the callstack", "[callstack]") thread_t thread = stack.fork_thread(); // Set X and Y temporary variables - stack.set(X, 100); - stack.set(Y, 200); + stack.set(X, 100_v); + stack.set(Y, 200_v); // Push a tunnel - stack.push_frame(505, frame_type::tunnel); + stack.push_frame(505); // Push some more temps - stack.set(X, 101); - stack.set(Y, 201); + stack.set(X, 101_v); + stack.set(Y, 201_v); // Push another tunnel - stack.push_frame(505, frame_type::tunnel); + stack.push_frame(505); // Push some more temps - stack.set(X, 102); - stack.set(Y, 202); + stack.set(X, 102_v); + stack.set(Y, 202_v); WHEN("another thread is started and completed on top of it") { @@ -347,16 +366,20 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("we should have access to the original variables") { - REQUIRE(*stack.get(X) == value(100)); - REQUIRE(*stack.get(Y) == value(200)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 200); } THEN("collapsing to the thread should also get us the original variables") { stack.complete_thread(thread); stack.collapse_to_thread(thread); - REQUIRE(*stack.get(X) == value(100)); - REQUIRE(*stack.get(Y) == value(200)); + REQUIRE(stack.get(X)->type() == value_type::int32); + REQUIRE(stack.get(X)->get() == 100); + REQUIRE(stack.get(Y)->type() == value_type::int32); + REQUIRE(stack.get(Y)->get() == 200); } } } diff --git a/inkcpp_test/Value.cpp b/inkcpp_test/Value.cpp index e1afb2fd..118c996d 100644 --- a/inkcpp_test/Value.cpp +++ b/inkcpp_test/Value.cpp @@ -1,13 +1,18 @@ #include "catch.hpp" -#include "../inkcpp/value.h" #include "../inkcpp/string_table.h" #include "../inkcpp/output.h" +#include "../inkcpp/executioner.h" +#include "../shared/private/command.h" -using value = ink::runtime::internal::value; -using data_type = ink::runtime::internal::data_type; -using string_table = ink::runtime::internal::string_table; +using ink::runtime::internal::value; +using ink::runtime::internal::value_type; +using ink::runtime::internal::data_type; +using ink::runtime::internal::string_table; using stream = ink::runtime::internal::stream<128>; +using ink::runtime::internal::executer; +using eval_stack = ink::runtime::internal::eval_stack; +using ink::Command; void cp_str(char* dst, const char* src) { while(*src) { *dst++ = *src++; } @@ -16,6 +21,9 @@ void cp_str(char* dst, const char* src) { SCENARIO("compare concatenated values") { + string_table str_table; + executer ops(str_table); + eval_stack stack; GIVEN("just single strings") { const char str_1[] = "Hello World!"; @@ -23,30 +31,31 @@ SCENARIO("compare concatenated values") const char str_2[] = "Bye World!"; WHEN("equal") { - value v1(str_1); - value v2(str_1_again); - value res = v1 == v2; + stack.push(value{}.set(str_1)); + stack.push(value{}.set(str_1_again)); + ops(Command::IS_EQUAL, stack); + value res = stack.pop(); THEN("== results in true") { - REQUIRE(res.get_data_type() == data_type::int32); - REQUIRE(res.get() == 1); + REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.get() == true); } } WHEN("not equal") { - value v1(str_1); - value v2(str_2); - value res = v1 == v2; + stack.push(value{}.set(str_1)); + stack.push(value{}.set(str_2)); + ops(Command::IS_EQUAL, stack); + value res = stack.pop(); THEN("== results in false") { - REQUIRE(res.get_data_type() == data_type::int32); - REQUIRE(res.get() == 0); + REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.get() == false); } } } GIVEN("string and numbers") { - string_table str_table; stream out{}; char* str_hello = str_table.create(6); cp_str(str_hello, "hello"); @@ -62,38 +71,50 @@ SCENARIO("compare concatenated values") int int_45 = 45; WHEN("concatenated string representation matches (2 fields)") { - value v1 = value::add(value(int_4), value(str_5hello), out, str_table); - value v2 = value::add(value(int_45), value(str_hello), out, str_table); - value res = v1 == v2; + stack.push(value{}.set(int_4)); + stack.push(value{}.set(str_5hello)); + ops(Command::ADD, stack); + stack.push(value{}.set(int_45)); + stack.push(value{}.set(str_hello)); + ops(Command::ADD, stack); + ops(Command::IS_EQUAL, stack); + value res = stack.pop(); THEN("== returns true") { - REQUIRE(res.get_data_type() == data_type::int32); - REQUIRE(res.get() == 1); + REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.get() == true); } } WHEN("concatenated string representation match (many fields)") { - value v1 = value(str_4); + stack.push(value{}.set(str_4)); for (int i = 0; i < 31; ++i) { - v1 = value::add(v1, value(int_4), out, str_table); + stack.push(value{}.set(int_4)); + ops(Command::ADD, stack); } - value v2 = value(str_32_4); - value res = v1 == v2; + stack.push(value{}.set(str_32_4)); + ops(Command::IS_EQUAL, stack); + value res = stack.pop(); THEN("== results true") { - REQUIRE(res.get_data_type() == data_type::int32); - REQUIRE(res.get() == 1); + REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.get() == true); } } WHEN("concatenated string representation won't match") { - value v1 = value::add(value(int_45), value(str_5hello), out, str_table); - value v2 = value::add(value(int_4),value(str_hello), out, str_table); - value res = v1 == v2; + stack.push(value{}.set(int_45)); + stack.push(value{}.set(str_5hello)); + ops(Command::ADD, stack); + stack.push(value{}.set(int_4)); + stack.push(value{}.set(str_hello)); + ops(Command::ADD, stack); + ops(Command::IS_EQUAL, stack); + value res = stack.pop(); THEN("== returns false") { - REQUIRE(res.get_data_type() == data_type::int32); - REQUIRE(res.get() == 0); + REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.get() == false); } } } From a549be4e370e9e118347c731e39556277cc99ca5 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 22 Feb 2021 11:56:18 +0100 Subject: [PATCH 05/23] Fix Callstack resolution --- inkcpp/stack.cpp | 41 +++++++++++++++++---------------------- inkcpp/value.h | 35 +++++++++++++++++++++++++-------- inkcpp_test/Callstack.cpp | 2 +- 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index f30bcccb..d1a82bf7 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -55,8 +55,7 @@ namespace ink::runtime::internal if (e.name == InvalidHash && (e.data.type() == value_type::thread_start || e.data.type() == value_type::jump_marker)) { // If this thread start has a jump value - // FIXME: used thread_jump before - uint32_t jump = e.data.get(); + uint32_t jump = e.data.get().thread_id; // Then we need to do some jumping. Skip if (jump > 0) { @@ -131,16 +130,15 @@ namespace ink::runtime::internal iterator threadIter = jumpStart; // Get a reference to its jump count - // FIXME: used thread_jump before - // FIXME: reintrudce refernce? - value_type vt = threadIter.get()->data.type(); - uint32_t jump = threadIter.get()->data.get(); + value& start = threadIter.get()->data; + value_type vt = start.type(); + auto jump = start.get(); // Move over it threadIter.next(); // Move back over the current jump value - for (uint32_t i = 0; i < jump; i++) + for (uint32_t i = 0; i < jump.thread_id; i++) threadIter.next(); // Now keep iterating back until we get to a frame marker @@ -156,9 +154,9 @@ namespace ink::runtime::internal // We basically want to skip until we get to the start of this thread (leave the block alone) thread_t tid = e->data.get(); while (threadIter.get()->data.type() != value_type::thread_start - || threadIter.get()->data.get() != tid) + || threadIter.get()->data.get().todo != tid) { - jump++; + jump.thread_id++; threadIter.next(); } @@ -166,17 +164,17 @@ namespace ink::runtime::internal } threadIter.next(); - jump++; + jump.thread_id++; } // Move us over the frame marker - jump++; + jump.thread_id++; // Now that thread marker is set to the correct jump value. if (vt == value_type::jump_marker) { - threadIter.get()->data.set(jump); + start.set(jump); } else if (vt == value_type::thread_start) { - threadIter.get()->data.set(jump); + start.set(jump); } else { throw ink_exception("unknown jump type"); } @@ -231,7 +229,7 @@ namespace ink::runtime::internal if (frame->data.type() == data_type::thread_end) { // Push a new jump marker after the thread end - entry& jump = push({ InvalidHash, value{}.set(0u) }); + entry& jump = push({ InvalidHash, value{}.set(0u,0u) }); // Do a pop back returnedFrame = do_thread_jump_pop(base::begin()); @@ -274,7 +272,7 @@ namespace ink::runtime::internal // Return the offset stored in the frame record // FIXME: correct type? - return returnedFrame->data.get(); + return returnedFrame->data.get().todo; } bool basic_stack::has_frame(frame_type* returnType) const @@ -299,7 +297,7 @@ namespace ink::runtime::internal // If we're skipping over a thread, wait until we hit its start before checking if (thread != ~0) { - if (elem.data.type() == value_type::thread_start && elem.data.get() == thread) + if (elem.data.type() == value_type::thread_start && elem.data.get().todo == thread) thread = ~0; return false; @@ -307,8 +305,7 @@ namespace ink::runtime::internal // If it's a jump marker or a thread start if (elem.data.type() == value_type::jump_marker || elem.data.type() == value_type::thread_start) { - // FIXME: used thread_jump before - jumping = elem.data.get(); + jumping = elem.data.get().thread_id; return false; } @@ -350,11 +347,10 @@ namespace ink::runtime::internal thread_t new_thread = _next_thread++; // Push a thread start marker here - entry& thread_entry = add(InvalidHash, value{}.set(new_thread)); + entry& thread_entry = add(InvalidHash, value{}.set(new_thread, 0u)); // Set stack jump counter for thread to 0. This number is used if the thread ever // tries to pop past its origin. It keeps track of how much of the preceeding stack it's popped back - // FIXME: second value thread_entry.data.thread_jump() = 0; return new_thread; } @@ -405,7 +401,7 @@ namespace ink::runtime::internal // If we're deleting a useless thread block if (nulling != ~0) { // If this is the start of the block, stop deleting - if (elem.name == InvalidHash && elem.data.type() == data_type::thread_start && elem.data.get() == nulling) { + if (elem.name == InvalidHash && elem.data.type() == data_type::thread_start && elem.data.get().todo == nulling) { nulling = ~0; } @@ -421,8 +417,7 @@ namespace ink::runtime::internal elem.name = NulledHashId; // Check if this is a jump, if so we need to ignore even more data - // FIXME: used thread_jump before - jumping = elem.data.get(); + jumping = elem.data.get().thread_id; } // Clear thread frame markers. We can't use them anymore diff --git a/inkcpp/value.h b/inkcpp/value.h index bb52a39f..e83fcf2b 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -78,6 +78,10 @@ namespace ink::runtime::internal { const char* str_value; uint32_t uint32_value; float float_value; + struct { + uint32_t todo; + uint32_t thread_id; + } jump; }; value_type _type; }; @@ -161,20 +165,35 @@ namespace ink::runtime::internal { return *this; } - template<> struct value::ret { using type = uint32_t; }; - template<> inline uint32_t value::get() const { return uint32_value; } + template<> struct value::ret { using type = decltype(value::jump); }; + template<> inline value::ret::type value::get() const { return jump; } template<> - inline constexpr value& value::set(uint32_t v) { - uint32_value = v; + inline constexpr value& value::set(decltype(value::jump) v) { + jump = v; + _type = value_type::jump_marker; + return *this; + } + template<> + inline constexpr value& value::set(uint32_t v, uint32_t j) { + jump.todo = v; + jump.thread_id = j; _type = value_type::jump_marker; return *this; } - template<> struct value::ret { using type = uint32_t; }; - template<> inline uint32_t value::get() const { return uint32_value; } + template<> struct value::ret { using type = decltype(value::jump); }; + template<> inline value::ret::type value::get() const { return jump; } template<> - inline constexpr value& value::set(uint32_t v) { - uint32_value = v; + inline constexpr value& value::set(decltype(value::jump) v) + { + jump = v; + _type = value_type::jump_marker; + return *this; + } + template<> + inline constexpr value& value::set(uint32_t v, uint32_t j) { + jump.todo = v; + jump.thread_id = j; _type = value_type::thread_start; return *this; } diff --git a/inkcpp_test/Callstack.cpp b/inkcpp_test/Callstack.cpp index 4dc89032..3e1a7f5d 100644 --- a/inkcpp_test/Callstack.cpp +++ b/inkcpp_test/Callstack.cpp @@ -98,7 +98,7 @@ SCENARIO("threading with the callstack", "[callstack]") THEN("we can still collapse to the first thread") { stack.collapse_to_thread(thread); - REQUIRE(stack.get(Y)->get() == 100); + REQUIRE(stack.get(Y)->get() == 200); REQUIRE(stack.get(Z) == nullptr); } } From b9c49b72045829112137daf5194037add60ec14f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 22 Feb 2021 12:10:05 +0100 Subject: [PATCH 06/23] Fix wrong typ setting --- inkcpp/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp/value.h b/inkcpp/value.h index e83fcf2b..ae00f678 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -187,7 +187,7 @@ namespace ink::runtime::internal { inline constexpr value& value::set(decltype(value::jump) v) { jump = v; - _type = value_type::jump_marker; + _type = value_type::thread_start; return *this; } template<> From 3a39ac2ef5d8a9597761aeb20ae687243fd95194 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 22 Feb 2021 12:40:09 +0100 Subject: [PATCH 07/23] Fixed missing ref in input operator --- inkcpp/CMakeLists.txt | 2 +- inkcpp/value.cpp | 4 ++-- inkcpp/value.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index abda8535..41f7207e 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -15,7 +15,7 @@ list(APPEND SOURCES story_impl.h story_impl.cpp story_ptr.cpp system.cpp - value.cpp value.h + value.h value.cpp string_table.h string_table.cpp avl_array.h string_operations.cpp header.cpp diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 397a7333..c7a0022e 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -23,12 +23,12 @@ namespace ink::runtime::internal } #endif - basic_stream& operator<<(basic_stream& os, value val) { + basic_stream& operator<<(basic_stream& os, const value& val) { os.append(val); return os; } - basic_stream& operator>>(basic_stream& is, value val) { + basic_stream& operator>>(basic_stream& is, value& val) { is.get(&val, 1); return is; } diff --git a/inkcpp/value.h b/inkcpp/value.h index ae00f678..a8f20047 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -67,8 +67,8 @@ namespace ink::runtime::internal { } value_type type() const { return _type; } - friend basic_stream& operator<<(basic_stream& os, value); - friend basic_stream& operator>>(basic_stream& is, value); + friend basic_stream& operator<<(basic_stream& os, const value&); + friend basic_stream& operator>>(basic_stream& is, value&); constexpr bool printable() const { return _type >= value_type::PRINT_BEGIN && _type < value_type::PRINT_END; } private: From 15be226d19ce8ab63eac664e6ce6b70fe012694b Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 22 Feb 2021 14:07:10 +0100 Subject: [PATCH 08/23] Rename todo to jump --- inkcpp/stack.cpp | 8 ++++---- inkcpp/value.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index d1a82bf7..01ac6934 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -154,7 +154,7 @@ namespace ink::runtime::internal // We basically want to skip until we get to the start of this thread (leave the block alone) thread_t tid = e->data.get(); while (threadIter.get()->data.type() != value_type::thread_start - || threadIter.get()->data.get().todo != tid) + || threadIter.get()->data.get().jump != tid) { jump.thread_id++; threadIter.next(); @@ -272,7 +272,7 @@ namespace ink::runtime::internal // Return the offset stored in the frame record // FIXME: correct type? - return returnedFrame->data.get().todo; + return returnedFrame->data.get().jump; } bool basic_stack::has_frame(frame_type* returnType) const @@ -297,7 +297,7 @@ namespace ink::runtime::internal // If we're skipping over a thread, wait until we hit its start before checking if (thread != ~0) { - if (elem.data.type() == value_type::thread_start && elem.data.get().todo == thread) + if (elem.data.type() == value_type::thread_start && elem.data.get().jump == thread) thread = ~0; return false; @@ -401,7 +401,7 @@ namespace ink::runtime::internal // If we're deleting a useless thread block if (nulling != ~0) { // If this is the start of the block, stop deleting - if (elem.name == InvalidHash && elem.data.type() == data_type::thread_start && elem.data.get().todo == nulling) { + if (elem.name == InvalidHash && elem.data.type() == data_type::thread_start && elem.data.get().jump == nulling) { nulling = ~0; } diff --git a/inkcpp/value.h b/inkcpp/value.h index a8f20047..4fc4c2aa 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -79,7 +79,7 @@ namespace ink::runtime::internal { uint32_t uint32_value; float float_value; struct { - uint32_t todo; + uint32_t jump; uint32_t thread_id; } jump; }; @@ -175,7 +175,7 @@ namespace ink::runtime::internal { } template<> inline constexpr value& value::set(uint32_t v, uint32_t j) { - jump.todo = v; + jump.jump = v; jump.thread_id = j; _type = value_type::jump_marker; return *this; @@ -192,7 +192,7 @@ namespace ink::runtime::internal { } template<> inline constexpr value& value::set(uint32_t v, uint32_t j) { - jump.todo = v; + jump.jump = v; jump.thread_id = j; _type = value_type::thread_start; return *this; From 83abccb351837d2eab6dc5011287dd7ef705f862 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 23 Feb 2021 14:54:34 +0100 Subject: [PATCH 09/23] Add casting meta + add numeric casting + Tests --- inkcpp/casting.h | 58 ++++++++++++++++++++------ inkcpp/executioner.h | 1 - inkcpp/numeric_operations.h | 81 +++++++++++++++++++++++++++++++------ inkcpp/operation_bases.h | 3 ++ inkcpp/operations.h | 9 +++++ inkcpp/string_operations.h | 9 +++++ inkcpp/value.h | 10 +---- inkcpp_test/Value.cpp | 67 ++++++++++++++++++++++++++++++ 8 files changed, 203 insertions(+), 35 deletions(-) diff --git a/inkcpp/casting.h b/inkcpp/casting.h index 22a266f0..1c733e54 100644 --- a/inkcpp/casting.h +++ b/inkcpp/casting.h @@ -2,19 +2,51 @@ #include "value.h" -namespace ink::runtime::internal { - namespace casting { - template - value_type common_base(const value* vs) { - if constexpr (N == 0) { return value_type::none; } - else if constexpr (N == 1) { return vs->type(); } - else { - if (vs[0].type() == value_type::string || vs[1].type() == value_type::string) { - return value_type::string; - } else { - return vs[0].type(); - } - } +namespace ink::runtime::internal::casting { + + class casting_matrix_type { + public: + constexpr value_type get(value_type t1, value_type t2) const { + return _data[static_cast(t1)][static_cast(t2)]; } + static constexpr size_t N = static_cast(value_type::END); + value_type _data[N][N]; }; + + template + constexpr void set_cast (value_type data[static_cast(value_type::END)][static_cast(value_type::END)]){ + if constexpr (t2 == value_type::END) { + } else if constexpr (t1 == value_type::END) { + set_cast(data); + } else { + constexpr size_t n1 = static_cast(t1); + constexpr size_t n2 = static_cast(t2); + if constexpr (n1 < n2) { + data[n1][n2] = cast; + } else { + data[n1][n2] = cast; + } + set_cast(data); + } + } + + constexpr casting_matrix_type construct_casting_matrix() { + casting_matrix_type cm; + set_cast(cm._data); + return cm; + } + static constexpr casting_matrix_type casting_matrix = construct_casting_matrix(); + + template + value_type common_base(const value* vs) { + if constexpr (N == 0) { return value_type::none; } + else if constexpr (N == 1) { return vs->type(); } + else { + value_type ty = vs[0].type(); + for(size_t i = 1; i < N; ++i) { + ty = casting_matrix.get(ty, vs[i].type()); + } + return ty; + } + } } diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h index ea3e47e8..0d58db49 100644 --- a/inkcpp/executioner.h +++ b/inkcpp/executioner.h @@ -1,7 +1,6 @@ #pragma once #include "value.h" -#include "casting.h" #include "stack.h" #include "operations.h" diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index b007a60a..bd783f33 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -1,12 +1,49 @@ #pragma once namespace ink::runtime::internal { + template + using is_numeric_t = typename std::enable_if< + ty == value_type::int32 + || ty == value_type::uint32 + || ty == value_type::float32, void>::type; + + template + using is_integral_t = typename std::enable_if< + ty == value_type::int32 + || ty == value_type::uint32, void>::type; + + namespace casting { + template<> + constexpr value_type cast = value_type::float32; + template + inline value::ret::type numeric_cast(const value& v) { + if (to == v.type()) { return v.get(); } + else { + throw ink_exception("invalid numeric_cast!"); + } + } + + template<> + inline float numeric_cast(const value& v) { + switch(v.type()) { + case value_type::float32: + return v.get(); + case value_type::int32: + return static_cast(v.get()); + default: + throw ink_exception("invalid numeric_cast!"); + } + } + } + template class operation> : operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() + vals[1].get() )); + stack.push(value{}.set( + casting::numeric_cast(vals[0]) + + casting::numeric_cast(vals[1]) )); } }; @@ -15,7 +52,9 @@ namespace ink::runtime::internal { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() - vals[1].get() )); + stack.push(value{}.set( + casting::numeric_cast(vals[0]) - + casting::numeric_cast(vals[1]) )); } }; @@ -24,7 +63,9 @@ namespace ink::runtime::internal { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() / vals[1].get() )); + stack.push(value{}.set( + casting::numeric_cast(vals[0]) / + casting::numeric_cast(vals[1]) )); } }; @@ -33,7 +74,9 @@ namespace ink::runtime::internal { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() * vals[1].get() )); + stack.push(value{}.set( + casting::numeric_cast(vals[0]) * + casting::numeric_cast(vals[1]) )); } }; @@ -52,7 +95,8 @@ namespace ink::runtime::internal { using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set( - vals[0].get() == vals[1].get() + casting::numeric_cast(vals[0]) == + casting::numeric_cast(vals[1]) )); } }; @@ -63,7 +107,8 @@ namespace ink::runtime::internal { using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set( - vals[0].get() > vals[1].get() + casting::numeric_cast(vals[0]) > + casting::numeric_cast(vals[1]) )); } }; @@ -75,7 +120,8 @@ namespace ink::runtime::internal { using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set( - vals[0].get() < vals[1].get() + casting::numeric_cast(vals[0]) < + casting::numeric_cast(vals[1]) )); } }; @@ -86,7 +132,8 @@ namespace ink::runtime::internal { using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set( - vals[0].get() >= vals[1].get() + casting::numeric_cast(vals[0]) >= + casting::numeric_cast(vals[1]) )); } }; @@ -98,7 +145,8 @@ namespace ink::runtime::internal { using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set( - vals[0].get() <= vals[1].get() + casting::numeric_cast(vals[0]) <= + casting::numeric_cast(vals[1]) )); } }; @@ -109,7 +157,8 @@ namespace ink::runtime::internal { using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { stack.push(value{}.set( - vals[0].get() != vals[1].get() + casting::numeric_cast(vals[0]) != + casting::numeric_cast(vals[1]) )); } }; @@ -137,7 +186,11 @@ namespace ink::runtime::internal { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { - stack.push(vals[0].get() < vals[1].get() ? vals[0] : vals[1]); + typename value::ret::type n[2] = { + casting::numeric_cast(vals[0]), + casting::numeric_cast(vals[1]) + }; + stack.push(value{}.set(n[0] < n[1] ? n[0] : n[1])); } }; @@ -146,7 +199,11 @@ namespace ink::runtime::internal { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { - stack.push(vals[0].get() > vals[1].get() ? vals[0] : vals[1]); + typename value::ret::type n[2] = { + casting::numeric_cast(vals[0]), + casting::numeric_cast(vals[1]) + }; + stack.push(value{}.set(n[0] > n[1] ? n[0] : n[1])); } }; diff --git a/inkcpp/operation_bases.h b/inkcpp/operation_bases.h index ae622728..e8a83390 100644 --- a/inkcpp/operation_bases.h +++ b/inkcpp/operation_bases.h @@ -8,6 +8,7 @@ namespace ink::runtime::internal { template class operation_base { public: + static constexpr bool enabled = false; template operation_base(const T&) { static_assert(always_false::value, "use undefined base!"); } }; @@ -15,6 +16,7 @@ namespace ink::runtime::internal { template<> class operation_base { public: + static constexpr bool enabled = true; template operation_base(const T&) {} }; @@ -22,6 +24,7 @@ namespace ink::runtime::internal { template<> class operation_base { public: + static constexpr bool enabled = true; template operation_base(const T& t) : _string_table{*std::get(t)} {} diff --git a/inkcpp/operations.h b/inkcpp/operations.h index f84e6521..69842972 100644 --- a/inkcpp/operations.h +++ b/inkcpp/operations.h @@ -4,6 +4,13 @@ namespace ink::runtime::internal { + namespace casting { + template + constexpr value_type cast = value_type::none; + template + constexpr value_type cast = t; + } + constexpr size_t command_num_args(Command cmd) { if (cmd >= Command::BINARY_OPERATORS_START && cmd <= Command::BINARY_OPERATORS_END) { return 2; @@ -19,6 +26,7 @@ namespace ink::runtime::internal { template class operation { public: + static constexpr bool enabled = false; template operation(const T& t) {} void operator()(eval_stack&, value*) { @@ -31,3 +39,4 @@ namespace ink::runtime::internal { #include "numeric_operations.h" /* #include "marker_operations.h" */ #include "string_operations.h" +#include "casting.h" diff --git a/inkcpp/string_operations.h b/inkcpp/string_operations.h index b42b4dc4..eb33f966 100644 --- a/inkcpp/string_operations.h +++ b/inkcpp/string_operations.h @@ -3,6 +3,15 @@ namespace ink::runtime::internal { + namespace casting { + template<> + constexpr value_type cast = value_type::string; + template<> + constexpr value_type cast = value_type::string; + template<> + constexpr value_type cast = value_type::string; + } + template<> class operation : public operation_base { public: diff --git a/inkcpp/value.h b/inkcpp/value.h index 4fc4c2aa..2c6e2276 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -34,15 +34,7 @@ namespace ink::runtime::internal { thread_end, jump_marker, END}; - template - using is_numeric_t = typename std::enable_if< - ty == value_type::int32 - || ty == value_type::uint32 - || ty == value_type::float32, void>::type; - template - using is_integral_t = typename std::enable_if< - ty == value_type::int32 - || ty == value_type::uint32, void>::type; + constexpr value_type operator+(value_type t, int i) { return static_cast(static_cast(t)+i); } diff --git a/inkcpp_test/Value.cpp b/inkcpp_test/Value.cpp index 118c996d..11534538 100644 --- a/inkcpp_test/Value.cpp +++ b/inkcpp_test/Value.cpp @@ -118,4 +118,71 @@ SCENARIO("compare concatenated values") } } } + GIVEN("numbers") + { + int i5 = 5; + int i8 = 8; + float f5 = 5.f; + WHEN("numbers are same") + { + stack.push(value{}.set(i8)); + stack.push(value{}.set(i8)); + ops(Command::IS_EQUAL, stack); + value res1 = stack.pop(); + stack.push(value{}.set(f5)); + stack.push(value{}.set(f5)); + ops(Command::IS_EQUAL, stack); + value res2 = stack.pop(); + THEN("== returns true") + { + REQUIRE(res1.type() == data_type::boolean); + REQUIRE(res1.get() == true); + REQUIRE(res2.type() == data_type::boolean); + REQUIRE(res2.get() == true); + } + } + WHEN("numbers equal, but different encoding") + { + stack.push(value{}.set(i5)); + stack.push(value{}.set(f5)); + ops(Command::IS_EQUAL, stack); + value res = stack.pop(); + THEN("== returns true") + { + REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.get() == true); + } + } + WHEN("numbers value and encoding differs") + { + stack.push(value{}.set(f5)); + stack.push(value{}.set(i8)); + ops(Command::IS_EQUAL, stack); + value res = stack.pop(); + THEN("== returns false") + { + REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.get() == false); + } + } + WHEN("calculate with float and int (5.,8)") + { + stack.push(value{}.set(f5)); + stack.push(value{}.set(i8)); + THEN("adding results 13.") + { + ops(Command::ADD, stack); + value res = stack.pop(); + REQUIRE(res.type() == data_type::float32); + REQUIRE(res.get() == 13.f); + } + THEN("dividing results in 0.625") + { + ops(Command::DIVIDE, stack); + value res = stack.pop(); + REQUIRE(res.type() == data_type::float32); + REQUIRE(res.get() == 0.625f); + } + } + } } From 76318eddcf9582dc402b4ad03dd4a7b293bc76a4 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 23 Feb 2021 15:38:06 +0100 Subject: [PATCH 10/23] Use only operatable types for operator --- inkcpp/casting.h | 8 ++++---- inkcpp/executioner.h | 2 +- inkcpp/string_operations.cpp | 2 +- inkcpp/value.h | 7 ++++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/inkcpp/casting.h b/inkcpp/casting.h index 1c733e54..8a8c4a02 100644 --- a/inkcpp/casting.h +++ b/inkcpp/casting.h @@ -9,14 +9,14 @@ namespace ink::runtime::internal::casting { constexpr value_type get(value_type t1, value_type t2) const { return _data[static_cast(t1)][static_cast(t2)]; } - static constexpr size_t N = static_cast(value_type::END); + static constexpr size_t N = static_cast(value_type::OP_END); value_type _data[N][N]; }; template - constexpr void set_cast (value_type data[static_cast(value_type::END)][static_cast(value_type::END)]){ - if constexpr (t2 == value_type::END) { - } else if constexpr (t1 == value_type::END) { + constexpr void set_cast (value_type data[static_cast(value_type::OP_END)][static_cast(value_type::OP_END)]){ + if constexpr (t2 == value_type::OP_END) { + } else if constexpr (t1 == value_type::OP_END) { set_cast(data); } else { constexpr size_t n1 = static_cast(t1); diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h index 0d58db49..4fecda5e 100644 --- a/inkcpp/executioner.h +++ b/inkcpp/executioner.h @@ -25,7 +25,7 @@ namespace ink::runtime::internal { operation _op; }; template - class typed_executer { + class typed_executer { public: template typed_executer(const T& t) {} diff --git a/inkcpp/string_operations.cpp b/inkcpp/string_operations.cpp index f23c4f6b..6f50a54f 100644 --- a/inkcpp/string_operations.cpp +++ b/inkcpp/string_operations.cpp @@ -26,7 +26,7 @@ namespace ink::runtime::internal { int to_string(char data[N], const value& v) { if (v.type() == ty) { return cast_to_string(data, N, v); - } else if constexpr (ty < value_type::END) { + } else if constexpr (ty < value_type::OP_END) { return to_string(data, v); } else { throw ink_exception("cast target not exist!"); diff --git a/inkcpp/value.h b/inkcpp/value.h index 2c6e2276..4e471c81 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -20,7 +20,8 @@ namespace ink::runtime::internal { int32, float32, string, - newline, + OP_END, + newline = OP_END, PRINT_END, marker = PRINT_END, glue, @@ -32,8 +33,8 @@ namespace ink::runtime::internal { thread_start, thread_frame, thread_end, - jump_marker, - END}; + jump_marker + }; constexpr value_type operator+(value_type t, int i) { return static_cast(static_cast(t)+i); From 803f7e0a3fbe182bc38afb87fa2179d4837ff623 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 24 Feb 2021 19:02:20 +0100 Subject: [PATCH 11/23] renaiming --- inkcpp/output.cpp | 110 +++++++++++++---------------------- inkcpp/output.h | 23 ++++---- inkcpp/stack.cpp | 17 +++--- inkcpp/string_operations.cpp | 4 ++ inkcpp/string_operations.h | 2 + inkcpp/string_utils.h | 44 ++++++++++++++ 6 files changed, 107 insertions(+), 93 deletions(-) diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index ab68bc0c..3ebdb44f 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -13,23 +13,23 @@ namespace ink { namespace internal { - basic_stream::basic_stream(data* buffer, size_t len) + basic_stream::basic_stream(value* buffer, size_t len) : _data(buffer), _max(len), _size(0), _save(~0) {} - void basic_stream::append(const data& in) + void basic_stream::append(const value& in) { // SPECIAL: Incoming newline - if (in.type() == data_type::newline && _size > 1) + if (in.type() == value_type::newline && _size > 1) { // If the end of the stream is a function start marker, we actually // want to ignore this. Function start trimming. - if (_data[_size - 1].type() == data_type::func_start) + if (_data[_size - 1].type() == value_type::func_start) return; } // Ignore leading newlines - if (in.type() == data_type::newline && _size == 0) + if (in.type() == value_type::newline && _size == 0) return; // Add to data stream @@ -38,21 +38,21 @@ namespace ink // Special: Incoming glue. Trim whitespace/newlines prior // This also applies when a function ends to trim trailing whitespace. - if ((in.type() == data_type::glue || in.type() == data_type::func_end) && _size > 1) + if ((in.type() == value_type::glue || in.type() == value_type::func_end) && _size > 1) { // Run backwards size_t i = _size - 2; while(true) { - data& d = _data[i]; + value& d = _data[i]; // Nullify newlines - if (d.type() == data_type::newline) { + if (d.type() == value_type::newline) { d = value{}; } // Nullify whitespace - else if ( d.type() == data_type::string + else if ( d.type() == value_type::string && is_whitespace(d.get())) d = value{}; @@ -69,7 +69,7 @@ namespace ink } } - void basic_stream::append(const data* in, unsigned int length) + void basic_stream::append(const value* in, unsigned int length) { // TODO: Better way to bulk while still executing glue checks? for (size_t i = 0; i < length; i++) @@ -94,23 +94,13 @@ namespace ink output.put(c); } - inline bool get_next(const data* list, size_t i, size_t size, const data** next) + inline bool get_next(const value* list, size_t i, size_t size, const value** next) { while (i + 1 < size) { *next = &list[i + 1]; - data_type type = (*next)->type(); - switch (type) - { - // FIXME: should be all printable symbols? - case data_type::int32: - case data_type::float32: - case data_type::uint32: - case data_type::string: - case data_type::newline: - return true; - } - + value_type type = (*next)->type(); + if ((*next)->printable()) { return true; } i++; } @@ -141,15 +131,15 @@ namespace ink } // check what the next item - const data* next = nullptr; + const value* next = nullptr; if (get_next(_data, dataIter, _size, &next)) { // If it's a newline, ignore all our whitespace - if (next->type() == data_type::newline) + if (next->type() == value_type::newline) return; // If it's another string, check if it starts with whitespace - if (next->type() == data_type::string ) + if (next->type() == value_type::string ) { if (is_whitespace(next->get()[0])) return; @@ -220,15 +210,14 @@ namespace ink switch (_data[i].type) { - case data_type::int32: + case value_type::int32: str += FString::Printf(TEXT("%d"), _data[i].integer_value); break; - case data_type::float32: + case value_type::float32: // TODO: Whitespace cleaning str += FString::Printf(TEXT("%f"), _data[i].float_value); break; - case data_type::string_table_pointer: - case data_type::allocated_string_pointer: + case value_type::string: str += _data[i].string_val; break; case data_type::newline: @@ -251,7 +240,7 @@ namespace ink return _size - start; } - const data& basic_stream::peek() const + const value& basic_stream::peek() const { inkAssert(_size > 0, "Attempting to peek empty stream!"); return _data[_size - 1]; @@ -265,12 +254,12 @@ namespace ink _size = 0; } - void basic_stream::get(data* ptr, size_t length) + void basic_stream::get(value* ptr, size_t length) { // Find start size_t start = find_start(); - const data* end = ptr + length; + const value* end = ptr + length; //inkAssert(_size - start < length, "Insufficient space in data array to store stream contents!"); // Move up from marker @@ -298,14 +287,14 @@ namespace ink // TODO: Cache? for (size_t i = 0; i < _size; i++) { - if (_data[i].type() == data_type::marker) + if (_data[i].type() == value_type::marker) return true; } return false; } - bool basic_stream::ends_with(data_type type) const + bool basic_stream::ends_with(value_type type) const { if (_size == 0) return false; @@ -313,7 +302,7 @@ namespace ink return _data[_size - 1].type() == type; } - bool basic_stream::saved_ends_with(data_type type) const + bool basic_stream::saved_ends_with(value_type type) const { inkAssert(_save != ~0, "Stream is not saved!"); @@ -363,23 +352,7 @@ namespace ink continue; if (_data[i].printable()) { - - } - // FIXME: not the right place - switch (_data[i].type()) - { - case data_type::int32: - length += decimal_digits(_data[i].get()); - break; - case data_type::float32: - length += decimal_digits(_data[i].get()); - break; - case data_type::string: - length += strlen(_data[i].get()); - break; - case data_type::newline: - length += 1; - break; + length += value_length(_data[i]); } } @@ -392,26 +365,21 @@ namespace ink { if (should_skip(i, hasGlue, lastNewline)) continue; - // FIXME: right place? switch (_data[i].type()) { - case data_type::int32: + case value_type::int32: + case value_type::float32: + case value_type::uint32: // Convert to string and advance - toStr(ptr, end - ptr, _data[i].get()); + toStr(ptr, end - ptr, _data[i]); while (*ptr != 0) ptr++; break; - case data_type::float32: - // Convert to string and advance - toStr(ptr, end - ptr, _data[i].get()); - while (*ptr != 0) ptr++; - - break; - case data_type::string: + case value_type::string: // Copy string and advance copy_string(_data[i].get(), i, ptr); break; - case data_type::newline: + case value_type::newline: *ptr = '\n'; ptr++; break; } @@ -435,7 +403,7 @@ namespace ink while (start > 0) { start--; - if (_data[start].type() == data_type::marker) + if (_data[start].type() == value_type::marker) break; } @@ -450,20 +418,20 @@ namespace ink { switch (_data[iter].type()) { - case data_type::int32: - case data_type::float32: - case data_type::string: + case value_type::int32: + case value_type::float32: + case value_type::string: hasGlue = false; lastNewline = false; break; - case data_type::newline: + case value_type::newline: if (lastNewline) return true; if (hasGlue) return true; lastNewline = true; break; - case data_type::glue: + case value_type::glue: hasGlue = true; break; } @@ -476,7 +444,7 @@ namespace ink // Check if there is text past the save for (size_t i = _save; i < _size; i++) { - const data& d = _data[i]; + const value& d = _data[i]; if (d.type() == value_type::string) { // TODO: Cache what counts as whitespace? diff --git a/inkcpp/output.h b/inkcpp/output.h index ddb57a70..d9ecc6e0 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -10,23 +10,20 @@ namespace ink namespace internal { class string_table; - using data = value; - using data_type = value_type; - class basic_stream { protected: - basic_stream(data*, size_t); + basic_stream(value*, size_t); public: // Append data to stream - void append(const data&); + void append(const value&); // Append data array to stream - void append(const data*, unsigned int length); + void append(const value*, unsigned int length); // Append fixed sized data array to stream template - void append(const data in[N]) + void append(const value in[N]) { append(&in[0], N); } @@ -35,13 +32,13 @@ namespace ink int queued() const; // Peeks the top entry - const data& peek() const; + const value& peek() const; // discards data void discard(size_t length); // Extract into a data array - void get(data*, size_t length); + void get(value*, size_t length); // Extract to a newly allocated string const char* get_alloc(string_table&); @@ -62,10 +59,10 @@ namespace ink bool has_marker() const; // Checks if the stream ends with a specific type - bool ends_with(data_type) const; + bool ends_with(value_type) const; // Checks if the last element when save()'d was this type - bool saved_ends_with(data_type) const; + bool saved_ends_with(value_type) const; // Checks if there are any elements past the save that // are non-whitespace strings @@ -91,7 +88,7 @@ namespace ink private: // data stream - data* _data; + value* _data; size_t _max; // size @@ -116,7 +113,7 @@ namespace ink stream() : basic_stream(&_buffer[0], N) { } private: - data _buffer[N]; + value _buffer[N]; }; } } diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 01ac6934..d409777b 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -3,8 +3,6 @@ namespace ink::runtime::internal { - using data = value; - using data_type = value_type; basic_stack::basic_stack(entry* data, size_t size) : base(data, size) { @@ -181,15 +179,15 @@ namespace ink::runtime::internal return threadIter.get(); } - frame_type get_frame_type(data_type type) + frame_type get_frame_type(value_type type) { switch (type) { - case data_type::tunnel_frame: + case value_type::tunnel_frame: return frame_type::tunnel; - case data_type::function_frame: + case value_type::function_frame: return frame_type::function; - case data_type::thread_frame: + case value_type::thread_frame: return frame_type::thread; default: inkAssert(false, "Unknown frame type detected"); @@ -226,7 +224,7 @@ namespace ink::runtime::internal ) { // End of thread marker, we need to create a jump marker - if (frame->data.type() == data_type::thread_end) + if (frame->data.type() == value_type::thread_end) { // Push a new jump marker after the thread end entry& jump = push({ InvalidHash, value{}.set(0u,0u) }); @@ -245,7 +243,7 @@ namespace ink::runtime::internal } // Popping past thread start - if (frame->data.type() == data_type::thread_start) + if (frame->data.type() == value_type::thread_start) { returnedFrame = do_thread_jump_pop(iter); break; @@ -261,7 +259,8 @@ namespace ink::runtime::internal inkAssert(returnedFrame, "Attempting to pop_frame when no frames exist! Stack reset."); // Make sure we're not somehow trying to "return" from a thread - inkAssert(returnedFrame->data.type() != data_type::thread_start && returnedFrame->data.type() != data_type::thread_end, + inkAssert(returnedFrame->data.type() != value_type::thread_start + && returnedFrame->data.type() != value_type::thread_end, "Can not return from a thread! How did this happen?"); // Store frame type diff --git a/inkcpp/string_operations.cpp b/inkcpp/string_operations.cpp index 6f50a54f..9dd5ed3f 100644 --- a/inkcpp/string_operations.cpp +++ b/inkcpp/string_operations.cpp @@ -45,6 +45,10 @@ namespace ink::runtime::internal { int cast_to_string(char* data, size_t size, const value& v) { return toStr(data, size, v.get()); } + template<> + int cast_to_string(char* data, size_t size, const value& v) { + return toStr(data, size, "\n"); + } string_cast::string_cast(const value& val) : _val{val}, _str{nullptr} { diff --git a/inkcpp/string_operations.h b/inkcpp/string_operations.h index eb33f966..0a39be44 100644 --- a/inkcpp/string_operations.h +++ b/inkcpp/string_operations.h @@ -10,6 +10,8 @@ namespace ink::runtime::internal { constexpr value_type cast = value_type::string; template<> constexpr value_type cast = value_type::string; + template<> + constexpr value_type cast = value_type::string; } template<> diff --git a/inkcpp/string_utils.h b/inkcpp/string_utils.h index cd6956fc..300a9ce0 100644 --- a/inkcpp/string_utils.h +++ b/inkcpp/string_utils.h @@ -1,6 +1,7 @@ #pragma once #include "system.h" +#include "value.h" #include @@ -41,6 +42,32 @@ namespace ink::runtime::internal { return EINVAL; #endif } + + inline int toStr(char* buffer, size_t size, const char* c) { + char* ptr = buffer; + size_t i = 0; + while(*c && i < size) { + *ptr++ = *c; + ++i; + } + if (i >= size) { return EINVAL; } + *ptr = 0; + return 0; + } + + inline int toStr(char * buffer, size_t size, const value& v) { + switch(v.type()) { + case value_type::int32: + return toStr(buffer, size, v.get()); + case value_type::uint32: + return toStr(buffer, size, v.get()); + case value_type::float32: + return toStr(buffer, size, v.get()); + default: + throw ink_exception("only support toStr for numeric types"); + } + } + inline size_t strlen(const char* str) { size_t len = 0; for(const char* c = str; *c; ++c) { @@ -65,4 +92,21 @@ namespace ink::runtime::internal { inline constexpr size_t decimal_digits(float number) { return 16; } + + inline constexpr size_t value_length(const value& v) { + switch(v.type()) { + case value_type::int32: + return decimal_digits(v.get()); + case value_type::uint32: + return decimal_digits(v.get()); + case value_type::float32: + return decimal_digits(v.get()); + case value_type::string: + return strlen(v.get()); + case value_type::newline: + return 1; + default: + throw ink_exception("Can't determine length of this value type"); + } + } } From a656a865dfcadcc54cd28a0c3f5f5fa5bdf5d001 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 00:11:32 +0100 Subject: [PATCH 12/23] Squash unnecessary typeed_executer --- inkcpp/choice.cpp | 4 ++-- inkcpp/executioner.h | 14 ++++++++++++-- inkcpp/numeric_operations.h | 34 +++++++++++++++++----------------- inkcpp/runner_impl.cpp | 4 ++-- inkcpp/stack.cpp | 8 ++++---- inkcpp/value.h | 2 +- inkcpp_test/Value.cpp | 23 +++++++++++------------ shared/public/system.h | 6 ++++++ 8 files changed, 55 insertions(+), 40 deletions(-) diff --git a/inkcpp/choice.cpp b/inkcpp/choice.cpp index 93110937..621291ef 100644 --- a/inkcpp/choice.cpp +++ b/inkcpp/choice.cpp @@ -11,10 +11,10 @@ namespace ink if (in.queued() == 2) { // If it's a string, just grab it. Otherwise, use allocation - const internal::data& data = in.peek(); + const internal::value& data = in.peek(); switch (data.type()) { - case internal::data_type::string: + case internal::value_type::string: _text = data.get(); in.discard(2); break; diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h index 4fecda5e..eb4a7d72 100644 --- a/inkcpp/executioner.h +++ b/inkcpp/executioner.h @@ -10,7 +10,17 @@ namespace ink::runtime::internal { - template + template + constexpr value_type next_operatable_type() { + if constexpr (operation::enabled) { + return ty; + } else if constexpr (ty >= value_type::OP_END){ + return value_type::OP_END; + } else { + return next_operatable_type(); + } + } + template()> class typed_executer { public: template @@ -21,7 +31,7 @@ namespace ink::runtime::internal { else { _typed_exe(t, s, v); } } private: - typed_executer _typed_exe; + typed_executer()> _typed_exe; operation _op; }; template diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index bd783f33..ea50acde 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -37,7 +37,7 @@ namespace ink::runtime::internal { } template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -48,7 +48,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -59,7 +59,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -70,7 +70,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -81,7 +81,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -90,7 +90,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -102,7 +102,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -115,7 +115,7 @@ namespace ink::runtime::internal { template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -127,7 +127,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -140,7 +140,7 @@ namespace ink::runtime::internal { template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -152,7 +152,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -164,7 +164,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -173,7 +173,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -182,7 +182,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -195,7 +195,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -208,7 +208,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { @@ -217,7 +217,7 @@ namespace ink::runtime::internal { }; template - class operation> : operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(eval_stack& stack, value* vals) { diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index fbc7c635..39dbb299 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -354,7 +354,7 @@ namespace ink::runtime::internal { // Check if the old newline is still present (hasn't been glu'd) and // if there is new text (non-whitespace) in the stream since saving - bool stillHasNewline = _output.saved_ends_with(data_type::newline); + bool stillHasNewline = _output.saved_ends_with(value_type::newline); bool hasAddedNewText = _output.text_past_save(); // Newline is still there and there's no new text @@ -401,7 +401,7 @@ namespace ink::runtime::internal } // If we're on a newline - if (_output.ends_with(data_type::newline)) + if (_output.ends_with(value_type::newline)) { // TODO: REMOVE // return true; diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index d409777b..e451ee16 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -371,7 +371,7 @@ namespace ink::runtime::internal // Keep popping until we find the requested thread's end marker const entry* top = pop(); while (!( - top->data.type() == data_type::thread_end && + top->data.type() == value_type::thread_end && top->data.get() == thread)) { inkAssert(!is_empty(), "Ran out of stack while searching for end of thread marker. Did you call complete_thread?"); @@ -400,7 +400,7 @@ namespace ink::runtime::internal // If we're deleting a useless thread block if (nulling != ~0) { // If this is the start of the block, stop deleting - if (elem.name == InvalidHash && elem.data.type() == data_type::thread_start && elem.data.get().jump == nulling) { + if (elem.name == InvalidHash && elem.data.type() == value_type::thread_start && elem.data.get().jump == nulling) { nulling = ~0; } @@ -420,7 +420,7 @@ namespace ink::runtime::internal } // Clear thread frame markers. We can't use them anymore - if (elem.name == InvalidHash && elem.data.type() == data_type::thread_frame) { + if (elem.name == InvalidHash && elem.data.type() == value_type::thread_frame) { elem.name = NulledHashId; } } @@ -511,7 +511,7 @@ namespace ink::runtime::internal void basic_eval_stack::forget() { // Clear out - data x; x.set(); + value x; x.set(); value none = value(x); base::forget([&none](value& elem) { elem = none; }); } diff --git a/inkcpp/value.h b/inkcpp/value.h index 4e471c81..e625da86 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -59,7 +59,7 @@ namespace ink::runtime::internal { return *this; } - value_type type() const { return _type; } + constexpr value_type type() const { return _type; } friend basic_stream& operator<<(basic_stream& os, const value&); friend basic_stream& operator>>(basic_stream& is, value&); diff --git a/inkcpp_test/Value.cpp b/inkcpp_test/Value.cpp index 11534538..d6917d8c 100644 --- a/inkcpp_test/Value.cpp +++ b/inkcpp_test/Value.cpp @@ -7,7 +7,6 @@ using ink::runtime::internal::value; using ink::runtime::internal::value_type; -using ink::runtime::internal::data_type; using ink::runtime::internal::string_table; using stream = ink::runtime::internal::stream<128>; using ink::runtime::internal::executer; @@ -37,7 +36,7 @@ SCENARIO("compare concatenated values") value res = stack.pop(); THEN("== results in true") { - REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.type() == value_type::boolean); REQUIRE(res.get() == true); } } @@ -49,7 +48,7 @@ SCENARIO("compare concatenated values") value res = stack.pop(); THEN("== results in false") { - REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.type() == value_type::boolean); REQUIRE(res.get() == false); } } @@ -81,7 +80,7 @@ SCENARIO("compare concatenated values") value res = stack.pop(); THEN("== returns true") { - REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.type() == value_type::boolean); REQUIRE(res.get() == true); } } @@ -97,7 +96,7 @@ SCENARIO("compare concatenated values") value res = stack.pop(); THEN("== results true") { - REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.type() == value_type::boolean); REQUIRE(res.get() == true); } } @@ -113,7 +112,7 @@ SCENARIO("compare concatenated values") value res = stack.pop(); THEN("== returns false") { - REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.type() == value_type::boolean); REQUIRE(res.get() == false); } } @@ -135,9 +134,9 @@ SCENARIO("compare concatenated values") value res2 = stack.pop(); THEN("== returns true") { - REQUIRE(res1.type() == data_type::boolean); + REQUIRE(res1.type() == value_type::boolean); REQUIRE(res1.get() == true); - REQUIRE(res2.type() == data_type::boolean); + REQUIRE(res2.type() == value_type::boolean); REQUIRE(res2.get() == true); } } @@ -149,7 +148,7 @@ SCENARIO("compare concatenated values") value res = stack.pop(); THEN("== returns true") { - REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.type() == value_type::boolean); REQUIRE(res.get() == true); } } @@ -161,7 +160,7 @@ SCENARIO("compare concatenated values") value res = stack.pop(); THEN("== returns false") { - REQUIRE(res.type() == data_type::boolean); + REQUIRE(res.type() == value_type::boolean); REQUIRE(res.get() == false); } } @@ -173,14 +172,14 @@ SCENARIO("compare concatenated values") { ops(Command::ADD, stack); value res = stack.pop(); - REQUIRE(res.type() == data_type::float32); + REQUIRE(res.type() == value_type::float32); REQUIRE(res.get() == 13.f); } THEN("dividing results in 0.625") { ops(Command::DIVIDE, stack); value res = stack.pop(); - REQUIRE(res.type() == data_type::float32); + REQUIRE(res.type() == value_type::float32); REQUIRE(res.get() == 0.625f); } } diff --git a/shared/public/system.h b/shared/public/system.h index 0a7d6a5c..5f85b50b 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -121,6 +121,12 @@ namespace ink { template struct always_false { static constexpr bool value = false; }; + template + struct if_else { static constexpr T value{}; }; + template + struct if_else{ static constexpr T value = V1; }; + template + struct if_else{ static constexpr T value = V2; }; } #ifdef INK_ENABLE_STL From e122a8c0af0780a89e43df055fbd1fc73f0b6b0f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 10:57:34 +0100 Subject: [PATCH 13/23] Add conversion from bool to uint32t --- inkcpp/numeric_operations.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index ea50acde..52625457 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -15,6 +15,8 @@ namespace ink::runtime::internal { namespace casting { template<> constexpr value_type cast = value_type::float32; + template<> + constexpr value_type cast = value_type::uint32; template inline value::ret::type numeric_cast(const value& v) { if (to == v.type()) { return v.get(); } @@ -23,6 +25,17 @@ namespace ink::runtime::internal { } } + template<> + inline value::ret::type numeric_cast(const value& v) { + switch(v.type()) { + case value_type::uint32: + return v.get(); + case value_type::boolean: + return static_cast(v.get()); + default: + throw ink_exception("invalid cast to uint!"); + } + } template<> inline float numeric_cast(const value& v) { switch(v.type()) { From ab9aa8c60dd62aced976eecd49e17c667c46c1c6 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 12:47:54 +0100 Subject: [PATCH 14/23] Add documentation --- inkcpp/casting.h | 23 +++++- inkcpp/executioner.h | 25 ++++-- inkcpp/numeric_operations.h | 10 ++- inkcpp/operation_bases.h | 11 ++- inkcpp/operations.h | 23 +++++- inkcpp/output.cpp | 9 ++- inkcpp/stack.cpp | 9 ++- inkcpp/string_operations.cpp | 47 ++++++++--- inkcpp/string_operations.h | 3 + inkcpp/value.h | 147 +++++++++++++++++++++-------------- notes/OperationNotes.md | 35 +++++++++ shared/public/system.h | 26 +++++-- 12 files changed, 271 insertions(+), 97 deletions(-) create mode 100644 notes/OperationNotes.md diff --git a/inkcpp/casting.h b/inkcpp/casting.h index 8a8c4a02..bb267e5a 100644 --- a/inkcpp/casting.h +++ b/inkcpp/casting.h @@ -4,7 +4,10 @@ namespace ink::runtime::internal::casting { - class casting_matrix_type { + /** + * @brief casting_matrix data and access wrapper. + */ + struct casting_matrix_type { public: constexpr value_type get(value_type t1, value_type t2) const { return _data[static_cast(t1)][static_cast(t2)]; @@ -13,12 +16,17 @@ namespace ink::runtime::internal::casting { value_type _data[N][N]; }; + // iterate through each value_type combination and populate the + // casting_matrix template constexpr void set_cast (value_type data[static_cast(value_type::OP_END)][static_cast(value_type::OP_END)]){ if constexpr (t2 == value_type::OP_END) { + // end reached } else if constexpr (t1 == value_type::OP_END) { + // go to next row set_cast(data); } else { + // get entry from cast constexpr size_t n1 = static_cast(t1); constexpr size_t n2 = static_cast(t2); if constexpr (n1 < n2) { @@ -30,13 +38,26 @@ namespace ink::runtime::internal::casting { } } + // function to populate casting_matrix constexpr casting_matrix_type construct_casting_matrix() { casting_matrix_type cm; set_cast(cm._data); return cm; } + + /// NxN matrix which contains in cell i,j the common base of value_type(i) + /// and value_type(j). static constexpr casting_matrix_type casting_matrix = construct_casting_matrix(); + /** + * @brief returns a type where each value can be casted to. + * Result based on `cast = value_type` + * definitions. + * @tparam N number of values/value array length + * @param vs array which contains the values + * @return value_type::none if there is no common base defined + * @return common base of types in vs else + */ template value_type common_base(const value* vs) { if constexpr (N == 0) { return value_type::none; } diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h index eb4a7d72..1a631d89 100644 --- a/inkcpp/executioner.h +++ b/inkcpp/executioner.h @@ -1,11 +1,10 @@ #pragma once +#include "system.h" #include "value.h" #include "stack.h" #include "operations.h" -#include - namespace ink::runtime::internal { @@ -23,6 +22,7 @@ namespace ink::runtime::internal { template()> class typed_executer { public: + static constexpr bool enabled = true; template typed_executer(const T& t) : _typed_exe{t}, _op{t} {} @@ -37,6 +37,7 @@ namespace ink::runtime::internal { template class typed_executer { public: + static constexpr bool enabled = false; template typed_executer(const T& t) {} @@ -45,7 +46,17 @@ namespace ink::runtime::internal { } }; - template + template + constexpr Command next_operatable_command() { + if constexpr (typed_executer::enabled) { + return cmd; + } else if constexpr (cmd >= Command::OP_END){ + return Command::OP_END; + } else { + return next_operatable_command(); + } + } + template()> class executer_imp { public: template @@ -53,9 +64,9 @@ namespace ink::runtime::internal { void operator()(Command c, eval_stack& s) { if (c == cmd) { - static constexpr size_t N = CommandNumArguments; + static constexpr size_t N = command_num_args(cmd); value args[N]; - for (int i = CommandNumArguments-1; i >= 0 ; --i) { + for (int i = command_num_args(cmd)-1; i >= 0 ; --i) { args[i] = s.pop(); } value_type ty = casting::common_base(args); @@ -63,7 +74,7 @@ namespace ink::runtime::internal { } else { _exe(c, s); } } private: - executer_imp _exe; + executer_imp()> _exe; typed_executer _typed_exe; }; template<> @@ -79,7 +90,7 @@ namespace ink::runtime::internal { class executer { public: template - executer(Args& ... args) : _executer{std::tuple(&args...)} {} + executer(Args& ... args) : _executer{tuple(&args...)} {} void operator()(Command cmd, eval_stack& stack) { _executer(cmd, stack); } diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index 52625457..d63e6aae 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -1,22 +1,27 @@ #pragma once namespace ink::runtime::internal { + /// define numeric value types template using is_numeric_t = typename std::enable_if< ty == value_type::int32 || ty == value_type::uint32 || ty == value_type::float32, void>::type; + /// define integral value types template using is_integral_t = typename std::enable_if< ty == value_type::int32 || ty == value_type::uint32, void>::type; namespace casting { + /// define valid casts template<> constexpr value_type cast = value_type::float32; template<> constexpr value_type cast = value_type::uint32; + + /// defined numeric cast template inline value::ret::type numeric_cast(const value& v) { if (to == v.type()) { return v.get(); } @@ -25,8 +30,9 @@ namespace ink::runtime::internal { } } + /// specialisation for uint32 template<> - inline value::ret::type numeric_cast(const value& v) { + inline value::ret::type numeric_cast(const value& v) { switch(v.type()) { case value_type::uint32: return v.get(); @@ -36,6 +42,8 @@ namespace ink::runtime::internal { throw ink_exception("invalid cast to uint!"); } } + + /// specialisation for float32 template<> inline float numeric_cast(const value& v) { switch(v.type()) { diff --git a/inkcpp/operation_bases.h b/inkcpp/operation_bases.h index e8a83390..6eabefda 100644 --- a/inkcpp/operation_bases.h +++ b/inkcpp/operation_bases.h @@ -1,10 +1,12 @@ #pragma once -#include +#include "system.h" namespace ink::runtime::internal { class string_table; + /// base class for operations to acquire data and provide flags and + /// constructor template class operation_base { public: @@ -21,12 +23,17 @@ namespace ink::runtime::internal { operation_base(const T&) {} }; + // base class for operations which needs a string_table template<> class operation_base { public: static constexpr bool enabled = true; template - operation_base(const T& t) : _string_table{*std::get(t)} {} + operation_base(const T& t) : _string_table{*std::get(t)} { + static_assert(has_type::value, "Executioner " + "constructor needs a string table to instantiate " + "some operations!"); + } protected: string_table& _string_table; diff --git a/inkcpp/operations.h b/inkcpp/operations.h index 69842972..c512e34a 100644 --- a/inkcpp/operations.h +++ b/inkcpp/operations.h @@ -5,12 +5,18 @@ namespace ink::runtime::internal { namespace casting { + // default cast to none (invalid cast) template constexpr value_type cast = value_type::none; + + // no cast for same type template constexpr value_type cast = t; } + /** + * @brief Determines the number of arguments needed for an command. + */ constexpr size_t command_num_args(Command cmd) { if (cmd >= Command::BINARY_OPERATORS_START && cmd <= Command::BINARY_OPERATORS_END) { return 2; @@ -20,16 +26,26 @@ namespace ink::runtime::internal { return 0; } } - template - static constexpr size_t CommandNumArguments = command_num_args(cmd); + /** + * @brief Operation definition. + * A class which contains a call operator to execute the operation needed + * for the command type combination. + * @tparam cmd Command which should be executed + * @tparam ty type on which the command should be executed + */ template class operation { public: static constexpr bool enabled = false; template operation(const T& t) {} - void operator()(eval_stack&, value*) { + /** + * @brief execute operation. + * @param stack were the result(s) get pushed + * @param vs array of values, first one = first argument etc + */ + void operator()(eval_stack& stack, value* vs) { throw ink_exception("operation not implemented!"); } }; @@ -37,6 +53,5 @@ namespace ink::runtime::internal { #include "operation_bases.h" #include "numeric_operations.h" -/* #include "marker_operations.h" */ #include "string_operations.h" #include "casting.h" diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 3ebdb44f..74898e20 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -468,9 +468,12 @@ namespace ink // Find all allocated strings and mark them as used for (int i = 0; i < _size; i++) { - // FIXME: only allocated strings!! - if (_data[i].type() == value_type::string) - strings.mark_used(_data[i].get()); + if (_data[i].type() == value_type::string) { + string_type str = _data[i].get(); + if (str.allocated) { + strings.mark_used(str.str); + } + } } } diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index e451ee16..caf99c9f 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -35,13 +35,13 @@ namespace ink::runtime::internal // If this is an end thread marker, skip over it if (skip == ~0 && e.data.type() == value_type::thread_end) { - skip = e.data.get(); + skip = e.data.get(); } // If we're skipping if (skip != ~0) { // Stop if we get to the start of the thread block - if (e.data.type() == value_type::thread_start && skip == e.data.get()) { + if (e.data.type() == value_type::thread_start && skip == e.data.get().jump) { skip = ~0; } @@ -493,7 +493,10 @@ namespace ink::runtime::internal // Iterate everything (including what we have saved) and mark strings base::for_each_all([&strings](const value& elem) { if (elem.type() == value_type::string) { - strings.mark_used(elem.get()); + string_type str = elem.get(); + if (str.allocated) { + strings.mark_used(str.str); + } } }); } diff --git a/inkcpp/string_operations.cpp b/inkcpp/string_operations.cpp index 9dd5ed3f..8d853542 100644 --- a/inkcpp/string_operations.cpp +++ b/inkcpp/string_operations.cpp @@ -6,6 +6,10 @@ namespace ink::runtime::internal { namespace casting { + /** + * @brief Wrapper to cast values to string. + * string representation is stored inside string_cast. + */ class string_cast { public: string_cast(const value& val); @@ -16,12 +20,22 @@ namespace ink::runtime::internal { char _data[512]; //TODO define central }; + /** + * @brief template for string conversion. + * @param buffer to store the string + * @param n length of buffer + * @param v value to convert + * @tparam ty value type of v + */ template int cast_to_string(char* buffer, size_t n, const value& v){ ink_exception("cast not implemented"); return -1; } + /** + * @brief wrapper to call correct cast_to_string function. + */ template int to_string(char data[N], const value& v) { if (v.type() == ty) { @@ -33,60 +47,71 @@ namespace ink::runtime::internal { } } + // cast for int32 template<> int cast_to_string(char* data, size_t size, const value& v) { return toStr(data, size, v.get()); } + + // cast for uint32 template<> int cast_to_string(char* data, size_t size, const value& v) { return toStr(data, size, v.get()); } + + // cast for float32 template<> int cast_to_string(char* data, size_t size, const value& v) { return toStr(data, size, v.get()); } + + // cast for newline template<> int cast_to_string(char* data, size_t size, const value& v) { return toStr(data, size, "\n"); } + // constructor for string_cast class string_cast::string_cast(const value& val) : _val{val}, _str{nullptr} { if (val.type() == value_type::string) { + // reference string if value is already a string _str = val.get(); } else { + // convert else _str = _data; to_string<512>(_data, val); } } } void operation::operator()(eval_stack& stack, value* vals) { + // convert values to strings casting::string_cast lh(vals[0]); casting::string_cast rh (vals[1]); + + // create new string with needed size char* str = _string_table.create(strlen(lh.get()) + strlen(rh.get()) + 1); + + // copy to new string char* dst = str; for(const char* src = lh.get(); *src; ++src) { *dst++ = *src; } for(const char* src = rh.get(); *src; ++src) { *dst++ = *src; } *dst = 0; + stack.push(value{}.set(str)); } void operation::operator()(eval_stack& stack, value* vals) { + // convert values to string casting::string_cast lh (vals[0]); casting::string_cast rh(vals[1]); + + // compare strings char wise const char* li = lh.get(); const char* ri = rh.get(); - bool res; - while(*li && *ri) { - ++li; - ++ri; - } - if (*li != 0 || *ri != 0) { - res = false; - } else { - res = true; - } - stack.push(value{}.set(res)); + while(*li && *ri && *li++ == *ri++); + + stack.push(value{}.set(*li == *ri)); } } diff --git a/inkcpp/string_operations.h b/inkcpp/string_operations.h index 0a39be44..72727728 100644 --- a/inkcpp/string_operations.h +++ b/inkcpp/string_operations.h @@ -4,6 +4,7 @@ namespace ink::runtime::internal { namespace casting { + // define valid castings template<> constexpr value_type cast = value_type::string; template<> @@ -14,6 +15,7 @@ namespace ink::runtime::internal { constexpr value_type cast = value_type::string; } + // operation declaration add template<> class operation : public operation_base { public: @@ -21,6 +23,7 @@ namespace ink::runtime::internal { void operator()(eval_stack& stack, value* vals); }; + // operation declaration equality template<> class operation : public operation_base { public: diff --git a/inkcpp/value.h b/inkcpp/value.h index e625da86..9408ea87 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -10,65 +10,94 @@ namespace ink::runtime::internal { class basic_stream; + + /// different existing value_types enum class value_type { - BEGIN, - none = BEGIN, - divert, - uint32, - PRINT_BEGIN, - boolean = PRINT_BEGIN, - int32, - float32, - string, - OP_END, - newline = OP_END, - PRINT_END, - marker = PRINT_END, - glue, - func_start, - func_end, - null, - tunnel_frame, - function_frame, - thread_start, - thread_frame, - thread_end, - jump_marker + BEGIN, // To find the start of list + none = BEGIN, // no type -> invalid + divert, // divert to different story position + PRINT_BEGIN, // first printable value + boolean = PRINT_BEGIN, // boolean variable + uint32, // 32bit unsigned integer variable + int32, // 32bit integer variable + float32, // 32bit floating point value + string, // Pointer to string + OP_END, // END of types where we can operate on + newline = OP_END, // newline symbol + PRINT_END, // END of printable values + marker = PRINT_END, // special marker (used in output stream) + glue, // glue symbol + func_start, // start of function marker + func_end, // end of function marker + null, // void value, for function returns + tunnel_frame, // return from tunnel + function_frame, // return from function + thread_frame, // return from thread + thread_start, // start of thread frame + thread_end, // end of thread frame + jump_marker // callstack jump }; + // add operator for value_type (to simplify usage templates). constexpr value_type operator+(value_type t, int i) { return static_cast(static_cast(t)+i); } + // add operator for Command (to simplify usage in templates). constexpr Command operator+(Command c, int i) { return static_cast(static_cast(c)+i); } + struct string_type{ + constexpr string_type(const char* string, bool allocated) + : str{string}, allocated{allocated}{} + constexpr string_type(const char* string) + : str{string}, allocated{true} {} + operator const char*() const { + return str; + } + const char* str; + bool allocated; + }; + /** + * @brief class to wrap stack value to common type. + */ class value { public: + /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; constexpr value() : _type{value_type::none}, uint32_value{0}{} + + /// get value of the type (if possible) template typename ret::type get() const { static_assert(ty != ty, "No getter for this type defined!"); } + /// set value of type (if possible) template constexpr value& set(Args ...args) { static_assert(sizeof...(Args)!=sizeof...(Args), "No setter for this type defined!"); return *this; } + /// get type of value constexpr value_type type() const { return _type; } + friend basic_stream& operator<<(basic_stream& os, const value&); friend basic_stream& operator>>(basic_stream& is, value&); - constexpr bool printable() const { return _type >= value_type::PRINT_BEGIN && _type < value_type::PRINT_END; } + /// returns if type is printable (see value_type) + constexpr bool printable() const { + return _type >= value_type::PRINT_BEGIN && _type < value_type::PRINT_END; + } + private: + /// actual storage union { bool bool_value; int32_t int32_value; - const char* str_value; + string_type string_value; uint32_t uint32_value; float float_value; struct { @@ -82,6 +111,8 @@ namespace ink::runtime::internal { #ifdef INK_ENABLE_STL std::ostream& operator<<(std::ostream&,const value&); #endif + + // define get and set for int32 template<> struct value::ret { using type = int32_t; }; template<> inline int32_t value::get() const { return int32_value; } template<> @@ -92,6 +123,7 @@ namespace ink::runtime::internal { } + // define get and set for uint32 template<> struct value::ret { using type = uint32_t; }; template<> inline uint32_t value::get() const { return uint32_value; } template<> @@ -100,6 +132,8 @@ namespace ink::runtime::internal { _type = value_type::uint32; return *this; } + + // define get and set for divert template<> struct value::ret { using type = uint32_t; }; template<> inline uint32_t value::get() const { return uint32_value; } template<> @@ -109,6 +143,7 @@ namespace ink::runtime::internal { return *this; } + // define get and set for float template<> struct value::ret { using type = float; }; template<> inline float value::get() const { return float_value; } template<> @@ -118,6 +153,7 @@ namespace ink::runtime::internal { return *this; } + // define get and set for boolean template<> struct value::ret { using type = bool; }; template<> inline bool value::get() const { return bool_value; } template<> @@ -127,37 +163,35 @@ namespace ink::runtime::internal { return *this; } - template<> struct value::ret { using type = const char*; }; - template<> inline const char* value::get() const { return str_value; } + // define get and set for string + template<> struct value::ret { using type = string_type; }; + template<> inline string_type value::get() const { return string_value; } template<> inline constexpr value& value::set(const char* v) { - // TODO: decode allocw - str_value = v; + string_value = {v}; _type = value_type::string; return *this; } template<> inline constexpr value& value::set(char* v) { - // TODO: decode allocw - str_value = v; + string_value = {v}; _type = value_type::string; return *this; } template<> inline constexpr value& value::set(const char* v, bool allocated) { - // TODO use bool - str_value = v; + string_value = {v, allocated}; _type = value_type::string; return *this; } template<> inline constexpr value& value::set( char* v, bool allocated) { - // TODO use bool - str_value = v; + string_value = {v, allocated}; _type = value_type::string; return *this; } + // define getter and setter for jump_marker template<> struct value::ret { using type = decltype(value::jump); }; template<> inline value::ret::type value::get() const { return jump; } template<> @@ -174,6 +208,7 @@ namespace ink::runtime::internal { return *this; } + // define getter and setter for thread_start template<> struct value::ret { using type = decltype(value::jump); }; template<> inline value::ret::type value::get() const { return jump; } template<> @@ -191,6 +226,7 @@ namespace ink::runtime::internal { return *this; } + // define getter and setter for thread_end template<> struct value::ret { using type = uint32_t; }; template<> inline uint32_t value::get() const { return uint32_value; } template<> @@ -200,6 +236,7 @@ namespace ink::runtime::internal { return *this; } + // define setter for values without storage template<> inline constexpr value& value::set() { _type = value_type::marker; @@ -236,45 +273,42 @@ namespace ink::runtime::internal { _type = value_type::null; return *this; } + template<> + inline constexpr value& value::set() { + _type = value_type::none; + return *this; + } + // getter and setter for different frame types + // FIXME: the getter are not used? + /* template<> struct value::ret{ using type = uint32_t; }; */ + /* template<> inline uint32_t value::get() const { return uint32_value; } */ template<> inline constexpr value& value::set(uint32_t v) { uint32_value = v; _type = value_type::function_frame; return *this; } + // FIXME: the getter are not used? + /* template<> struct value::ret{ using type = uint32_t; }; */ + /* template<> inline uint32_t value::get() const { return uint32_value; } */ template<> inline constexpr value& value::set(uint32_t v) { uint32_value = v; _type = value_type::tunnel_frame; return *this; } + // FIXME: the getter are not used? + /* template<> struct value::ret{ using type = uint32_t; }; */ + /* template<> inline uint32_t value::get() const { return uint32_value; } */ template<> inline constexpr value& value::set(uint32_t v) { uint32_value = v; _type = value_type::thread_frame; return *this; } - template<> - inline constexpr value& value::set() { - _type = value_type::tunnel_frame; - return *this; - } - template<> - inline constexpr value& value::set() { - _type = value_type::function_frame; - return *this; - } - template<> - inline constexpr value& value::set() { - _type = value_type::thread_end; - return *this; - } - template<> - inline constexpr value& value::set() { - _type = value_type::none; - return *this; - } + + // static constexpr instantiations of flag values namespace values { static constexpr value marker = value{}.set(); static constexpr value glue = value{}.set(); @@ -282,8 +316,5 @@ namespace ink::runtime::internal { static constexpr value func_start = value{}.set(); static constexpr value func_end = value{}.set(); static constexpr value null = value{}.set(); - static constexpr value tunnel_frame = value{}.set(); - static constexpr value function_frame = value{}.set(); - static constexpr value thread_end = value{}.set(); } } diff --git a/notes/OperationNotes.md b/notes/OperationNotes.md new file mode 100644 index 00000000..2966a494 --- /dev/null +++ b/notes/OperationNotes.md @@ -0,0 +1,35 @@ +Operations are everything which works only on the evaluation stack! +(eg. ADD, NOT, EQUAL, etc.) + +To use them include `executioner.h`, this will take care of including the other +needed header in the correct order. + +The `executioner` need to instantiation time all resources needed for all +supported operations (a string_table for now). + +Then you can execute a command with the call operator of the `executioner`. +`void operator()(Command, eval_stack&)` + +The executioner then iterates through a compile time created and optimization +list of all types. To determine how many arguments needed +(with `size_t command_num_args(Command)`) and then finds the matching operator +for the value type. + +Type casting is solved with a cast matrix, where each entry is defined with: +`template<> constexpr value_type cast = value_type` +`template<> constexpr value_type cast = resulting_type` + +! At the moment we must ensure that `(int)(t1) < (int)(t2)`! + +The gain: + +* Operation handling can be separated in different files + (`numeric_operations.h, string_operations.h`). The value class only knows what + data it contains, and not how to handle it => adding new types or operators + is know possible without changing many `switch cases`. +* Values are acquired with `get()` witch allows changing type + without breaking unnoticed code. +* Not one huge file. +* The `executioner` knows the resources for the operations + => no need to pass a string_table to each add. +* everything is without virtualisation => no/negligible runtime overhead. diff --git a/shared/public/system.h b/shared/public/system.h index 5f85b50b..3937be4a 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -12,6 +12,7 @@ #include #include #include +#include #endif namespace ink @@ -117,16 +118,27 @@ namespace ink }; #endif +#ifdef INK_ENABLE_STL + template + using tuple = std::tuple; +#else +#endif + namespace runtime::internal { + struct false_type { static constexpr bool value = false; }; + struct true_type { static constexpr bool value = true; }; + template + struct always_false : false_type {}; + + template + struct has_type; template - struct always_false { static constexpr bool value = false; }; - template - struct if_else { static constexpr T value{}; }; - template - struct if_else{ static constexpr T value = V1; }; - template - struct if_else{ static constexpr T value = V2; }; + struct has_type> : false_type {}; + template + struct has_type> : has_type> {}; + template + struct has_type> : true_type {}; } #ifdef INK_ENABLE_STL From 27f4f1e8345996670de3a53cca2449191ef8cdb1 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 14:40:59 +0100 Subject: [PATCH 15/23] Working tuple replacement --- inkcpp/operation_bases.h | 2 +- shared/public/system.h | 111 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/inkcpp/operation_bases.h b/inkcpp/operation_bases.h index 6eabefda..e2563dd7 100644 --- a/inkcpp/operation_bases.h +++ b/inkcpp/operation_bases.h @@ -29,7 +29,7 @@ namespace ink::runtime::internal { public: static constexpr bool enabled = true; template - operation_base(const T& t) : _string_table{*std::get(t)} { + operation_base(const T& t) : _string_table{*t_get(t)} { static_assert(has_type::value, "Executioner " "constructor needs a string table to instantiate " "some operations!"); diff --git a/shared/public/system.h b/shared/public/system.h index 3937be4a..6e7dcd36 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -1,6 +1,7 @@ #pragma once #include "config.h" +#include "traits.h" #ifdef INK_ENABLE_UNREAL #include "Misc/AssertionMacros.h" @@ -121,7 +122,117 @@ namespace ink #ifdef INK_ENABLE_STL template using tuple = std::tuple; + template + constexpr auto t_get = std::get; #else + namespace tuple_internal { + template + class tuple_leaf{ + public: + tuple_leaf() : _value() {}; + template + explicit tuple_leaf(U&& u) : _value(std::forward(u)) {} + T& get() { return _value; } + const T& get() const { return _value; } + private: + T _value; + tuple_leaf(const tuple_leaf& tl) = delete; + tuple_leaf& operator=(const tuple_leaf&) = delete; + }; + + // handle indexing + template + struct tuple_indexes {}; + + template + struct make_tuple_indexes { + using type = typename make_tuple_indexes::type; + }; + template + struct make_tuple_indexes { + using type = tuple_indexes; + }; + + // handle types + template + struct tuple_type : tuple_type {}; + template + struct tuple_type<0, T, Tys...> { + using type = T; + }; + + template + struct type_index_imp : type_index_imp {}; + template + struct type_index_imp { + static constexpr size_t value = I; + }; + template + constexpr size_t type_index = type_index_imp::value; + + + template + struct tuple_imp; + + template + struct tuple_imp, Tys...> + : public tuple_leaf... + { + template + tuple_imp(Us&& ... us) : tuple_leaf(std::forward(us))... { + static_assert(sizeof...(Us) == sizeof...(Tys), + "Tuple must be initialized with same amount of arguments" + ", then types!"); + } + }; + } + + /// minimal tuple class, only for simple data types! + template + class tuple + : public tuple_internal::tuple_imp< + typename tuple_internal::make_tuple_indexes::type, + Tys... > + { + using base = tuple_internal::tuple_imp< + typename tuple_internal::make_tuple_indexes::type, + Tys...>; + using this_type = tuple; + template + using element_type = typename tuple_internal::tuple_type::type; + template + static constexpr size_t type_index = tuple_internal::type_index; + public: + template + tuple(Us&& ... us) : base(std::forward(us)...) {} + + template + friend + inline constexpr element_type const& + t_get(const this_type& t); + template + friend + const T& + t_get(const this_type& t); + }; + + template + inline constexpr typename T::template element_type const& + t_get(const T& t) { + return static_cast>const&>(t).get(); + }; + + template + const T& + t_get(const Tuple& t) { + return t_get>(t); + }; + + template<> + class tuple<> { + public: + tuple() {} + }; #endif namespace runtime::internal From 54c3499e98ea184fe02e3bfe1f4f40f1dc624c26 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 18:45:55 +0100 Subject: [PATCH 16/23] tide closer with traits.h --- inkcpp/include/traits.h | 20 +++--- inkcpp/operation_bases.h | 3 +- inkcpp/string_operations.cpp | 2 +- inkcpp/string_utils.h | 11 +-- inkcpp/tuple.hpp | 114 +++++++++++++++++++++++++++++ shared/public/system.h | 135 ++--------------------------------- 6 files changed, 133 insertions(+), 152 deletions(-) create mode 100644 inkcpp/tuple.hpp diff --git a/inkcpp/include/traits.h b/inkcpp/include/traits.h index 5e3104fa..86ade898 100644 --- a/inkcpp/include/traits.h +++ b/inkcpp/include/traits.h @@ -1,6 +1,7 @@ #pragma once #include "config.h" +#include "system.h" #ifdef INK_ENABLE_STL #include @@ -9,19 +10,15 @@ namespace ink::runtime::internal { template - struct get - { - using type = typename get::type; - }; + struct get_ith_type : get_ith_type {}; template - struct get<0, Arg, Args...> + struct get_ith_type<0, Arg, Args...> { using type = Arg; }; // constant and is_same from http://www.cppreference.com - template struct constant { static constexpr T value = v; @@ -31,16 +28,19 @@ namespace ink::runtime::internal constexpr value_type operator()() const noexcept { return value; } //since c++14 }; + struct false_type : constant {}; + struct true_type : constant{}; + template - struct is_same : constant {}; + struct is_same : false_type {}; template - struct is_same : constant {}; + struct is_same : true_type {}; // == string testing (from me) == template - struct is_string : constant { }; + struct is_string : false_type { }; template struct is_string : is_string { }; @@ -107,7 +107,7 @@ namespace ink::runtime::internal struct argument { static_assert(N < arity, "error: invalid parameter index."); - using type = typename get::type; + using type = typename get_ith_type::type; }; }; diff --git a/inkcpp/operation_bases.h b/inkcpp/operation_bases.h index e2563dd7..79fac6c1 100644 --- a/inkcpp/operation_bases.h +++ b/inkcpp/operation_bases.h @@ -1,6 +1,7 @@ #pragma once #include "system.h" +#include "./tuple.hpp" namespace ink::runtime::internal { class string_table; @@ -29,7 +30,7 @@ namespace ink::runtime::internal { public: static constexpr bool enabled = true; template - operation_base(const T& t) : _string_table{*t_get(t)} { + operation_base(const T& t) : _string_table{*get(t)} { static_assert(has_type::value, "Executioner " "constructor needs a string table to instantiate " "some operations!"); diff --git a/inkcpp/string_operations.cpp b/inkcpp/string_operations.cpp index 8d853542..6805249f 100644 --- a/inkcpp/string_operations.cpp +++ b/inkcpp/string_operations.cpp @@ -90,7 +90,7 @@ namespace ink::runtime::internal { casting::string_cast rh (vals[1]); // create new string with needed size - char* str = _string_table.create(strlen(lh.get()) + strlen(rh.get()) + 1); + char* str = _string_table.create(c_str_len(lh.get()) + c_str_len(rh.get()) + 1); // copy to new string char* dst = str; diff --git a/inkcpp/string_utils.h b/inkcpp/string_utils.h index 300a9ce0..672bb267 100644 --- a/inkcpp/string_utils.h +++ b/inkcpp/string_utils.h @@ -1,6 +1,7 @@ #pragma once #include "system.h" +#include "traits.h" #include "value.h" #include @@ -68,14 +69,6 @@ namespace ink::runtime::internal { } } - inline size_t strlen(const char* str) { - size_t len = 0; - for(const char* c = str; *c; ++c) { - ++len; - } - return len; - } - // return a upper bound for the string representation of the number inline constexpr size_t decimal_digits(uint32_t number) { size_t length = 1; @@ -102,7 +95,7 @@ namespace ink::runtime::internal { case value_type::float32: return decimal_digits(v.get()); case value_type::string: - return strlen(v.get()); + return c_str_len(v.get()); case value_type::newline: return 1; default: diff --git a/inkcpp/tuple.hpp b/inkcpp/tuple.hpp new file mode 100644 index 00000000..91d143db --- /dev/null +++ b/inkcpp/tuple.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include "./include/traits.h" + +namespace ink::runtime::internal { + namespace tuple_internal { + /// data member of tuple + template + class tuple_leaf{ + public: + tuple_leaf() : _value() {}; + template + explicit tuple_leaf(U&& u) : _value(std::forward(u)) {} + T& get() { return _value; } + const T& get() const { return _value; } + private: + T _value; + tuple_leaf(const tuple_leaf& tl) = delete; + tuple_leaf& operator=(const tuple_leaf&) = delete; + }; + + // handle indexing + template + struct tuple_indexes {}; + + // create tuple_indexes in [Start,End[ + template + struct make_tuple_indexes { + using type = typename make_tuple_indexes::type; + }; + template + struct make_tuple_indexes { + using type = tuple_indexes; + }; + + /// get the index of first appearance of an type in tuple + template + struct type_index_imp : type_index_imp {}; + template + struct type_index_imp { + static constexpr size_t value = I; + }; + template + constexpr size_t type_index = type_index_imp::value; + + + /// implementation class to extract indices + template + struct tuple_imp; + template + struct tuple_imp, Tys...> + : public tuple_leaf... + { + template + tuple_imp(Us&& ... us) : tuple_leaf(std::forward(us))... { + static_assert(sizeof...(Us) == sizeof...(Tys), + "Tuple must be initialized with same amount of arguments" + ", then types!"); + } + }; + } + + /// minimal tuple class, only for simple data types! + /// flat tuple implementation + template + class tuple + : public tuple_internal::tuple_imp< + typename tuple_internal::make_tuple_indexes::type, + Tys... > + { + using base = tuple_internal::tuple_imp< + typename tuple_internal::make_tuple_indexes::type, + Tys...>; + using this_type = tuple; + public: + template + using element_type = typename get_ith_type::type; + template + static constexpr size_t type_index = tuple_internal::type_index; + + template + tuple(Us&& ... us) : base(std::forward(us)...) {} + }; + + /// access tuple element by index + template + constexpr auto const& + get(const T& t) { + return static_cast>const&>(t).get(); + }; + + /// access tuple element by type. First of this type + template + constexpr const T& + get(const Tuple& t) { + return get, Tuple>(t); + }; + + template<> + class tuple<> { + public: + tuple() {} + }; + + /// check if tuple contains type + template + struct has_type; + template + struct has_type> : false_type {}; + template + struct has_type> : has_type> {}; + template + struct has_type> : true_type {}; +} diff --git a/shared/public/system.h b/shared/public/system.h index 6e7dcd36..cd523dfb 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -1,7 +1,6 @@ #pragma once #include "config.h" -#include "traits.h" #ifdef INK_ENABLE_UNREAL #include "Misc/AssertionMacros.h" @@ -13,7 +12,6 @@ #include #include #include -#include #endif namespace ink @@ -119,138 +117,13 @@ namespace ink }; #endif -#ifdef INK_ENABLE_STL - template - using tuple = std::tuple; - template - constexpr auto t_get = std::get; -#else - namespace tuple_internal { - template - class tuple_leaf{ - public: - tuple_leaf() : _value() {}; - template - explicit tuple_leaf(U&& u) : _value(std::forward(u)) {} - T& get() { return _value; } - const T& get() const { return _value; } - private: - T _value; - tuple_leaf(const tuple_leaf& tl) = delete; - tuple_leaf& operator=(const tuple_leaf&) = delete; - }; - - // handle indexing - template - struct tuple_indexes {}; - - template - struct make_tuple_indexes { - using type = typename make_tuple_indexes::type; - }; - template - struct make_tuple_indexes { - using type = tuple_indexes; - }; - - // handle types - template - struct tuple_type : tuple_type {}; - template - struct tuple_type<0, T, Tys...> { - using type = T; - }; - - template - struct type_index_imp : type_index_imp {}; - template - struct type_index_imp { - static constexpr size_t value = I; - }; - template - constexpr size_t type_index = type_index_imp::value; - - - template - struct tuple_imp; - - template - struct tuple_imp, Tys...> - : public tuple_leaf... - { - template - tuple_imp(Us&& ... us) : tuple_leaf(std::forward(us))... { - static_assert(sizeof...(Us) == sizeof...(Tys), - "Tuple must be initialized with same amount of arguments" - ", then types!"); - } + namespace runtime::internal { + template + struct always_false { + static constexpr bool value = false; }; } - /// minimal tuple class, only for simple data types! - template - class tuple - : public tuple_internal::tuple_imp< - typename tuple_internal::make_tuple_indexes::type, - Tys... > - { - using base = tuple_internal::tuple_imp< - typename tuple_internal::make_tuple_indexes::type, - Tys...>; - using this_type = tuple; - template - using element_type = typename tuple_internal::tuple_type::type; - template - static constexpr size_t type_index = tuple_internal::type_index; - public: - template - tuple(Us&& ... us) : base(std::forward(us)...) {} - - template - friend - inline constexpr element_type const& - t_get(const this_type& t); - template - friend - const T& - t_get(const this_type& t); - }; - - template - inline constexpr typename T::template element_type const& - t_get(const T& t) { - return static_cast>const&>(t).get(); - }; - - template - const T& - t_get(const Tuple& t) { - return t_get>(t); - }; - - template<> - class tuple<> { - public: - tuple() {} - }; -#endif - - namespace runtime::internal - { - struct false_type { static constexpr bool value = false; }; - struct true_type { static constexpr bool value = true; }; - template - struct always_false : false_type {}; - - template - struct has_type; - template - struct has_type> : false_type {}; - template - struct has_type> : has_type> {}; - template - struct has_type> : true_type {}; - } #ifdef INK_ENABLE_STL template From 11516283cd70765b81aafb3068d31a48e992091e Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 20:36:13 +0100 Subject: [PATCH 17/23] fix compiler errors for gcc 9 --- inkcpp/casting.h | 12 +++++++----- inkcpp/numeric_operations.h | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/inkcpp/casting.h b/inkcpp/casting.h index bb267e5a..375860cf 100644 --- a/inkcpp/casting.h +++ b/inkcpp/casting.h @@ -9,17 +9,19 @@ namespace ink::runtime::internal::casting { */ struct casting_matrix_type { public: + constexpr casting_matrix_type() : _data{value_type::none}{}; constexpr value_type get(value_type t1, value_type t2) const { - return _data[static_cast(t1)][static_cast(t2)]; + return _data[static_cast(t1)*N+static_cast(t2)]; } static constexpr size_t N = static_cast(value_type::OP_END); - value_type _data[N][N]; + value_type _data[N*N]; }; // iterate through each value_type combination and populate the // casting_matrix template - constexpr void set_cast (value_type data[static_cast(value_type::OP_END)][static_cast(value_type::OP_END)]){ + constexpr void set_cast (value_type data[casting_matrix_type::N*casting_matrix_type::N]){ + if constexpr (t2 == value_type::OP_END) { // end reached } else if constexpr (t1 == value_type::OP_END) { @@ -30,9 +32,9 @@ namespace ink::runtime::internal::casting { constexpr size_t n1 = static_cast(t1); constexpr size_t n2 = static_cast(t2); if constexpr (n1 < n2) { - data[n1][n2] = cast; + data[n1*casting_matrix_type::N + n2] = cast; } else { - data[n1][n2] = cast; + data[n1*casting_matrix_type::N + n2] = cast; } set_cast(data); } diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index d63e6aae..d8448f4b 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -23,7 +23,7 @@ namespace ink::runtime::internal { /// defined numeric cast template - inline value::ret::type numeric_cast(const value& v) { + inline typename value::ret::type numeric_cast(const value& v) { if (to == v.type()) { return v.get(); } else { throw ink_exception("invalid numeric_cast!"); @@ -32,7 +32,7 @@ namespace ink::runtime::internal { /// specialisation for uint32 template<> - inline value::ret::type numeric_cast(const value& v) { + inline typename value::ret::type numeric_cast(const value& v) { switch(v.type()) { case value_type::uint32: return v.get(); From c1a5bbe7edd846f1b2ea300b767f9b203c165be6 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 20:43:29 +0100 Subject: [PATCH 18/23] Fix mac compiler complains --- inkcpp/output.cpp | 2 ++ inkcpp/runner_impl.cpp | 1 + inkcpp/string_utils.h | 5 ++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 74898e20..117ef591 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -382,6 +382,7 @@ namespace ink case value_type::newline: *ptr = '\n'; ptr++; break; + default: throw ink_exception("cant convert expression to string!"); } } @@ -434,6 +435,7 @@ namespace ink case value_type::glue: hasGlue = true; break; + default: break; } return false; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 39dbb299..a4865221 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -397,6 +397,7 @@ namespace ink::runtime::internal // Newline was removed. Proceed as if we never hit it forget(); break; + case change_type::no_change: break; } } diff --git a/inkcpp/string_utils.h b/inkcpp/string_utils.h index 672bb267..28b4a592 100644 --- a/inkcpp/string_utils.h +++ b/inkcpp/string_utils.h @@ -6,8 +6,11 @@ #include -namespace ink::runtime::internal { +#ifndef EINVAL +#define EINVAL -1 +#endif +namespace ink::runtime::internal { // error behavior from: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/itoa-s-itow-s?view=msvc-160 inline int toStr(char * buffer, size_t size, uint32_t value) { #ifdef WIN32 From a562ff6654b1df9ac2b33f00c1692bfa72e9f545 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 20:56:16 +0100 Subject: [PATCH 19/23] Set CXX version --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47812713..c820b5e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,11 +5,12 @@ enable_testing() # Project setup project(inkcpp VERSION 0.1) - +SET(CMAKE_CXX_STANDARD 17) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) # Add subdirectories add_subdirectory(shared) add_subdirectory(inkcpp) add_subdirectory(inkcpp_compiler) add_subdirectory(inkcpp_cl) add_subdirectory(inkcpp_test) -add_subdirectory(unreal) \ No newline at end of file +add_subdirectory(unreal) From 0f42c435c5724c073fc063c8874ebf996e4b84a8 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 21:06:36 +0100 Subject: [PATCH 20/23] Try to fix clangs complains --- inkcpp/casting.h | 4 ++-- inkcpp/numeric_operations.h | 6 ++++-- inkcpp/operations.h | 8 ++++++-- inkcpp/string_operations.h | 12 ++++++++---- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/inkcpp/casting.h b/inkcpp/casting.h index 375860cf..5299bf82 100644 --- a/inkcpp/casting.h +++ b/inkcpp/casting.h @@ -32,9 +32,9 @@ namespace ink::runtime::internal::casting { constexpr size_t n1 = static_cast(t1); constexpr size_t n2 = static_cast(t2); if constexpr (n1 < n2) { - data[n1*casting_matrix_type::N + n2] = cast; + data[n1*casting_matrix_type::N + n2] = cast::value; } else { - data[n1*casting_matrix_type::N + n2] = cast; + data[n1*casting_matrix_type::N + n2] = cast::value; } set_cast(data); } diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index d8448f4b..26456381 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -17,9 +17,11 @@ namespace ink::runtime::internal { namespace casting { /// define valid casts template<> - constexpr value_type cast = value_type::float32; + struct cast + { static constexpr value_type value = value_type::float32; }; template<> - constexpr value_type cast = value_type::uint32; + struct cast + { static constexpr value_type value = value_type::uint32; }; /// defined numeric cast template diff --git a/inkcpp/operations.h b/inkcpp/operations.h index c512e34a..4cef6d24 100644 --- a/inkcpp/operations.h +++ b/inkcpp/operations.h @@ -7,11 +7,15 @@ namespace ink::runtime::internal { namespace casting { // default cast to none (invalid cast) template - constexpr value_type cast = value_type::none; + struct cast { + static constexpr value_type value = value_type::none; + }; // no cast for same type template - constexpr value_type cast = t; + struct cast { + static constexpr value_type value = t; + }; } /** diff --git a/inkcpp/string_operations.h b/inkcpp/string_operations.h index 72727728..229dca52 100644 --- a/inkcpp/string_operations.h +++ b/inkcpp/string_operations.h @@ -6,13 +6,17 @@ namespace ink::runtime::internal { namespace casting { // define valid castings template<> - constexpr value_type cast = value_type::string; + struct cast + { static constexpr value_type value = value_type::string; }; template<> - constexpr value_type cast = value_type::string; + struct cast + { static constexpr value_type value = value_type::string; }; template<> - constexpr value_type cast = value_type::string; + struct cast + { static constexpr value_type value = value_type::string; }; template<> - constexpr value_type cast = value_type::string; + struct cast + { static constexpr value_type value = value_type::string; }; } // operation declaration add From 1e7d72d5aa49557097de221d1ab25a742162bcd2 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 25 Feb 2021 23:12:41 +0100 Subject: [PATCH 21/23] Adapt to MSVC compiler --- inkcpp/executioner.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h index 1a631d89..3a56fc2b 100644 --- a/inkcpp/executioner.h +++ b/inkcpp/executioner.h @@ -9,17 +9,18 @@ namespace ink::runtime::internal { - template + template constexpr value_type next_operatable_type() { + constexpr value_type ty = t + Offset; if constexpr (operation::enabled) { return ty; } else if constexpr (ty >= value_type::OP_END){ return value_type::OP_END; } else { - return next_operatable_type(); + return next_operatable_type(); } } - template()> + template()> class typed_executer { public: static constexpr bool enabled = true; @@ -31,7 +32,7 @@ namespace ink::runtime::internal { else { _typed_exe(t, s, v); } } private: - typed_executer()> _typed_exe; + typed_executer(ty),1>()> _typed_exe; operation _op; }; template @@ -46,17 +47,18 @@ namespace ink::runtime::internal { } }; - template + template constexpr Command next_operatable_command() { + constexpr Command cmd = c + Offset; if constexpr (typed_executer::enabled) { return cmd; } else if constexpr (cmd >= Command::OP_END){ return Command::OP_END; } else { - return next_operatable_command(); + return next_operatable_command(); } } - template()> + template()> class executer_imp { public: template @@ -74,7 +76,7 @@ namespace ink::runtime::internal { } else { _exe(c, s); } } private: - executer_imp()> _exe; + executer_imp()> _exe; typed_executer _typed_exe; }; template<> From 7565a4f91e0cd98a1da8f4935f3ec349fe5e19bf Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 2 Mar 2021 13:17:57 +0100 Subject: [PATCH 22/23] Remove obsolete files --- inkcpp/default_operations.h | 173 ------------------------------------ inkcpp/list_table.h | 57 ------------ inkcpp/marker_operations.h | 8 -- notes/ListNotes.md | 63 ------------- 4 files changed, 301 deletions(-) delete mode 100644 inkcpp/default_operations.h delete mode 100644 inkcpp/list_table.h delete mode 100644 inkcpp/marker_operations.h delete mode 100644 notes/ListNotes.md diff --git a/inkcpp/default_operations.h b/inkcpp/default_operations.h deleted file mode 100644 index 922b9a66..00000000 --- a/inkcpp/default_operations.h +++ /dev/null @@ -1,173 +0,0 @@ -#pragma once - -#include "executioner.h" -#include "operation_bases.h" - -namespace ink::runtime::internal { - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() + vals[1].get() )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() - vals[1].get() )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() / vals[1].get() )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() * vals[1].get() )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() % vals[1].get() )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( - vals[0].get() == vals[1].get() - )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( - vals[0].get() > vals[1].get() - )); - } - }; - - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( - vals[0].get() < vals[1].get() - )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( - vals[0].get() >= vals[1].get() - )); - } - }; - - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( - vals[0].get() <= vals[1].get() - )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( - vals[0].get() != vals[1].get() - )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() && vals[1].get() )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set( vals[0].get() || vals[1].get() )); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(vals[0].get() < vals[1].get() ? vals[0] : vals[1]); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(vals[0].get() > vals[1].get() ? vals[0] : vals[1]); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set(!vals[0].get())); - } - }; - - template = true> - class operation : operation_base { - public: - using operation_base::operation_base; - void operator()(eval_stack& stack, value* vals) { - stack.push(value{}.set(-vals[0].get())); - } - }; -} diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h deleted file mode 100644 index 653b5e80..00000000 --- a/inkcpp/list_table.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "system.h" - -namespace ink::runtime::internal { - using handel_t = uint32_t; - - class list_table; - struct list_element; - class list_flags { - public: - list_flags(const list_table& table, handel_t list); - class iterator { - handel_t operator*() const; - bool operator==(const iterator& rh) const; - iterator& operator++(); - }; - iterator begin() const; - iterator end() const; - private: - handel_t _list; - const list_table& _table; - }; - - class list_table { - public: - list_table(size_t num_lists, size_t* list_lengths); - list_flags get(handel_t list) const; - // get list_id as input and returns list_id of result list - handel_t add(handel_t lh, handel_t rh); - handel_t add(handel_t list, list_element el); - handel_t subtract(handel_t lh, handel_t rh); - handel_t subtract(handel_t list, list_element el); - handel_t inc(handel_t list); - handel_t dec(handel_t list); - handel_t count(handel_t list); - list_element min(handel_t list); - list_element max(handel_t list); - handel_t all(handel_t list); - handel_t invert(handel_t list); - - list_element lrnd(handel_t list); - - // list_id of lh and rh, returns result - bool is_equal(handel_t lh, handel_t rh); - bool less_then(handel_t lh, handel_t rh); - bool greater(handel_t lh, handel_t rh); - bool less_equal(handel_t lh, handel_t rh); - bool greater_equal(handel_t lh, handel_t rh); - - private: - size_t* _list_start; //< maps list id -> first entry - uint32_t* _start_masks; - uint32_t* _end_masks; - uint32_t _entries; - }; -} diff --git a/inkcpp/marker_operations.h b/inkcpp/marker_operations.h deleted file mode 100644 index c70736d4..00000000 --- a/inkcpp/marker_operations.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "value.h" -#include "operations.h" -#include "operation_bases.h" - -namespace ink::runtime::internal { -} diff --git a/notes/ListNotes.md b/notes/ListNotes.md deleted file mode 100644 index ec897e1c..00000000 --- a/notes/ListNotes.md +++ /dev/null @@ -1,63 +0,0 @@ -## general construction: - -+ A list is a bit vector! -+ Further we will call each bit vector which represents a list a _flag_. -+ Each flag is tide to a number, which is equal to the position in the bit vector, which we will call _flag value_ -+ Each flag has a string representation, further be called _key_. -+ Each multi list is associated to zero ore more list types - + when adding a new element the list type of that element get added to associated lists - + when removing the last element of an list type then the type get removed from associated lists - + __except:__ the list of associated types will be empty, than nothing happen - + -> only an empty initialized list has no d list -+ each list key will be an element variable at global scope -> no two keys can have the same name -+ a list_type is a variable defined with `LIST` therefore mentioned in the `declList` section of the InkJson - -## List operators - -+ `+(list lh,list rh)` lh = lh ∪ rh -+ `+(list, element)` add the element to the list -+ `-(list lh, list rh)` remove elements lh = lh / (lh ∩ rh) -+ `-(list lh, list rh)` removes element from list -+ `++(list l)` `l = l << 1` (bits get shifted out) -+ `--(list l)` `l = l >> 1` (bits get shifted out) -+ `LIST_COUNT(list l)` returns the number of flags sets -+ `LIST_MIN(list l)` returns the flag with the lowest numerical representation which is set -+ `LIST_MAX(list l)` return the flag with the highest numerical representation which is set -+ `lrnd(list l)` return a random flag which is set - + for the user it is `LIST_RANDOM`, but in InkJson it is called `lrnd` -+ `LIST_ALL(list l)` returns a list which all elements of associated list of l -+ `LIST_INVERT(list l)` returns a list which each flag is toggeld of all associated lists -+ `<(list lh, list rh)` return true if `LIST_MAX(lh) < LIST_MIN(rh)` -+ `>(list lh, list rh)` returns true if `LIST_MIN(lh) > LIST_MAX(rh)` -+ `==(list lh, list rh)` returns true if the setted flags of both are equal -+ `!=(list lh, list rh)` return true if `lh == rh` returns false -+ `>=(list lh, list rh)` returns true if `LIST_MAX(lh) >= LIST_MAX(rh) && LIST_MIN(lh) >= LIST_MIN(rh)` -+ `<=(list lh, list rh)` returns true if `LIST_MAX(lh) <= LIST_MAX(rh) && LIST_MIN(lh) <= LIST_MIN(rh)` - -## Datatype - -+ Each list has a id(`listId`), this mapping is arbitrary. To allow usage as array index we start with 0 an increased it for each list_type -+ A flag is identified by `flagId`, it's correspond to the flag value. (flagId - min flagId of that list = flag value) - -### list_element - -`uint32_t` with bit[0-15] are the listId and bit[16-31] the flag value. - -### list - -`uint32_t` position in list_table - -### list_table - -Datatype to manage lists (similar to string_table) - -It contains: - -+ a reference to the string_table -+ A array `keys` which maps each flagId to an key, who lives in string_table -+ A array `flag_start` which maps each listId to an offset, which leads to value 0 for that list in the entry bitmap. -+ A array `fids` which maps each listId to the flagId for the value 0 of that list (used to get key from keys). -+ A array of entries, each entry contains: - + a bitmap, where 1 represent that the list is associated to the corresponding list - + a bitmap, where 1 represent that the list contains the flag - + the two bitmaps are WORD align for better memory access From 6495ae9c7bcc442ba872059e6ed58d6102ed389b Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 2 Mar 2021 13:18:13 +0100 Subject: [PATCH 23/23] Add description at top of files - remove redundant value to string conversion - define own enable_if --- inkcpp/casting.h | 12 +++++++ inkcpp/executioner.h | 65 ++++++++++++++++++++++++++++++++++++ inkcpp/numeric_operations.h | 22 +++++++++--- inkcpp/operation_bases.h | 4 +++ inkcpp/operations.h | 4 +++ inkcpp/string_operations.cpp | 59 ++++---------------------------- inkcpp/string_operations.h | 2 ++ inkcpp/string_utils.h | 2 ++ inkcpp/tuple.hpp | 2 ++ inkcpp/value.h | 5 +++ shared/public/system.h | 4 +++ 11 files changed, 124 insertions(+), 57 deletions(-) diff --git a/inkcpp/casting.h b/inkcpp/casting.h index 5299bf82..fb940446 100644 --- a/inkcpp/casting.h +++ b/inkcpp/casting.h @@ -1,5 +1,16 @@ #pragma once +/// Managing casting between value types. +/// The casting is defined by an NxN matrix where N = |value_types|. +/// The entry m,n is the type where we cast to when rh = value_type(m) and +/// lh = value_type(n). +/// for that the matrix is symmetric. +/// `value_type::none` is used to mark an invalid cast +/// +/// The entries are set in the `set_cast` function, which iterates over all +/// value_types combination. For each combination it checks the value of +/// `cast` (with v1 < v2). When not other defined it is none. + #include "value.h" namespace ink::runtime::internal::casting { @@ -31,6 +42,7 @@ namespace ink::runtime::internal::casting { // get entry from cast constexpr size_t n1 = static_cast(t1); constexpr size_t n2 = static_cast(t2); + // set matrix entry if constexpr (n1 < n2) { data[n1*casting_matrix_type::N + n2] = cast::value; } else { diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h index 3a56fc2b..7d3a7fbb 100644 --- a/inkcpp/executioner.h +++ b/inkcpp/executioner.h @@ -1,5 +1,25 @@ #pragma once +/// Defines the executioner class which initialize the different operations +/// and managed the access to them. +/// +/// The executer creates a array of pointer to the arguments passed, and pass +/// them to each operator, so that each operator can grep the needed arguments. +/// Therefore it is required that each argument has a unique type, so that the +/// order won't matter. +/// +/// When call an operation the executioner iterates through all commands and +/// after find an command match. +/// Then pop arguments from the stack as defined in `command_num_args`. +/// After this iterate through the implementations of that command for different +/// type until it found the correct type, than execute the operation. +/// The search is O(n), but the list is only populated with commands which have +/// at least one implementation, also per command only types listed for which +/// the command is implemented. +/// +/// Improvements: The executioner -> typed_executer could be O(1) when using a +/// look up table. + #include "system.h" #include "value.h" #include "stack.h" @@ -9,6 +29,15 @@ namespace ink::runtime::internal { + /** + * @brief iterates through value_types until it found a matching operator. + * Matching means a operator which implements the command for the type. + * @tparam cmd Command to search operation for. + * @tparam t value type to start search + * @tparam Offset t + Offset is real start, used because trouble with mscv + * @return value_type::OP_END, if no "next operation" found + * @return type which is greater t + Offset and implement the command + */ template constexpr value_type next_operatable_type() { constexpr value_type ty = t + Offset; @@ -20,6 +49,10 @@ namespace ink::runtime::internal { return next_operatable_type(); } } + + /** + * @brief Iterates through all existing operations for this Command. + */ template()> class typed_executer { public: @@ -32,9 +65,12 @@ namespace ink::runtime::internal { else { _typed_exe(t, s, v); } } private: + // skip command for not implemented types typed_executer(ty),1>()> _typed_exe; operation _op; }; + + // end of recursion (has no operation attached to it) template class typed_executer { public: @@ -47,6 +83,13 @@ namespace ink::runtime::internal { } }; + /** + * @brief Find next command which is at least for one type implemented. + * @tparam c command to start search + * @tparam Offset offset to start search, used because of trouble with mscv + * @return Command::OP_END if no next operation is found + * @return next command witch at least of implementation. + */ template constexpr Command next_operatable_command() { constexpr Command cmd = c + Offset; @@ -58,6 +101,11 @@ namespace ink::runtime::internal { return next_operatable_command(); } } + + /** + * @brief Iterate through all commands to find correct command. + * Also instantiates all typed_executer and with them the operations. + */ template()> class executer_imp { public: @@ -79,6 +127,8 @@ namespace ink::runtime::internal { executer_imp()> _exe; typed_executer _typed_exe; }; + + /// end of recursion template<> class executer_imp { public: @@ -89,10 +139,25 @@ namespace ink::runtime::internal { } }; + /** + * @brief Class which instantiates all operations and give access to them. + */ class executer { public: + /** + * @brief pass all arguments to operations who need them. + * @attention each type need to be unique for the look up later! + * @tparam Args argument types + * @param args arguments + */ template executer(Args& ... args) : _executer{tuple(&args...)} {} + + /** + * @brief execute command on stack. + * @param cmd command to execute + * @param stack stack to operate on + */ void operator()(Command cmd, eval_stack& stack) { _executer(cmd, stack); } diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index 26456381..7654d617 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -1,29 +1,41 @@ #pragma once +/// Define operation for numeric types. +/// use generalized types numeric and integral to keep redundancy minimal. +/// define a cast to support operations like int + float, bool + uint etc. + namespace ink::runtime::internal { - /// define numeric value types + + /// list of numeric value types + /// produces a SFINAE error if type is not part of list template - using is_numeric_t = typename std::enable_if< + using is_numeric_t = typename enable_if< ty == value_type::int32 || ty == value_type::uint32 || ty == value_type::float32, void>::type; - /// define integral value types + /// list of internal value types + /// produces a SFINAE error if type is not part of list template - using is_integral_t = typename std::enable_if< + using is_integral_t = typename enable_if< ty == value_type::int32 || ty == value_type::uint32, void>::type; namespace casting { /// define valid casts + + /// result of operation with int and float is float. template<> struct cast { static constexpr value_type value = value_type::float32; }; + + /// result of operation with uint and bool is uint template<> struct cast { static constexpr value_type value = value_type::uint32; }; /// defined numeric cast + /// generic numeric_cast only allow casting to its one type template inline typename value::ret::type numeric_cast(const value& v) { if (to == v.type()) { return v.get(); } @@ -38,6 +50,7 @@ namespace ink::runtime::internal { switch(v.type()) { case value_type::uint32: return v.get(); + /// bool value can cast to uint32 case value_type::boolean: return static_cast(v.get()); default: @@ -51,6 +64,7 @@ namespace ink::runtime::internal { switch(v.type()) { case value_type::float32: return v.get(); + // int value can cast to float case value_type::int32: return static_cast(v.get()); default: diff --git a/inkcpp/operation_bases.h b/inkcpp/operation_bases.h index 79fac6c1..64accc7a 100644 --- a/inkcpp/operation_bases.h +++ b/inkcpp/operation_bases.h @@ -1,5 +1,9 @@ #pragma once +/// defines data storage for operations. +/// provide constructor and handle the data member. +/// the data member therefore are protected accessible + #include "system.h" #include "./tuple.hpp" diff --git a/inkcpp/operations.h b/inkcpp/operations.h index 4cef6d24..66de710f 100644 --- a/inkcpp/operations.h +++ b/inkcpp/operations.h @@ -1,5 +1,7 @@ #pragma once +/// Define base constructs to specify by operation headers. + #include "../shared/private/command.h" namespace ink::runtime::internal { @@ -55,6 +57,8 @@ namespace ink::runtime::internal { }; } +// include header here to ensure correct order + #include "operation_bases.h" #include "numeric_operations.h" #include "string_operations.h" diff --git a/inkcpp/string_operations.cpp b/inkcpp/string_operations.cpp index 6805249f..2919dbca 100644 --- a/inkcpp/string_operations.cpp +++ b/inkcpp/string_operations.cpp @@ -1,3 +1,8 @@ +/// implementation for commands on strings +/// string_cast is a class which convert an value to a string. +/// if the value is already a string it dose nothing (just serve the pointer), +/// else it convert the value to a string and store it, in it internal storage. + #include "stack.h" #include "value.h" #include "string_utils.h" @@ -20,58 +25,6 @@ namespace ink::runtime::internal { char _data[512]; //TODO define central }; - /** - * @brief template for string conversion. - * @param buffer to store the string - * @param n length of buffer - * @param v value to convert - * @tparam ty value type of v - */ - template - int cast_to_string(char* buffer, size_t n, const value& v){ - ink_exception("cast not implemented"); - return -1; - } - - /** - * @brief wrapper to call correct cast_to_string function. - */ - template - int to_string(char data[N], const value& v) { - if (v.type() == ty) { - return cast_to_string(data, N, v); - } else if constexpr (ty < value_type::OP_END) { - return to_string(data, v); - } else { - throw ink_exception("cast target not exist!"); - } - } - - // cast for int32 - template<> - int cast_to_string(char* data, size_t size, const value& v) { - return toStr(data, size, v.get()); - } - - // cast for uint32 - template<> - int cast_to_string(char* data, size_t size, const value& v) { - return toStr(data, size, v.get()); - } - - // cast for float32 - template<> - int cast_to_string(char* data, size_t size, const value& v) { - return toStr(data, size, v.get()); - } - - // cast for newline - template<> - int cast_to_string(char* data, size_t size, const value& v) { - return toStr(data, size, "\n"); - } - - // constructor for string_cast class string_cast::string_cast(const value& val) : _val{val}, _str{nullptr} { if (val.type() == value_type::string) { @@ -80,7 +33,7 @@ namespace ink::runtime::internal { } else { // convert else _str = _data; - to_string<512>(_data, val); + toStr(_data, 512, val); } } } diff --git a/inkcpp/string_operations.h b/inkcpp/string_operations.h index 229dca52..44507eac 100644 --- a/inkcpp/string_operations.h +++ b/inkcpp/string_operations.h @@ -1,10 +1,12 @@ #pragma once +/// defines operations allowed on strings. namespace ink::runtime::internal { namespace casting { // define valid castings + // when operate on float and string, the result is a string template<> struct cast { static constexpr value_type value = value_type::string; }; diff --git a/inkcpp/string_utils.h b/inkcpp/string_utils.h index 28b4a592..193c1759 100644 --- a/inkcpp/string_utils.h +++ b/inkcpp/string_utils.h @@ -67,6 +67,8 @@ namespace ink::runtime::internal { return toStr(buffer, size, v.get()); case value_type::float32: return toStr(buffer, size, v.get()); + case value_type::newline: + return toStr(buffer, size, "\n"); default: throw ink_exception("only support toStr for numeric types"); } diff --git a/inkcpp/tuple.hpp b/inkcpp/tuple.hpp index 91d143db..528c8d47 100644 --- a/inkcpp/tuple.hpp +++ b/inkcpp/tuple.hpp @@ -1,5 +1,7 @@ #pragma once +/// very basic flat tuple implementation, only use for trivial data types. + #include "./include/traits.h" namespace ink::runtime::internal { diff --git a/inkcpp/value.h b/inkcpp/value.h index 9408ea87..73651637 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -1,5 +1,10 @@ #pragma once +/// The value class contains the information which type its hold and a small +/// piece of information to access the data. +/// use explicit getter and setter to make access more uniform. +/// define different value_types, and the mapping between type and data. + #include "system.h" #include "../shared/private/command.h" diff --git a/shared/public/system.h b/shared/public/system.h index cd523dfb..6838c4a5 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -122,6 +122,10 @@ namespace ink struct always_false { static constexpr bool value = false; }; + template + struct enable_if {}; + template + struct enable_if { using type = T; }; }