Start working on a c++ interface.

This commit is contained in:
King_DuckZ 2020-04-25 20:45:59 +02:00
parent 257513e72a
commit 3142e4c4ce
11 changed files with 382 additions and 19 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
tags
compile_commands.json

View file

@ -8,6 +8,9 @@ wren_dep = dependency('wren', version: '>=0.2.0', fallback: ['wren', 'wren_dep']
executable(meson.project_name(),
'src/main.cpp',
'src/wren/vm.cpp',
'src/wren/configuration.cpp',
'src/wren/def_configuration.cpp',
dependencies: [wren_dep],
install: true,
)

View file

@ -1,32 +1,18 @@
#include "wren/vm.hpp"
#include "wren/def_configuration.hpp"
#include <iostream>
#include <wren.hpp>
#include <chrono>
#include <thread>
namespace {
void wren_print (WrenVM*, const char* text) {
std::cout << text;
}
void wren_error (WrenVM*, WrenErrorType type, const char* module, int line, const char* message) {
std::cerr << "Wren error: " << message << " in " << module << ':' << line << '\n';
}
} //unnamed namespace
int main() {
std::cout << "hello world\n";
WrenConfiguration config;
wrenInitConfiguration(&config);
config.writeFn = &wren_print;
config.errorFn = &wren_error;
WrenVM* vm = wrenNewVM(&config);
WrenInterpretResult result = wrenInterpret(
vm,
"my_module",
"System.print(\"I am running in a VM!\")"
);
wren::DefConfiguration config;
wren::VM vm(&config);
vm.interpret("my_module", "System.print(\"I am running in a VM!\")");
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
wrenFreeVM(vm);
return 0;
}

View file

@ -0,0 +1,13 @@
#include "configuration.hpp"
#include <wren.hpp>
namespace wren {
Configuration::Configuration() {
WrenConfiguration conf;
wrenInitConfiguration(&conf);
m_initial_heap_size = conf.initialHeapSize;
m_min_heap_size = conf.minHeapSize;
m_heap_growth_percent = conf.heapGrowthPercent;
}
} //namespace wren

View file

@ -0,0 +1,25 @@
#pragma once
#include <cstddef>
namespace wren {
class Configuration {
public:
Configuration();
~Configuration() noexcept = default;
std::size_t initial_heap_size() const { return m_initial_heap_size; }
void set_initial_heap_size(std::size_t v) { m_initial_heap_size = v; }
std::size_t min_heap_size() const { return m_min_heap_size; }
void set_min_heap_size(std::size_t v) { m_min_heap_size = v; }
std::size_t heap_growth_percent() const { return m_heap_growth_percent; }
void set_heap_growth_percent(std::size_t v) { m_heap_growth_percent = v; }
private:
std::size_t m_initial_heap_size;
std::size_t m_min_heap_size;
std::size_t m_heap_growth_percent;
};
} //namespace wren

View file

@ -0,0 +1,12 @@
#include "def_configuration.hpp"
#include <iostream>
namespace wren {
void DefConfiguration::write_fn (VM*, const char* text) {
std::cout << text;
}
void DefConfiguration::error_fn (VM*, ErrorType type, const char* module, int line, const char* message) {
std::cerr << "Wren error: " << message << " in " << module << ':' << line << '\n';
}
} //namespace wren

View file

@ -0,0 +1,14 @@
#pragma once
#include "configuration.hpp"
#include "error_type.hpp"
namespace wren {
class VM;
class DefConfiguration : public Configuration {
public:
static void write_fn (VM*, const char* text);
static void error_fn (VM*, ErrorType, const char* module, int line, const char* msg);
};
} //namespace wren

14
src/wren/error_type.hpp Normal file
View file

@ -0,0 +1,14 @@
#pragma once
namespace wren {
enum class ErrorType {
// A syntax or resolution error detected at compile time.
Compile,
// The error message for a runtime error.
Runtime,
// One entry of a runtime error's stack trace.
StackTrace
};
} //namespace wren

53
src/wren/has_method.hpp Normal file
View file

@ -0,0 +1,53 @@
#pragma once
#include <type_traits>
//see https://stackoverflow.com/a/10707822
#define define_method_info(method_name, pretty_name, ret_type, ...) \
class method_ ## pretty_name { \
typedef std::integral_constant<int, 0> no_method_t; \
typedef std::integral_constant<int, 1> has_method_t; \
typedef std::integral_constant<int, 2> has_static_method_t; \
template<typename A, typename... Args> \
static has_method_t test(ret_type (A::*)(Args...)) { \
return {}; \
} \
template <typename A, typename... Args> \
static has_static_method_t test(ret_type (*)(Args...)) { \
return {}; \
} \
template <typename A, typename... Args> \
static auto test(decltype(std::declval<A>().method_name(std::declval<Args>()...))*,void *) { \
typedef decltype(test<A, Args...>(&A::method_name)) return_type; \
return return_type{}; \
} \
template<typename A, typename... Args> \
static no_method_t test(...) { \
return {}; \
} \
public: \
template< typename T> \
class exists { \
typedef decltype(test<T, __VA_ARGS__>(0,0)) found_t; \
public: \
static const constexpr bool value = (found_t::value != no_method_t::value); \
}; \
template <typename T> \
class is_static { \
typedef decltype(test<T, __VA_ARGS__>(0,0)) found_t; \
public: \
static const constexpr bool value = (found_t::value == has_static_method_t::value); \
}; \
template <typename T> struct static_ptr { \
typedef ret_type (*type)(__VA_ARGS__); \
}; \
template <typename T> struct nonstatic_ptr { \
typedef ret_type (T::*type)(__VA_ARGS__); \
}; \
template <typename T> struct ptr { \
typedef typename std::conditional<is_static<T>::value, typename static_ptr<T>::type, typename std::conditional<exists<T>::value, typename nonstatic_ptr<T>::type, void>::type>::type type; \
}; \
}
namespace wren {
} //namespace wren

116
src/wren/vm.cpp Normal file
View file

@ -0,0 +1,116 @@
#include "vm.hpp"
#include "configuration.hpp"
#include <wren.hpp>
#include <cassert>
#include <stdexcept>
namespace wren {
namespace {
[[gnu::const]]
ErrorType to_error_type (WrenErrorType wet) {
switch (wet) {
case WREN_ERROR_COMPILE: return ErrorType::Compile;
case WREN_ERROR_RUNTIME: return ErrorType::Runtime;
case WREN_ERROR_STACK_TRACE: return ErrorType::StackTrace;
default: return ErrorType::Runtime;
};
}
void write_fn (WrenVM* wvm, const char* text) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->write_fn and cb->config_obj and cb->vm);
cb->write_fn(*cb->config_obj, cb->vm, text);
}
void error_fn (WrenVM* wvm, WrenErrorType type, const char* module, int line, const char* message) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->error_fn and cb->config_obj and cb->vm);
cb->error_fn(*cb->config_obj, cb->vm, to_error_type(type), module, line, message);
}
const char* resolve_module_fn (WrenVM* wvm, const char* importer, const char* name) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->resolve_module_fn and cb->config_obj and cb->vm);
return cb->resolve_module_fn(*cb->config_obj, cb->vm, importer, name);
}
char* load_module_fn (WrenVM* wvm, const char* name) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->load_module_fn and cb->config_obj and cb->vm);
return cb->load_module_fn(*cb->config_obj, cb->vm, name);
}
} //unnamed namespace
VM::VM (Configuration* conf, const detail::Callbacks& cb) :
m_callbacks(cb),
m_vm(nullptr)
{
WrenConfiguration wconf;
wrenInitConfiguration(&wconf);
wconf.userData = static_cast<void*>(&m_callbacks);
wconf.initialHeapSize = conf->initial_heap_size();
wconf.minHeapSize = conf->min_heap_size();
wconf.heapGrowthPercent = conf->heap_growth_percent();
if (cb.write_fn)
wconf.writeFn = &write_fn;
if (cb.error_fn)
wconf.errorFn = &error_fn;
if (cb.reallocate_fn)
wconf.reallocateFn = cb.reallocate_fn;
if (cb.resolve_module_fn)
wconf.resolveModuleFn = &resolve_module_fn;
if (cb.load_module_fn)
wconf.loadModuleFn = &load_module_fn;
m_vm = wrenNewVM(&wconf);
if (not m_vm)
throw std::runtime_error("Failed to initialize Wren VM");
}
VM::VM (VM&& other) :
m_vm(nullptr)
{
std::swap(m_vm, other.m_vm);
m_callbacks = other.m_callbacks;
wrenSetUserData(m_vm, &m_callbacks);
}
VM::~VM() noexcept {
if (m_vm)
wrenFreeVM(m_vm);
#if !defined(NDEBUG)
m_vm = nullptr;
#endif
}
void VM::interpret (const char* module_name, const char* script) {
using std::string;
using std::runtime_error;
const WrenInterpretResult res = wrenInterpret(m_vm, module_name, script);
switch (res) {
case WREN_RESULT_COMPILE_ERROR:
throw runtime_error(string("Compilation of ") + module_name + " has failed");
case WREN_RESULT_RUNTIME_ERROR:
throw runtime_error(string("A runtime error occurred in ") + module_name);
case WREN_RESULT_SUCCESS:
break;
}
}
void VM::interpret (const std::string& module_name, const std::string& script) {
return interpret(module_name.c_str(), script.c_str());
}
} //namespace wren

125
src/wren/vm.hpp Normal file
View file

@ -0,0 +1,125 @@
#pragma once
#include "has_method.hpp"
#include "error_type.hpp"
#include <string>
extern "C" {
typedef struct WrenVM WrenVM;
} //extern C
namespace wren {
class Configuration;
class VM;
namespace detail {
struct Callbacks {
void (*write_fn)(Configuration&, VM*, const char*);
void (*error_fn)(Configuration&, VM*, ErrorType, const char*, int, const char*);
void* (*reallocate_fn)(void*, std::size_t);
const char* (*resolve_module_fn)(Configuration&, VM*, const char*, const char*);
char* (*load_module_fn)(Configuration&, VM*, const char*);
Configuration* config_obj;
VM* vm;
};
} //namespace detail
class VM {
public:
template <typename T>
VM (T* conf);
VM (const VM&) = delete;
VM (VM&& other);
~VM() noexcept;
void interpret (const char* module_name, const char* script);
void interpret (const std::string& module_name, const std::string& script);
private:
VM (Configuration* conf, const detail::Callbacks&);
template <typename T>
detail::Callbacks to_callbacks (T& conf);
detail::Callbacks m_callbacks;
WrenVM* m_vm;
};
namespace detail {
define_method_info(write_fn, write, void, VM*, const char*);
define_method_info(error_fn, error, void, VM*, ErrorType, const char*, int, const char*);
define_method_info(reallocate_fn, reallocate, void*, void*, std::size_t);
define_method_info(resolve_module_fn, resolve_module, const char*, const char*, const char*);
define_method_info(load_module_fn, load_module, char*, VM*, const char*);
namespace fwd {
template <typename T>
inline void write_fn (Configuration& obj, VM* vm, const char* text) {
reinterpret_cast<T&>(obj).write_fn(vm, text);
}
template <typename T>
inline void error_fn (Configuration& obj, VM* vm, ErrorType et, const char* module, int line, const char* message) {
reinterpret_cast<T&>(obj).error_fn(vm, et, module, line, message);
}
template <typename T>
inline const char* resolve_module_fn (Configuration& obj, VM* vm, void* memory, std::size_t new_size) {
return reinterpret_cast<T&>(obj).resolve_module_fn(vm, memory, new_size);
}
template <typename T>
inline char* load_module_fn (Configuration& obj, VM* vm, const char* name) {
return reinterpret_cast<T&>(obj).load_module_fn(vm, name);
}
} //namespace fwd
} //namespace detail
template <typename T>
inline detail::Callbacks VM::to_callbacks (T& conf) {
using detail::method_write;
using detail::method_error;
using detail::method_reallocate;
using detail::method_resolve_module;
using detail::method_load_module;
detail::Callbacks ret;
ret.config_obj = &conf;
ret.vm = this;
if constexpr (method_write::exists<T>::value)
ret.write_fn = &detail::fwd::write_fn<T>;
else
ret.write_fn = nullptr;
if constexpr (method_error::exists<T>::value)
ret.error_fn = &detail::fwd::error_fn<T>;
else
ret.error_fn = nullptr;
if constexpr (method_reallocate::exists<T>::value and method_reallocate::is_static<T>::value)
ret.reallocate_fn = &T::reallocate_fn;
else
ret.reallocate_fn = nullptr;
static_assert(not method_reallocate::exists<T>::value or method_reallocate::is_static<T>::value, "Realloc function must be a static function");
if constexpr (method_resolve_module::exists<T>::value)
ret.resolve_module_fn = &detail::fwd::resolve_module_fn<T>;
else
ret.resolve_module_fn = nullptr;
if constexpr (method_load_module::exists<T>::value)
ret.load_module_fn = &detail::fwd::load_module_fn<T>;
else
ret.load_module_fn = nullptr;
return ret;
}
template <typename T>
inline VM::VM (T* conf) :
VM(static_cast<Configuration*>(conf), to_callbacks(*conf))
{
}
} //namespace wren