183 lines
4.2 KiB
C++
183 lines
4.2 KiB
C++
export module kym.environment;
|
|
|
|
import core;
|
|
import core.sequence;
|
|
|
|
import kym;
|
|
|
|
using loggable = core::callable<void(core::slice<char const> const &)>;
|
|
|
|
export namespace kym {
|
|
struct vm;
|
|
}
|
|
|
|
enum class token_kind {
|
|
end,
|
|
};
|
|
|
|
struct token {
|
|
core::slice<char const> text;
|
|
|
|
token_kind kind;
|
|
};
|
|
|
|
struct tokenizer {
|
|
core::slice<char const> source;
|
|
|
|
tokenizer(core::slice<char const> const & source) : source{source} {}
|
|
|
|
token next() {
|
|
core::usize cursor = 0;
|
|
|
|
while (cursor < source.length) {
|
|
|
|
}
|
|
|
|
return token{
|
|
.kind = token_kind::end,
|
|
};
|
|
}
|
|
};
|
|
|
|
struct bytecode {
|
|
bytecode(core::allocator * allocator) : error_message_buffer{allocator} {
|
|
|
|
}
|
|
|
|
bool compile(tokenizer bytecode_tokenizer, loggable const & log_error) {
|
|
for (;;) {
|
|
token const initial_token = bytecode_tokenizer.next();
|
|
|
|
switch (initial_token.kind) {
|
|
case token_kind::end: return true;
|
|
|
|
default: core::unreachable();
|
|
}
|
|
}
|
|
}
|
|
|
|
kym::value execute(kym::vm & vm, core::slice<kym::value const> const & arguments) {
|
|
return kym::nil;
|
|
}
|
|
|
|
private:
|
|
core::stack<char> error_message_buffer;
|
|
};
|
|
|
|
export namespace kym {
|
|
value default_call(vm & owning_vm, void * userdata, core::slice<value const> const & arguments) {
|
|
return nil;
|
|
}
|
|
|
|
core::expected<core::usize, core::io_error> default_stringify(vm & owning_vm, void * userdata, core::writable const & output) {
|
|
return output(core::slice("[object]").as_bytes());
|
|
}
|
|
|
|
struct bound_object {
|
|
void * userdata;
|
|
|
|
struct {
|
|
void(*cleanup)(vm &, void *);
|
|
|
|
value(*call)(vm &, void *, core::slice<value const> const &);
|
|
|
|
core::expected<core::usize, core::io_error>(*stringify)(vm &, void *, core::writable const &);
|
|
} behavior;
|
|
|
|
bound_object(vm * owning_vm) : userdata{nullptr}, owning_vm{owning_vm}, behavior{
|
|
.cleanup = [](vm & owning_vm, void * userdata) {},
|
|
.call = default_call,
|
|
.stringify = default_stringify,
|
|
} {}
|
|
|
|
void cleanup() {
|
|
this->behavior.cleanup(*this->owning_vm, this->userdata);
|
|
}
|
|
|
|
value call(core::slice<value const> const & arguments) {
|
|
return this->behavior.call(*this->owning_vm, this->userdata, arguments);
|
|
}
|
|
|
|
core::expected<core::usize, core::io_error> stringify(core::writable const & output) {
|
|
return this->behavior.stringify(*this->owning_vm, this->userdata, output);
|
|
}
|
|
|
|
value get_field(core::slice<char const> const & field_name) {
|
|
return nil;
|
|
}
|
|
|
|
private:
|
|
vm * owning_vm;
|
|
};
|
|
|
|
struct vm {
|
|
struct init_options {
|
|
core::u16 datastack_size;
|
|
|
|
core::u16 callstack_size;
|
|
};
|
|
|
|
vm(core::allocator * allocator, auto log) : allocator{allocator}, log{log}, data_stack{} {}
|
|
|
|
~vm() {
|
|
if (this->data_stack.pointer != nullptr)
|
|
this->allocator->deallocate(this->data_stack.pointer);
|
|
}
|
|
|
|
bool init(init_options const & options) {
|
|
core::u8 * const data_stack_buffer = this->allocator->reallocate(reinterpret_cast
|
|
<core::u8 *>(this->data_stack.pointer), options.datastack_size * sizeof(value));
|
|
|
|
if (data_stack_buffer == nullptr) return false;
|
|
|
|
this->data_stack = {
|
|
reinterpret_cast<value * >(data_stack_buffer), options.datastack_size};
|
|
|
|
return true;
|
|
}
|
|
|
|
value compile(core::slice<char const> const & source) {
|
|
bytecode * source_bytecode = new (*this->allocator) bytecode{allocator};
|
|
|
|
if (source_bytecode == nullptr) return nil;
|
|
|
|
if (!source_bytecode->compile(tokenizer{source}, [&](core::slice<char const> error_message) {
|
|
this->log(error_message);
|
|
})) {
|
|
this->allocator->deallocate(source_bytecode);
|
|
|
|
return nil;
|
|
}
|
|
|
|
return this->new_object([this, source_bytecode](bound_object & object) {
|
|
object.userdata = source_bytecode;
|
|
|
|
object.behavior.cleanup = [](vm & owning_vm, void * userdata) {
|
|
owning_vm.allocator->deallocate(userdata);
|
|
};
|
|
|
|
object.behavior.call = [](vm & owning_vm, void * userdata, core::slice<value const> const & arguments) -> value {
|
|
return reinterpret_cast<bytecode *>(userdata)->execute(owning_vm, arguments);
|
|
};
|
|
|
|
this->allocator->deallocate(source_bytecode);
|
|
});
|
|
}
|
|
|
|
value new_object(core::callable<void(bound_object &)> const & then) {
|
|
return nil;
|
|
}
|
|
|
|
void with_object(value object_value, core::callable<void(bound_object &)> const & then) {
|
|
|
|
}
|
|
|
|
private:
|
|
core::slice<value> data_stack;
|
|
|
|
loggable log;
|
|
|
|
core::allocator * allocator;
|
|
};
|
|
}
|