Start working on a c++ interface.
This commit is contained in:
parent
257513e72a
commit
3142e4c4ce
11 changed files with 382 additions and 19 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
tags
|
||||||
|
compile_commands.json
|
|
@ -8,6 +8,9 @@ wren_dep = dependency('wren', version: '>=0.2.0', fallback: ['wren', 'wren_dep']
|
||||||
|
|
||||||
executable(meson.project_name(),
|
executable(meson.project_name(),
|
||||||
'src/main.cpp',
|
'src/main.cpp',
|
||||||
|
'src/wren/vm.cpp',
|
||||||
|
'src/wren/configuration.cpp',
|
||||||
|
'src/wren/def_configuration.cpp',
|
||||||
dependencies: [wren_dep],
|
dependencies: [wren_dep],
|
||||||
install: true,
|
install: true,
|
||||||
)
|
)
|
||||||
|
|
24
src/main.cpp
24
src/main.cpp
|
@ -1,32 +1,18 @@
|
||||||
|
#include "wren/vm.hpp"
|
||||||
|
#include "wren/def_configuration.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <wren.hpp>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
namespace {
|
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
|
} //unnamed namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::cout << "hello world\n";
|
std::cout << "hello world\n";
|
||||||
WrenConfiguration config;
|
wren::DefConfiguration config;
|
||||||
wrenInitConfiguration(&config);
|
wren::VM vm(&config);
|
||||||
config.writeFn = &wren_print;
|
vm.interpret("my_module", "System.print(\"I am running in a VM!\")");
|
||||||
config.errorFn = &wren_error;
|
|
||||||
WrenVM* vm = wrenNewVM(&config);
|
|
||||||
WrenInterpretResult result = wrenInterpret(
|
|
||||||
vm,
|
|
||||||
"my_module",
|
|
||||||
"System.print(\"I am running in a VM!\")"
|
|
||||||
);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||||
wrenFreeVM(vm);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
13
src/wren/configuration.cpp
Normal file
13
src/wren/configuration.cpp
Normal 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
|
25
src/wren/configuration.hpp
Normal file
25
src/wren/configuration.hpp
Normal 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
|
12
src/wren/def_configuration.cpp
Normal file
12
src/wren/def_configuration.cpp
Normal 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
|
14
src/wren/def_configuration.hpp
Normal file
14
src/wren/def_configuration.hpp
Normal 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
14
src/wren/error_type.hpp
Normal 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
53
src/wren/has_method.hpp
Normal 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
116
src/wren/vm.cpp
Normal 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
125
src/wren/vm.hpp
Normal 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
|
Loading…
Reference in a new issue