diff --git a/source/core.cpp b/source/core.cpp index 429717e..af90811 100644 --- a/source/core.cpp +++ b/source/core.cpp @@ -70,6 +70,22 @@ export namespace core { __builtin_unreachable(); } + template constexpr scalar max(scalar const & a, scalar const & b) { + return (a > b) ? a : b; + } + + template constexpr scalar min(scalar const & a, scalar const & b) { + return (a < b) ? a : b; + } + + template constexpr scalar clamp(scalar const & value, scalar const & min_value, scalar const & max_value) { + return max(min_value, min(max_value, value)); + } + + f32 round32(f32 value) { + return __builtin_roundf(value); + } + template struct slice { usize length; @@ -167,6 +183,50 @@ export namespace core { u8 buffer[sizeof(element) + 1]; }; + template struct [[nodiscard]] expected { + expected(value_element const & value) : buffer{0} { + (*reinterpret_cast(this->buffer)) = value; + this->buffer[buffer_size] = 1; + } + + expected(error_element const & error) : buffer{0} { + (*reinterpret_cast(this->buffer)) = error; + } + + bool is_ok() const { + return this->buffer[buffer_size]; + } + + value_element & value() { + if (!this->is_ok()) unreachable(); + + return *reinterpret_cast(this->buffer); + } + + value_element const & value() const { + if (!this->is_ok()) unreachable(); + + return *reinterpret_cast(this->buffer); + } + + error_element & error() { + if (this->is_ok()) unreachable(); + + return *reinterpret_cast(this->buffer); + } + + error_element const & error() const { + if (this->is_ok()) unreachable(); + + return *reinterpret_cast(this->buffer); + } + + private: + static constexpr usize buffer_size = max(sizeof(value_element), sizeof(error_element)); + + u8 buffer[buffer_size + 1]; + }; + template struct callable; template struct callable { @@ -218,9 +278,13 @@ export namespace core { return a; } - using readable = callable(slice const &)>; + enum class io_error { + unavailable, + }; - using writable = callable(slice const &)>; + using readable = callable(slice const &)>; + + using writable = callable(slice const &)>; template bool equals(slice const & a, slice const & b) { if (a.length != b.length) return false; @@ -230,43 +294,27 @@ export namespace core { return true; } - optional stream(writable const & output, readable const & input, slice const & buffer) { + expected stream(writable const & output, readable const & input, slice const & buffer) { usize written = 0; - optional maybe_read = input(buffer); + expected maybe_read = input(buffer); - if (!maybe_read.has_value()) return {}; + if (!maybe_read.is_ok()) return maybe_read.error(); usize read = maybe_read.value(); while (read != 0) { - optional const maybe_written = output(buffer.until(read)); + expected const maybe_written = output(buffer.until(read)); - if (!maybe_written.has_value()) return {}; + if (!maybe_written.is_ok()) return maybe_read.error(); written += maybe_written.value(); maybe_read = input(buffer); - if (!maybe_read.has_value()) return {}; + if (!maybe_read.is_ok()) return maybe_read.error(); read = maybe_read.value(); } return written; } - - template scalar max(scalar const & a, scalar const & b) { - return (a > b) ? a : b; - } - - template scalar min(scalar const & a, scalar const & b) { - return (a < b) ? a : b; - } - - template scalar clamp(scalar const & value, scalar const & min_value, scalar const & max_value) { - return max(min_value, min(max_value, value)); - } - - f32 round32(f32 value) { - return __builtin_roundf(value); - } } diff --git a/source/core/sequence.cpp b/source/core/sequence.cpp index bcc0868..79d0bf4 100644 --- a/source/core/sequence.cpp +++ b/source/core/sequence.cpp @@ -107,10 +107,10 @@ export namespace core { }; struct sequence_writer : public writable { - sequence_writer(sequence * target_sequence) : writable{[target_sequence](slice const & buffer) -> optional { + sequence_writer(sequence * target_sequence) : writable{[target_sequence](slice const & buffer) -> expected { optional const maybe_error = target_sequence->append(buffer); - if (maybe_error.has_value()) return {}; + if (maybe_error.has_value()) return io_error::unavailable; return buffer.length; }} {} diff --git a/source/kym/environment.cpp b/source/kym/environment.cpp index dfd3b7f..87b26a6 100644 --- a/source/kym/environment.cpp +++ b/source/kym/environment.cpp @@ -19,16 +19,32 @@ struct token { token_kind kind; }; -using tokenizable = core::callable; +struct tokenizer { + core::slice source; + + tokenizer(core::slice 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(tokenizable const & bytecode_tokenizable) { + bool compile(tokenizer bytecode_tokenizer) { for (;;) { - token const initial_token = bytecode_tokenizable(); + token const initial_token = bytecode_tokenizer.next(); switch (initial_token.kind) { case token_kind::end: return true; @@ -38,7 +54,7 @@ struct bytecode { } } - kym::value execute(kym::vm & vm, core::slice const & arguments) { + kym::value execute(kym::vm & vm, core::slice const & arguments) { return kym::nil; } @@ -51,22 +67,49 @@ struct bytecode { }; export namespace kym { - struct bound_object { - core::callable cleanup; + value default_call(vm & owning_vm, void * userdata, core::slice const & arguments) { + return nil; + } - core::callable)> call; + core::expected default_stringify(vm & owning_vm, void * userdata, core::writable const & writable) { + return writable(core::slice("[object]").as_bytes()); + } + + struct bound_object { + void * userdata; + + struct { + void(*cleanup)(vm &, void *); + + value(*call)(vm &, void *, core::slice const &); + + core::expected(*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 const & arguments) { + return this->behavior.call(*this->owning_vm, this->userdata, arguments); + } + + core::expected stringify(core::writable const & writable) { + return this->behavior.stringify(*this->owning_vm, this->userdata, writable); + } value get_field(core::slice const & field_name) { return nil; } - bool is_string() { - return false; - } - - core::usize stringify(core::writable const & writable) { - return 0; - } + private: + vm * owning_vm; }; struct vm { @@ -100,13 +143,7 @@ export namespace kym { if (source_bytecode == nullptr) return nil; - core::usize cursor = 0; - - if (source_bytecode->compile([]() { - return token{ - - }; - })) { + if (source_bytecode->compile(tokenizer{source})) { this->log(source_bytecode->error_message()); this->allocator->deallocate(source_bytecode); @@ -114,12 +151,14 @@ export namespace kym { } return this->new_object([this, source_bytecode](bound_object & object) { - object.cleanup = [this, source_bytecode]() { - this->allocator->deallocate(source_bytecode); + object.userdata = source_bytecode; + + object.behavior.cleanup = [](vm & owning_vm, void * userdata) { + owning_vm.allocator->deallocate(userdata); }; - object.call = [this, source_bytecode](core::slice const & arguments) { - return source_bytecode->execute(*this, arguments); + object.behavior.call = [](vm & owning_vm, void * userdata, core::slice const & arguments) -> value { + return reinterpret_cast(userdata)->execute(owning_vm, arguments); }; this->allocator->deallocate(source_bytecode); diff --git a/source/runtime.cpp b/source/runtime.cpp index a183d25..38de729 100644 --- a/source/runtime.cpp +++ b/source/runtime.cpp @@ -33,29 +33,31 @@ extern "C" int main(int argc, char const * const * argv) { core::u8 config_source_stream_buffer[1024] = {}; if (!core::stream(core::sequence_writer{&config_source}, - config_readable, config_source_stream_buffer).has_value()) return; + config_readable, config_source_stream_buffer).is_ok()) return; vm.with_object(vm.compile(config_source.as_slice().as_chars()), [&](kym::bound_object & config_script) { vm.with_object(config_script.call({}), [&](kym::bound_object & config) { core::u16 const width = config.get_field("width").as_u16().value_or(0); - if (width == 0) return system.log( - app::log_level::error, "failed to decode `width` property of config"); + if (width == 0) return system.log(app::log_level::error, + "failed to decode `width` property of config"); core::u16 const height = config.get_field("height").as_u16().value_or(0); - if (height == 0) return system.log( - app::log_level::error, "failed to decode `height` property of config"); + if (height == 0) return system.log(app::log_level::error, + "failed to decode `height` property of config"); graphics.show(width, height); vm.with_object(config.get_field("title"), [&](kym::bound_object & title) { core::stack title_buffer{&system.thread_safe_allocator()}; - if (!title.is_string()) return system.log( - app::log_level::error, "failed to decode `title` property of config"); + if (!title.stringify(core::sequence_writer(&title_buffer)).is_ok()) { + system.log(app::log_level::error, + "failed to decode `title` property of config"); - title.stringify(core::sequence_writer(&title_buffer)); + return; + } is_config_loaded = true; });