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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Many, but not all features of the Ink language are supported (see Glaring Omissi
* Visit and read counts (`visits` and `CNT?` commands).
* `seq` command and all sequence types (stopping, cycle, shuffle)
* Global store that can be shared between runners
* External function binding (no fallback support yet)
* External function binding (define a function with same signature and name, which will be used if no function is bindeded)
* Tunnels and internal functions
* Ink threads (probably incredibly unstable though)

Expand Down
6 changes: 6 additions & 0 deletions inkcpp/functional.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ namespace ink::runtime::internal
stack->push(value{}.set<value_type::int32>(v));
}

void function_base::push_void(basic_eval_stack* stack)
{
stack->push(values::null);
}


void function_base::push_string(basic_eval_stack* stack, const char* dynamic_string)
{
stack->push(value{}.set<value_type::string>(dynamic_string, true));
Expand Down
4 changes: 3 additions & 1 deletion inkcpp/include/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace ink::runtime::internal
template<typename T>
static void push(basic_eval_stack* stack, const T& value);

static void push_void(basic_eval_stack* stack);

// string special push
static void push_string(basic_eval_stack* stack, const char* dynamic_string);

Expand Down Expand Up @@ -81,7 +83,7 @@ namespace ink::runtime::internal

// Ink expects us to push something
// TODO -- Should be a special "void" value
push(stack, 0);
push_void(stack);
}
else if constexpr (is_string<typename traits::return_type>::value)
{
Expand Down
26 changes: 13 additions & 13 deletions inkcpp/runner_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,9 +612,6 @@ namespace ink::runtime::internal
// If we're on a newline
if (_output.ends_with(value_type::newline))
{
// TODO: REMOVE
// return true;

// Unless we are out of content, we are going to try
// to continue a little further. This is to check for
// glue (which means there is potentially more content
Expand Down Expand Up @@ -850,7 +847,16 @@ namespace ink::runtime::internal
} else {
target = read<uint32_t>();
}
start_frame<frame_type::function>(target);
if (!(flag & CommandFlag::FALLBACK_FUNCTION)) {
start_frame<frame_type::function>(target);
} else {
inkAssert(!_eval.is_empty(), "fallback function but no function call before?");
if(_eval.top_value().type() == value_type::ex_fn_not_found) {
_eval.pop();
inkAssert(target != 0, "Exetrnal function was not binded, and no fallback function provided!");
start_frame<frame_type::function>(target);
}
}
}
break;
case Command::TUNNEL_RETURN:
Expand Down Expand Up @@ -922,18 +928,11 @@ namespace ink::runtime::internal
// find and execute. will automatically push a valid if applicable
bool success = _functions.call(functionName, &_eval, numArguments, _globals->strings());

// If we failed, we need to at least pretend so our state doesn't get fucked
// If we failed, notify a potential fallback function
if (!success)
{
// pop arguments
for (int i = 0; i < numArguments; i++)
_eval.pop();

// push void
_eval.push(value());
_eval.push(values::ex_fn_not_found);
}

// TODO: Verify something was found?
}
break;

Expand Down Expand Up @@ -1130,6 +1129,7 @@ namespace ink::runtime::internal
inkAssert(false, "Unrecognized command!");
break;
}

}
catch (...)
{
Expand Down
7 changes: 7 additions & 0 deletions inkcpp/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ namespace ink::runtime::internal {
func_start, // start of function marker
func_end, // end of function marker
null, // void value, for function returns
ex_fn_not_found, // value for failed external function calls
tunnel_frame, // return from tunnel
function_frame, // return from function
thread_frame, // return from thread
Expand Down Expand Up @@ -351,6 +352,11 @@ namespace ink::runtime::internal {
return *this;
}
template<>
inline constexpr value& value::set<value_type::ex_fn_not_found>() {
_type = value_type::ex_fn_not_found;
return *this;
}
template<>
inline constexpr value& value::set<value_type::newline>() {
_type = value_type::newline;
return *this;
Expand Down Expand Up @@ -418,6 +424,7 @@ namespace ink::runtime::internal {
namespace values {
static constexpr value marker = value{}.set<value_type::marker>();
static constexpr value glue = value{}.set<value_type::glue>();
static constexpr value ex_fn_not_found = value{}.set<value_type::ex_fn_not_found>();
static constexpr value newline = value{}.set<value_type::newline>();
static constexpr value func_start = value{}.set<value_type::func_start>();
static constexpr value func_end = value{}.set<value_type::func_end>();
Expand Down
41 changes: 34 additions & 7 deletions inkcpp_compiler/binary_emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace ink::compiler::internal
using std::vector;
using std::map;
using std::string;
using std::tuple;

char* strtok_s(char * s, const char * sep, char** context) {
#if defined(_WIN32) || defined(_WIN64)
Expand Down Expand Up @@ -107,8 +106,9 @@ namespace ink::compiler::internal
_current->children.push_back(container);
_current->indexed_children.insert({ index_in_parent, container });

if (!name.empty())
if (!name.empty()) {
_current->named_children.insert({ name, container });
}
}

// Set this as the current pointer
Expand All @@ -127,6 +127,23 @@ namespace ink::compiler::internal
return _containers.pos();
}

int binary_emitter::function_container_arguments(const std::string& name)
{
if(_root == nullptr) { return -1; }
auto fn = _root->named_children.find(name);
if (fn == _root->named_children.end()) { return -1; }

size_t offset = fn->second->offset;
byte_t cmd = _containers.get(offset);
int arity = 0;
while(static_cast<Command>(cmd) == Command::DEFINE_TEMP) {
offset += 6; // command(1) + flag(1) + variable_name_hash(4)
cmd = _containers.get(offset);
++arity;
}
return arity;
}

void binary_emitter::write_raw(Command command, CommandFlag flag, const char* payload, ink::size_t payload_size)
{
_containers.write(command);
Expand All @@ -142,7 +159,8 @@ namespace ink::compiler::internal

// Note the position of this later so we can resolve the paths at the end
size_t param_position = _containers.pos() - sizeof(uint32_t);
_paths.push_back(std::make_tuple(param_position, path, _current, useCountIndex));
bool op = flag & CommandFlag::FALLBACK_FUNCTION;
_paths.push_back(std::make_tuple(param_position, path, op, _current, useCountIndex));
}

void binary_emitter::write_variable(Command command, CommandFlag flag, const std::string& name)
Expand Down Expand Up @@ -274,8 +292,9 @@ namespace ink::compiler::internal
using std::get;
size_t position = get<0>(pair);
const std::string& path = get<1>(pair);
container_data* context = get<2>(pair);
bool useCountIndex = get<3>(pair);
bool optional = get<2>(pair);
container_data* context = get<3>(pair);
bool useCountIndex = get<4>(pair);

// Start at the root
container_data* container = _root;
Expand Down Expand Up @@ -325,7 +344,10 @@ namespace ink::compiler::internal
// Named child
else
{
container = container->named_children[token];
auto itr = container->named_children.find(token);
container = itr == container->named_children.end()
? nullptr
: itr->second;
}

firstParent = false;
Expand All @@ -350,7 +372,12 @@ namespace ink::compiler::internal
else
{
// Otherwise, write container address
_containers.set(position, container->offset);
if (container == nullptr) {
_containers.set(position, 0);
inkAssert(optional, ("Was not able to resolve a not optional path! '" + path + "'").c_str());
} else {
_containers.set(position, container->offset);
}
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion inkcpp_compiler/binary_emitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace ink::compiler::internal
// Begin emitter
virtual uint32_t start_container(int index_in_parent, const std::string& name) override;
virtual uint32_t end_container() override;
virtual int function_container_arguments(const std::string& name) override;
virtual void write_raw(Command command, CommandFlag flag = CommandFlag::NO_FLAGS, const char* payload = nullptr, ink::size_t payload_size = 0) override;
virtual void write_path(Command command, CommandFlag flag, const std::string& path, bool useCountIndex = false) override;
virtual void write_variable(Command command, CommandFlag flag, const std::string& name) override;
Expand Down Expand Up @@ -53,6 +54,11 @@ namespace ink::compiler::internal
binary_stream _lists;
binary_stream _containers;

std::vector<std::tuple<size_t, std::string, container_data*, bool>> _paths;
// positon to write address
// path as string
// if path may not exists (used for function fallbackes)
// container data
// use count index?
std::vector<std::tuple<size_t, std::string, bool, container_data*, bool>> _paths;
};
}
17 changes: 17 additions & 0 deletions inkcpp_compiler/binary_stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,23 @@ namespace ink
memcpy(ptr, data, len);
}

byte_t binary_stream::get(size_t offset) const
{
// Find slab for offset
unsigned int slab_index = offset / DATA_SIZE;
size_t pos = offset % DATA_SIZE;

// Get slab and ptr
byte_t* slab = nullptr;
if (slab_index < _slabs.size())
slab = _slabs[slab_index];
else if (slab_index == _slabs.size())
slab = _currentSlab;

inkAssert(slab != nullptr, "try to access invalid slab in binary stream");
return slab[pos];
}

void binary_stream::reset()
{
// Delete all slabs
Expand Down
3 changes: 3 additions & 0 deletions inkcpp_compiler/binary_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ namespace ink
// reset to 0
void reset();

// read a byte from stream
byte_t get(size_t offset) const;

private:
// Size of a data slab. Whenever
// a slab runs out of data,
Expand Down
6 changes: 6 additions & 0 deletions inkcpp_compiler/emitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ namespace ink::compiler::internal
// ends a container
virtual uint32_t end_container() = 0;

// checks if _root contains a container named name to check
// if name is in valid internal function name
// @return number of arguments functions takes (arity)
// @retval -1 if the function was not found
virtual int function_container_arguments(const std::string& name) = 0;

// Writes a command with an optional payload
virtual void write_raw(Command command, CommandFlag flag = CommandFlag::NO_FLAGS, const char* payload = nullptr, ink::size_t payload_size = 0) = 0;

Expand Down
3 changes: 2 additions & 1 deletion inkcpp_compiler/json_compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,8 @@ 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()),
static_cast<CommandFlag>(numArgs));
}
_emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val);
}

// list initialisation
else if (has(command, "list"))
Expand Down
23 changes: 12 additions & 11 deletions inkcpp_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
add_executable(inkcpp_test catch.hpp Main.cpp
Array.cpp
Pointer.cpp
Stack.cpp
Callstack.cpp
Restorable.cpp
Value.cpp
Globals.cpp
Lists.cpp
Tags.cpp
NewLines.cpp
)
Array.cpp
Pointer.cpp
Stack.cpp
Callstack.cpp
Restorable.cpp
Value.cpp
Globals.cpp
Lists.cpp
Tags.cpp
NewLines.cpp
FallbackFunction.cpp
)

target_link_libraries(inkcpp_test PUBLIC inkcpp inkcpp_compiler inkcpp_shared)
target_include_directories(inkcpp_test PRIVATE ../shared/private/)
Expand Down
63 changes: 63 additions & 0 deletions inkcpp_test/FallbackFunction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "catch.hpp"
#include "../inkcpp_cl/test.h"

#include <system.h>
#include <story.h>
#include <runner.h>
#include <globals.h>
#include <compiler.h>

#include <cmath>

using namespace ink::runtime;

SCENARIO("run a story with external function and fallback function", "[external function]")
{
GIVEN("story with two external functions, one with fallback")
{
inklecate("ink/FallBack.ink", "FallBack.tmp");
ink::compiler::run("FallBack.tmp", "FallBack.bin");
auto ink = story::from_file("FallBack.bin");
runner thread = ink->new_runner();

WHEN("bind both external functions")
{
int cnt_sqrt = 0;
auto fn_sqrt = [&cnt_sqrt](int x)->int{ ++cnt_sqrt; return sqrt(x); };
int cnt_greeting = 0;
auto fn_greeting = [&cnt_greeting]()->const char*{++cnt_greeting; return "Hohooh"; };

thread->bind("sqrt", fn_sqrt);
thread->bind("greeting", fn_greeting);

std::string out;
REQUIRE_NOTHROW(out = thread->getall());
THEN("Both function should be called the correct amount of times")
{
REQUIRE(out == "Hohooh ! A small demonstraion of my power:\n4 * 4 = 16, stunning i would say\n");
REQUIRE(cnt_sqrt == 2);
REQUIRE(cnt_greeting == 1);
}
}
WHEN("only bind function without fallback")
{
int cnt_sqrt = 0;
auto fn_sqrt = [&cnt_sqrt](int x)->int{++cnt_sqrt; return sqrt(x); };

thread ->bind("sqrt", fn_sqrt);

std::string out;
REQUIRE_NOTHROW(out = thread->getall());;
THEN("Sqrt should be falled twice, and uses default greeting")
{
REQUIRE(out == "Hello ! A small demonstraion of my power:\n4 * 4 = 16, stunning i would say\n");
REQUIRE(cnt_sqrt == 2);
}
}
WHEN("bind no function")
{
std::string out;
REQUIRE_THROWS_AS(out = thread->getall(), ink::ink_exception);
}
}
}
1 change: 1 addition & 0 deletions inkcpp_test/NewLines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "../inkcpp_cl/test.h"

#include <story.h>
#include <globals.h>
#include <runner.h>
#include <compiler.h>

Expand Down
Loading