Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
add_subdirectory(unreal)
1 change: 1 addition & 0 deletions inkcpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ list(APPEND SOURCES
system.cpp
value.h value.cpp
string_table.h string_table.cpp avl_array.h
string_operations.cpp
header.cpp
)
source_group(Collections REGULAR_EXPRESSION collections/.*)
Expand Down
87 changes: 87 additions & 0 deletions inkcpp/casting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#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<v1,v2>` (with v1 < v2). When not other defined it is none.

#include "value.h"

namespace ink::runtime::internal::casting {

/**
* @brief casting_matrix data and access wrapper.
*/
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<size_t>(t1)*N+static_cast<size_t>(t2)];
}
static constexpr size_t N = static_cast<size_t>(value_type::OP_END);
value_type _data[N*N];
};

// iterate through each value_type combination and populate the
// casting_matrix
template<value_type t1 = value_type::BEGIN, value_type t2 = value_type::BEGIN>
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) {
// go to next row
set_cast<value_type::BEGIN, t2+1>(data);
} else {
// get entry from cast<t1,t2>
constexpr size_t n1 = static_cast<size_t>(t1);
constexpr size_t n2 = static_cast<size_t>(t2);
// set matrix entry
if constexpr (n1 < n2) {
data[n1*casting_matrix_type::N + n2] = cast<t1,t2>::value;
} else {
data[n1*casting_matrix_type::N + n2] = cast<t2,t1>::value;
}
set_cast<t1+1,t2>(data);
}
}

// 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,value_type> = 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<size_t N>
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;
}
}
}
11 changes: 5 additions & 6 deletions inkcpp/choice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ namespace ink
if (in.queued() == 2)
{
// If it's a string, just grab it. Otherwise, use allocation
const internal::data& data = in.peek();
switch (data.type)
const internal::value& data = in.peek();
switch (data.type())
{
case internal::data_type::string_table_pointer:
case internal::data_type::allocated_string_pointer:
_text = data.string_val;
case internal::value_type::string:
_text = data.get<internal::value_type::string>();
in.discard(2);
break;
default:
Expand All @@ -35,4 +34,4 @@ namespace ink
_thread = thread;
}
}
}
}
168 changes: 168 additions & 0 deletions inkcpp/executioner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#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"
#include "operations.h"



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<Command cmd, value_type t, size_t Offset>
constexpr value_type next_operatable_type() {
constexpr value_type ty = t + Offset;
if constexpr (operation<cmd,ty>::enabled) {
return ty;
} else if constexpr (ty >= value_type::OP_END){
return value_type::OP_END;
} else {
return next_operatable_type<cmd,ty,1>();
}
}

/**
* @brief Iterates through all existing operations for this Command.
*/
template<Command cmd, value_type ty = next_operatable_type<cmd,value_type::BEGIN,0>()>
class typed_executer {
public:
static constexpr bool enabled = true;
template<typename T>
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:
// skip command for not implemented types
typed_executer<cmd, next_operatable_type<cmd,static_cast<value_type>(ty),1>()> _typed_exe;
operation<cmd, ty> _op;
};

// end of recursion (has no operation attached to it)
template<Command cmd>
class typed_executer<cmd, value_type::OP_END> {
public:
static constexpr bool enabled = false;
template<typename T>
typed_executer(const T& t) {}

void operator()(value_type, eval_stack&, value*) {
throw ink_exception("Operation for value not supported!");
}
};

/**
* @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<Command c, size_t Offset>
constexpr Command next_operatable_command() {
constexpr Command cmd = c + Offset;
if constexpr (typed_executer<cmd>::enabled) {
return cmd;
} else if constexpr (cmd >= Command::OP_END){
return Command::OP_END;
} else {
return next_operatable_command<cmd,1>();
}
}

/**
* @brief Iterate through all commands to find correct command.
* Also instantiates all typed_executer and with them the operations.
*/
template<Command cmd = next_operatable_command<Command::OP_BEGIN,0>()>
class executer_imp {
public:
template<typename T>
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 = command_num_args(cmd);
value args[N];
for (int i = command_num_args(cmd)-1; i >= 0 ; --i) {
args[i] = s.pop();
}
value_type ty = casting::common_base<N>(args);
_typed_exe(ty, s, args);
} else { _exe(c, s); }
}
private:
executer_imp<next_operatable_command<cmd,1>()> _exe;
typed_executer<cmd> _typed_exe;
};

/// end of recursion
template<>
class executer_imp<Command::OP_END> {
public:
template<typename T>
executer_imp(const T& t) {}
void operator()(Command, eval_stack&) {
throw ink_exception("requested command was not found!");
}
};

/**
* @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<typename ... Args>
executer(Args& ... args) : _executer{tuple<Args*...>(&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);
}
private:
executer_imp<Command::OP_BEGIN> _executer;
};
}

30 changes: 13 additions & 17 deletions inkcpp/functional.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@

namespace ink::runtime::internal
{
template<typename T>
T function_base::pop(basic_eval_stack* stack)
template<>
int32_t function_base::pop<int32_t>(basic_eval_stack* stack)
{
return stack->pop().get<T>();
value val = stack->pop();
inkAssert(val.type() == value_type::int32, "Type missmatch!");
return val.get<value_type::int32>();
}

template<typename T>
void function_base::push(basic_eval_stack* stack, const T& value)
template<>
void function_base::push<int32_t>(basic_eval_stack* stack, const int32_t& v)
{
stack->push(value);
stack->push(value{}.set<value_type::int32>(v));
}

void function_base::push_string(basic_eval_stack* stack, const char* dynamic_string)
{
stack->push(value(dynamic_string, true));
stack->push(value{}.set<value_type::string>(dynamic_string, true));
}

char* function_base::allocate(string_table& strings, size_t len)
Expand All @@ -34,17 +36,11 @@ namespace ink::runtime::internal

// Generate template implementations for all significant types

#define SUPPORT_TYPE(TYPE) template TYPE function_base::pop<TYPE>(basic_eval_stack*); template void function_base::push<TYPE>(basic_eval_stack*, const TYPE&)
#define SUPPORT_TYPE_PARAMETER_ONLY(TYPE) template TYPE function_base::pop<TYPE>(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<std::string>(basic_eval_stack* stack) {
return std::string(pop<const char*>(stack));
}
#endif
#ifdef INK_ENABLE_UNREAL
SUPPORT_TYPE_PARAMETER_ONLY(FString);
Expand Down
Loading