diff --git a/CMakeLists.txt b/CMakeLists.txt index 242958f..6bb67df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources(vm_lib src/fio.cpp src/stdlib.cpp src/lineedit.cpp + src/serialize.cpp PUBLIC FILE_SET HEADERS @@ -43,6 +44,7 @@ target_sources(vm_lib src/fio.hpp src/stdlib.hpp src/lineedit.hpp + src/serialize.hpp ) add_executable(valeri src/valeri.cpp) @@ -67,6 +69,8 @@ set(LISP_TESTS dict function collections + continuations + serialize ) diff --git a/src/arena.cpp b/src/arena.cpp index ab0853c..f027c09 100644 --- a/src/arena.cpp +++ b/src/arena.cpp @@ -265,6 +265,7 @@ Result Arena::gc_stack(PodStackFrame* obj) { nobj->header.tag = Tag::StackFrame; nobj->parent = TRY(gc_pod(obj->parent.get())); nobj->fun = TRY(gc_pod(obj->fun.get())); + nobj->guard = obj->guard; nobj->pc = obj->pc; nobj->size = obj->size; for (uint64_t i = 0; i < obj->size; i++) { diff --git a/src/arena.hpp b/src/arena.hpp index fe012f3..f77d38e 100644 --- a/src/arena.hpp +++ b/src/arena.hpp @@ -16,6 +16,10 @@ class GcRootList; Arena& get_arena(); void set_arena(Arena& arena); +static uint64_t round_up(uint64_t number, uint64_t to) { + return ((number + to - 1) / to) * to; +} + class GcRootBase { public: friend class Arena; @@ -131,11 +135,29 @@ class Arena { uint64_t objsize = sizeof(T) + extra; auto ptr = _first->alloc(objsize); - if (ptr.has_value()) return (T*)ptr.value(); + if (ptr.has_value()) { + new (ptr.value()) T(); + return (T*)ptr.value(); + } - // TODO: trigger GC + TRY(gc()); - return (T*)TRY(_first->alloc(objsize)); + T* res = (T*)TRY(_first->alloc(objsize)); + new (res) T(); + return res; + } + + template + Result alloc_bytes(uint64_t size = 0) { + auto ptr = _first->alloc(size); + + if (ptr.has_value()) { + return (T*)ptr.value(); + } + + TRY(gc()); + + return (T*)TRY(_first->alloc(size)); } Result gc(); @@ -205,6 +227,11 @@ Result arena_alloc(uint64_t extra = 0) { return get_arena().alloc(extra); } +template +Result arena_alloc_bytes(uint64_t size = 0) { + return get_arena().alloc_bytes(size); +} + Result arena_gc(); template diff --git a/src/common.cpp b/src/common.cpp index 15c5425..b40fedf 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -61,7 +61,6 @@ Result Symbol::create(String& rhs) { uint64_t res_size = rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(char32_t))); - pod->header.tag = Tag::Symbol; pod->size = res_size; memcpy(pod->data, rhs._value->data, sizeof(char32_t) * rhs_size); @@ -73,7 +72,6 @@ Result String::create(const Symbol& rhs) { uint64_t res_size = rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(char32_t))); - pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, rhs._value->data, sizeof(char32_t) * rhs_size); @@ -87,7 +85,6 @@ Result ByteArray::create(const String& str) { size += utf8_codepoint_size(TRY(str[i])); } auto pod = TRY(arena_alloc(size * sizeof(char))); - pod->header.tag = Tag::ByteArray; pod->size = size; char* res = pod->data; @@ -100,9 +97,106 @@ Result ByteArray::create(const String& str) { return ByteArray(TRY(MkGcRoot(pod))); } +Result ByteArray::hex() const { + auto size = _value->size * 2; + auto pod = TRY(arena_alloc(size * sizeof(char32_t))); + pod->size = size; + + const char* hexa = "0123456789ABCDEF"; + for (uint64_t i = 0; i < _value->size; i++) { + auto b = (uint8_t)_value->data[i]; + pod->data[i * 2] = hexa[(b >> 4) & 0xF]; + pod->data[i * 2 + 1] = hexa[b & 0xF]; + } + + return String(TRY(MkGcRoot(pod))); +} +Result ByteArray::to_rle() const { + auto size = _value->size; + + auto compressed_size = 0; + for (uint64_t i = 0; i < size; ++i) { + auto b = _value->data[i]; + if (b == 0) { + uint64_t rl = 0; + for (uint64_t j = 0; j < 255 && i + j < size && _value->data[i + j] == 0; + j++) { + rl++; + } + i += rl - 1; + compressed_size += 2; + } else { + ++compressed_size; + } + } + + auto pod = TRY(arena_alloc(compressed_size)); + pod->size = compressed_size; + + auto pos = 0; + for (uint64_t i = 0; i < size; ++i) { + auto b = _value->data[i]; + if (b == 0) { + uint64_t rl = 0; + for (uint64_t j = 0; j < 255 && i + j < size && _value->data[i + j] == 0; + j++) { + rl++; + } + i += rl - 1; + pod->data[pos] = 0; + pod->data[pos + 1] = rl; + pos += 2; + } else { + pod->data[pos] = _value->data[i]; + ++pos; + } + } + + return ByteArray(TRY(MkGcRoot(pod))); +} + +Result ByteArray::from_rle() const { + uint64_t size = _value->size; + uint64_t decompressed_size = 0; + + for (uint64_t i = 0; i < size; ++i) { + auto b = _value->data[i]; + if (b == 0) { + if (i + 1 >= size) { + return ERROR(KeyError); + } + decompressed_size += _value->data[i + 1]; + i += 1; + } else { + ++decompressed_size; + } + } + + auto pod = TRY(arena_alloc(decompressed_size)); + pod->size = decompressed_size; + + uint64_t pos = 0; + for (uint64_t i = 0; i < size; ++i) { + auto b = _value->data[i]; + if (b == 0) { + uint64_t run_size = _value->data[i + 1]; + + for (uint64_t j = 0; j < run_size; j++) { + pod->data[pos + j] = 0; + } + i += 1; + pos += run_size; + } else { + pod->data[pos] = _value->data[i]; + ++pos; + } + } + + return ByteArray(TRY(MkGcRoot(pod))); +} + Result SrcLoc::create(const SourceRange& sourcerange) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::SrcLoc; pod->sourcerange = sourcerange; return SrcLoc(TRY(MkGcRoot(pod))); @@ -134,7 +228,6 @@ Result SrcLoc::cmp(const SrcLoc& rhs) const { Result Syntax::create(const String& filename, const SrcLoc& srcloc, const Value& expression) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Syntax; pod->filename = filename.pod(); pod->srcloc = srcloc.pod(); pod->expression = expression.pod(); @@ -342,12 +435,13 @@ Result Module::cmp(const Module& rhs) const { return res; } -Result StackFrame::create(const Value& parent, const Value& fun) { +Result StackFrame::create(const Value& parent, const Value& fun, + bool guard) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::StackFrame; pod->parent = parent.pod(); pod->fun = fun.pod(); + pod->guard = guard; pod->pc = 0; pod->size = 0; @@ -371,11 +465,11 @@ Result StackFrame::get(uint64_t idx) const { Result StackFrame::set(uint64_t idx, const Value& val) const { uint64_t size = std::max(_value->size, idx + 1); auto pod = TRY(arena_alloc(sizeof(OffPtr) * size)); - pod->header.tag = Tag::StackFrame; pod->parent = _value->parent; pod->fun = _value->fun; pod->pc = _value->pc; + pod->guard = _value->guard; pod->size = size; for (uint64_t i = 0; i < _value->size; i++) { @@ -394,10 +488,10 @@ Result StackFrame::set(uint64_t idx, const Value& val) const { Result StackFrame::settop(uint64_t idx) const { auto pod = TRY(arena_alloc(sizeof(OffPtr) * idx)); - pod->header.tag = Tag::StackFrame; pod->parent = _value->parent; pod->fun = _value->fun; + pod->guard = _value->guard; pod->pc = _value->pc; pod->size = idx; @@ -427,10 +521,10 @@ uint64_t StackFrame::pc() const { return _value->pc; } Result StackFrame::setpc(uint64_t pc) { auto pod = TRY(arena_alloc(sizeof(OffPtr) * _value->size)); - pod->header.tag = Tag::StackFrame; pod->parent = _value->parent; pod->fun = _value->fun; + pod->guard = _value->guard; pod->pc = pc; pod->size = _value->size; @@ -476,6 +570,105 @@ Result StackFrame::ret(uint64_t regnum) const { return std::move(parent_stack); } +Result StackFrame::detach(uint64_t depth) const { + if (depth == 0) { + uint64_t size = _value->size; + auto pod = + TRY(arena_alloc(sizeof(OffPtr) * size)); + + pod->parent = TRY(Nil::create()).pod(); + pod->fun = _value->fun; + pod->guard = _value->guard; + pod->pc = _value->pc; + pod->size = size; + + for (uint64_t i = 0; i < _value->size; i++) { + pod->data[i] = _value->data[i]; + } + + return StackFrame(TRY(MkGcRoot(pod))); + } + + auto par = TRY(parent()); + if (par.is()) return ERROR(KeyError); + + StackFrame& par_frame = *par.to(); + + auto par_frame_new = TRY(par_frame.detach(depth - 1)); + + uint64_t size = _value->size; + auto pod = TRY(arena_alloc(sizeof(OffPtr) * size)); + + pod->parent = par_frame_new.pod(); + pod->fun = _value->fun; + pod->guard = _value->guard; + pod->pc = _value->pc; + pod->size = size; + + for (uint64_t i = 0; i < _value->size; i++) { + pod->data[i] = _value->data[i]; + } + + return StackFrame(TRY(MkGcRoot(pod))); +} + +Result StackFrame::attach(const StackFrame& frame) const { + auto par = TRY(parent()); + if (par.is()) { + uint64_t size = _value->size; + auto pod = + TRY(arena_alloc(sizeof(OffPtr) * size)); + + pod->parent = frame.pod(); + pod->fun = _value->fun; + pod->guard = _value->guard; + pod->pc = _value->pc; + pod->size = size; + + for (uint64_t i = 0; i < _value->size; i++) { + pod->data[i] = _value->data[i]; + } + + return StackFrame(TRY(MkGcRoot(pod))); + } + + StackFrame& par_frame = *par.to(); + + auto par_frame_new = TRY(par_frame.attach(frame)); + + uint64_t size = _value->size; + auto pod = TRY(arena_alloc(sizeof(OffPtr) * size)); + + pod->parent = par_frame_new.pod(); + pod->fun = _value->fun; + pod->guard = _value->guard; + pod->pc = _value->pc; + pod->size = size; + + for (uint64_t i = 0; i < _value->size; i++) { + pod->data[i] = _value->data[i]; + } + + return StackFrame(TRY(MkGcRoot(pod))); +} + +Result StackFrame::set_guard(bool guard) const { + uint64_t size = _value->size; + auto pod = TRY(arena_alloc(sizeof(OffPtr) * size)); + + pod->parent = _value->parent; + pod->fun = _value->fun; + pod->guard = guard; + pod->pc = _value->pc; + pod->size = size; + + for (uint64_t i = 0; i < _value->size; i++) { + pod->data[i] = _value->data[i]; + } + + return StackFrame(TRY(MkGcRoot(pod))); +} + Result StackFrame::backtrace(uint64_t indent) const { String res = TRY(String::create("")); @@ -483,34 +676,36 @@ Result StackFrame::backtrace(uint64_t indent) const { while (true) { Value par = TRY(cur.parent()); - // If parent frame is empty - we are at the top-level. Top-level is always - // , so let's just skip it - if (par.is()) break; - auto fun = TRY(cur.fun()); for (uint64_t i = 0; i < indent; i++) { res = TRY(res.concat(" ")); } - res = TRY(res.concat("Function ")); - if (fun.is()) { - Function& f = *fun.to(); - auto name = TRY(f.name()); - if (!name.is()) { - name = Value(TRY(String::create(""))); - } else { - name = Value(TRY(String::create(*name.to()))); + if (cur.guard()) { + res = TRY(res.concat("guard")); + } else { + res = TRY(res.concat("Function ")); + if (fun.is()) { + Function& f = *fun.to(); + auto name = TRY(f.name()); + if (!name.is()) { + name = Value(TRY(String::create(""))); + } else { + name = Value(TRY(String::create(*name.to()))); + } + res = TRY(res.concat(*name.to())); + } else if (fun.is()) { + StdlibFunction& f = *fun.to(); + auto name = TRY(String::create(TRY(f.name()))); + res = TRY(res.concat(name)); } - res = TRY(res.concat(*name.to())); - } else if (fun.is()) { - StdlibFunction& f = *fun.to(); - auto name = TRY(String::create(TRY(f.name()))); - res = TRY(res.concat(name)); } res = TRY(res.concat("\n")); + if (par.is()) break; + cur = TRY(par.to()->copy()); } return res; @@ -541,7 +736,6 @@ Result Error::cmp(const Error& rhs) const { Result Continuation::create(const Value& value, const StackFrame& frame) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Continuation; pod->value = value.pod(); pod->frame = frame.pod(); @@ -577,7 +771,6 @@ Result Continuation::cmp(const Continuation& rhs) const { Result Task::create(uint64_t task_id, const Array& params) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Task; pod->task_id = task_id; pod->params = params.pod(); @@ -614,7 +807,6 @@ Result Task::cmp(const Task& rhs) const { Result TaskResult::create(const Value& result, const Value& error) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::TaskResult; pod->result = result.pod(); pod->error = error.pod(); @@ -650,7 +842,6 @@ Result TaskResult::cmp(const TaskResult& rhs) const { Result Pair::create(const Value& first, const Value& rest) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Pair; pod->first = first.pod(); pod->rest = rest.pod(); @@ -719,7 +910,6 @@ Result Function::create(const Value& name, uint64_t arity, const Array& constants, const Array& code, const Array& closure) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Function; pod->arity = arity; pod->name = name.pod(); pod->constants = constants.pod(); @@ -732,7 +922,6 @@ Result Function::create(const Value& name, uint64_t arity, Result Function::create(const Function& prototype, const Array& closure) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Function; pod->arity = prototype._value->arity; pod->name = prototype._value->name; pod->constants = prototype._value->constants; @@ -760,7 +949,6 @@ Result Function::closure() const { Result StdlibFunction::create(StdlibFunctionId fun_id) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::StdlibFunction; pod->fun_id = (uint64_t)fun_id; return StdlibFunction(TRY(MkGcRoot(pod))); @@ -778,7 +966,6 @@ StdlibFunctionId StdlibFunction::fun_id() const { Result Module::create(const Value& name, const Function& fun) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Module; pod->name = name.pod(); pod->fun = fun.pod(); @@ -847,7 +1034,6 @@ Result Array::set(uint64_t idx, const Value& value) const { if (idx >= res_size) return ERROR(IndexOutOfRange); auto pod = TRY(arena_alloc(res_size * sizeof(OffPtr))); - pod->header.tag = Tag::Array; pod->size = res_size; for (uint64_t i = 0; i < res_size; i++) { pod->data[i] = _value->data[i].get(); @@ -872,7 +1058,6 @@ Result Array::append(const Value& rhs) const { auto size = TRY(this->size()); auto pod = TRY(arena_alloc(res_size * sizeof(OffPtr))); - pod->header.tag = Tag::Array; pod->size = res_size; for (uint64_t i = 0; i < size; i++) { pod->data[i] = _value->data[i].get(); @@ -930,7 +1115,6 @@ Result Dict::set(const Value& key, const Value& value) const { } auto pod = TRY(arena_alloc(2 * s * sizeof(OffPtr))); - pod->header.tag = Tag::Dict; pod->size = s; auto vpod = _value.get(); @@ -1092,7 +1276,7 @@ Result Dict::cmp(const Dict& rhs) const { uint64_t i = 0; uint64_t j = 0; while (1) { - if (i == lsize && j == lsize) return 0; + if (i == lsize && j == rsize) return 0; short cmp = short(i == lsize) - short(j == rsize); if (cmp != 0) return cmp; @@ -1113,7 +1297,7 @@ Result Array::cmp(const Array& rhs) const { uint64_t i = 0; uint64_t j = 0; while (1) { - if (i == lsize && j == lsize) return 0; + if (i == lsize && j == rsize) return 0; short cmp = short(i == lsize) - short(j == rsize); if (cmp != 0) return cmp; diff --git a/src/common.hpp b/src/common.hpp index 422e70a..9923fbe 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -166,7 +166,6 @@ class Nil : public Object { static Result create() { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Nil; return Nil(TRY(MkGcRoot(pod))); } @@ -209,7 +208,6 @@ class Array : public Object { static Result create() { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Array; pod->size = 0; @@ -237,7 +235,6 @@ class Array : public Object { uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(PodObject*))); - pod->header.tag = Tag::Array; pod->size = res_size; for (uint64_t i = 0; i < lhs_size; i++) { pod->data[i] = _value->data[i].get(); @@ -253,7 +250,6 @@ class Array : public Object { if (start > end) return ERROR(IndexOutOfRange); uint64_t res_size = end - start; auto pod = TRY(arena_alloc(res_size * sizeof(PodObject*))); - pod->header.tag = Tag::Array; pod->size = res_size; for (uint64_t i = 0; i < end - start; i++) { pod->data[i] = _value->data[start + i]; @@ -293,7 +289,6 @@ class ByteArray : public Object { } static Result create(char* chars, int64_t size) { auto pod = TRY(arena_alloc(size * sizeof(char))); - pod->header.tag = Tag::ByteArray; memcpy(pod->data, chars, size * sizeof(char32_t)); pod->size = size; @@ -304,7 +299,6 @@ class ByteArray : public Object { static Result create(const char* str) { uint64_t size = strlen(str); auto pod = TRY(arena_alloc(size * sizeof(char32_t))); - pod->header.tag = Tag::ByteArray; memcpy(pod->data, str, size); pod->size = size; @@ -331,7 +325,6 @@ class ByteArray : public Object { uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(char))); - pod->header.tag = Tag::ByteArray; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char) * lhs_size); memcpy(pod->data, rhs + lhs_size, sizeof(char) * rhs_size); @@ -344,10 +337,9 @@ class ByteArray : public Object { uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(char))); - pod->header.tag = Tag::ByteArray; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char) * lhs_size); - memcpy(pod->data, rhs + lhs_size, sizeof(char) * rhs_size); + memcpy(pod->data + lhs_size, rhs, sizeof(char) * rhs_size); return ByteArray(TRY(MkGcRoot(pod))); } @@ -358,7 +350,6 @@ class ByteArray : public Object { uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(char))); - pod->header.tag = Tag::ByteArray; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char) * lhs_size); memcpy(pod->data + lhs_size, rhs._value->data, sizeof(char) * rhs_size); @@ -370,13 +361,16 @@ class ByteArray : public Object { if (start > end) return ERROR(IndexOutOfRange); uint64_t res_size = end - start; auto pod = TRY(arena_alloc(res_size * sizeof(char))); - pod->header.tag = Tag::ByteArray; pod->size = res_size; memcpy(pod->data, _value->data + start, sizeof(char) * res_size); return ByteArray(TRY(MkGcRoot(pod))); } + Result hex() const; + Result to_rle() const; + Result from_rle() const; + private: GcRoot _value; }; @@ -410,7 +404,6 @@ class Dict : public Object { static Result create() { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Dict; pod->size = 0; @@ -476,7 +469,7 @@ class String : public Object { uint64_t i = 0; uint64_t j = 0; while (1) { - if (i == lsize && j == lsize) return 0; + if (i == lsize && j == rsize) return 0; short cmp = short(i == lsize) - short(j == rsize); if (cmp != 0) return cmp; @@ -498,7 +491,6 @@ class String : public Object { } static Result create(char32_t* chars, int64_t size) { auto pod = TRY(arena_alloc(size * sizeof(char32_t))); - pod->header.tag = Tag::String; memcpy(pod->data, chars, size * sizeof(char32_t)); pod->size = size; @@ -510,7 +502,6 @@ class String : public Object { auto size = TRY(bytes.size()); auto pod = TRY(arena_alloc(size * sizeof(char32_t))); - pod->header.tag = Tag::String; for (uint64_t i = 0; i < size; i++) pod->data[i] = TRY(bytes[i]); pod->size = size; @@ -523,7 +514,6 @@ class String : public Object { static Result create(const char* str) { uint64_t size = strlen(str); auto pod = TRY(arena_alloc(size * sizeof(char32_t))); - pod->header.tag = Tag::String; for (uint64_t i = 0; i < size; i++) pod->data[i] = str[i]; pod->size = size; @@ -548,7 +538,6 @@ class String : public Object { uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(char32_t))); - pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char32_t) * lhs_size); for (uint64_t i = 0; i < rhs_size; i++) pod->data[lhs_size + i] = rhs[i]; @@ -563,7 +552,6 @@ class String : public Object { uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(char32_t))); - pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char32_t) * lhs_size); for (uint64_t i = 0; i < rhs_size; i++) pod->data[lhs_size + i] = rhs[i]; @@ -577,7 +565,6 @@ class String : public Object { uint64_t res_size = lhs_size + rhs_size; auto pod = TRY(arena_alloc(res_size * sizeof(char32_t))); - pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, _value->data, sizeof(char32_t) * lhs_size); memcpy(pod->data + lhs_size, rhs._value->data, sizeof(char32_t) * rhs_size); @@ -589,7 +576,6 @@ class String : public Object { if (start > end) return ERROR(IndexOutOfRange); uint64_t res_size = end - start; auto pod = TRY(arena_alloc(res_size * sizeof(char32_t))); - pod->header.tag = Tag::String; pod->size = res_size; memcpy(pod->data, _value->data + start, sizeof(char32_t) * res_size); @@ -620,7 +606,7 @@ class Symbol : public Object { uint64_t i = 0; uint64_t j = 0; while (1) { - if (i == lsize && j == lsize) return 0; + if (i == lsize && j == rsize) return 0; short cmp = short(i == lsize) - short(j == rsize); if (cmp != 0) return cmp; @@ -662,7 +648,6 @@ class Symbol : public Object { static Result create(char32_t* chars, int64_t size) { auto pod = TRY(arena_alloc(size * sizeof(char32_t))); - pod->header.tag = Tag::Symbol; memcpy(pod->data, chars, size * sizeof(char32_t)); @@ -672,7 +657,6 @@ class Symbol : public Object { static Result create(const char* str) { uint64_t size = strlen(str); auto pod = TRY(arena_alloc(size * sizeof(char32_t))); - pod->header.tag = Tag::Symbol; for (uint64_t i = 0; i < size; i++) pod->data[i] = str[i]; pod->size = size; @@ -717,7 +701,6 @@ class SrcLoc : public Object { static Result create(const SourcePosition& start, const SourcePosition& end) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::SrcLoc; pod->sourcerange.start = start; pod->sourcerange.end = end; @@ -849,9 +832,8 @@ class Int64 : public Object { return Int64(TRY(MkGcRoot(obj))); } - static Result create(double val) { + static Result create(int64_t val) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Int64; pod->value = val; return Int64(TRY(MkGcRoot(pod))); @@ -908,7 +890,6 @@ class Float : public Object { static Result create(double val) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Float; pod->value = val; return Float(TRY(MkGcRoot(pod))); @@ -945,7 +926,6 @@ class Bool : public Object { static Result create(bool val) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Bool; pod->value = val; return Bool(TRY(MkGcRoot(pod))); @@ -983,7 +963,6 @@ class Opcode : public Object { OpArg arg2 = {0, 0}, OpArg arg3 = {0, 0}, OpArg arg4 = {0, 0}) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Opcode; pod->opcode = opcode; pod->arg1 = arg1; pod->arg2 = arg2; @@ -1164,13 +1143,13 @@ class StackFrame : public Object { Result incpc(); Result size() const { return _value->size; } - static Result create(const Value& parent, const Value& fun); + static Result create(const Value& parent, const Value& fun, + bool guard = false); Result slice(uint64_t start, uint64_t end) const { if (start > end || end > _value->size) return ERROR(IndexOutOfRange); uint64_t res_size = end - start; auto pod = TRY(arena_alloc(res_size * sizeof(PodObject*))); - pod->header.tag = Tag::Array; pod->size = res_size; for (uint64_t i = 0; i < end - start; i++) { pod->data[i] = _value->data[start + i]; @@ -1182,9 +1161,16 @@ class StackFrame : public Object { Result call(const Value& fun, uint64_t start, uint64_t end) const; Result ret(uint64_t regno) const; + Result detach(uint64_t depth) const; + Result attach(const StackFrame& frame) const; + + Result set_guard(bool guard = true) const; + virtual Result copy_value() const final; Result copy() const; + bool guard() const { return _value->guard; } + Result backtrace(uint64_t indent = 0) const; private: @@ -1215,7 +1201,6 @@ class Error : public Object { auto message_str = TRY(String::create(message)); auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Error; pod->name = name_str.pod(); pod->message = message_str.pod(); @@ -1225,7 +1210,6 @@ class Error : public Object { static Result create(const Symbol& name, const String& message) { auto pod = TRY(arena_alloc()); - pod->header.tag = Tag::Error; pod->name = name.pod(); pod->message = message.pod(); diff --git a/src/error.cpp b/src/error.cpp index e3c8701..e2ae8c5 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -19,9 +19,58 @@ void reseterrobj() { errobj = Value(); } const char* errname(ErrorCode errcode) { switch (errcode) { + case ErrorCode::Success: + return "success"; + case ErrorCode::Unknown: + return "unknown"; + case ErrorCode::OutOfMemory: + return "out-of-memory"; + case ErrorCode::IndexOutOfRange: + return "index-out-of-range"; + case ErrorCode::TypeMismatch: + return "type-mismatch"; + case ErrorCode::ReadError: + return "read-error"; + case ErrorCode::UnterminatedStringLiteral: + return "unterminalted-string-literal"; + case ErrorCode::InvalidNumericLiteral: + return "invalid-numeric-literal"; + case ErrorCode::NotImplemented: + return "not-implemented"; + case ErrorCode::InvalidSymbol: + return "invalid-symbol"; + case ErrorCode::MalformedList: + return "malformed-list"; + case ErrorCode::KeyError: + return "key-error"; + case ErrorCode::EndOfProgram: + return "end-of-program"; + case ErrorCode::CompilationError: + return "compilation-error"; + case ErrorCode::ArgumentCountMismatch: + return "argument-count-mismatch"; + case ErrorCode::IOError: + return "io-error"; + case ErrorCode::Interrupt: + return "interrupt"; + case ErrorCode::AssertionFailed: + return "assertion-failed"; + case ErrorCode::DivisionByZero: + return "division-by-zero"; case ErrorCode::SyntaxError: return "syntax-error"; - default: - return "unknown"; + case ErrorCode::RuntimeError: + return "runtime-error"; + case ErrorCode::Raise: + return "raise"; + case ErrorCode::TaskFailed: + return "task-failed"; } + return ""; +} + +Result wrap_error(ErrorCode code) { + auto name = errname(code); + auto message = geterr(); + return Value(TRY(Error::create(name, message))); } diff --git a/src/error.hpp b/src/error.hpp index 1d6d8c0..65d8b85 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -40,6 +40,8 @@ const Value& geterrobj(void); Result seterrobj(const Value&); void reseterrobj(); +Result wrap_error(ErrorCode code); + #define STRINGIZE_NESTED(A) #A #define STRINGIZE(A) STRINGIZE_NESTED(A) diff --git a/src/fio.cpp b/src/fio.cpp index 922f338..9204f07 100644 --- a/src/fio.cpp +++ b/src/fio.cpp @@ -63,7 +63,7 @@ Result read_file(const char* filename) { } Result read_file(const ByteArray& filename) { - auto filename_bytes = TRY(filename.concat("\0")); + auto filename_bytes = TRY(filename.concat("\0", 1)); FILE* fh = fopen(filename_bytes.raw_bytes(), "r"); if (!fh) { @@ -78,7 +78,7 @@ Result read_file(const ByteArray& filename) { Result read_stdin() { return read_fh(stdin); } Result write_file(const ByteArray& filename, const ByteArray& contents) { - auto filename_bytes = TRY(filename.concat("\0")); + auto filename_bytes = TRY(filename.concat("\0", 1)); FILE* fh = fopen(filename_bytes.raw_bytes(), "w"); if (!fh) { diff --git a/src/pod.hpp b/src/pod.hpp index 2953121..001229c 100644 --- a/src/pod.hpp +++ b/src/pod.hpp @@ -201,6 +201,7 @@ class PodStackFrame final : public PodObject { OffPtr parent; OffPtr fun; + bool guard; uint64_t pc; uint64_t size; OffPtr data[]; diff --git a/src/serialize.cpp b/src/serialize.cpp new file mode 100644 index 0000000..49da110 --- /dev/null +++ b/src/serialize.cpp @@ -0,0 +1,395 @@ +#include "serialize.hpp" + +#include "arena.hpp" +#include "common.hpp" +#include "pod.hpp" + +class Serializer { + public: + Serializer(uint8_t* buf, bool dryrun) + : buf(buf), _offset(0), dryrun(dryrun) {} + + PodObject* serialize_nil(PodNil* obj) { + PodNil* res = (PodNil*)(buf + _offset); + if (!dryrun) { + memcpy(res, obj, sizeof(PodNil)); + } + + _offset += round_up(sizeof(PodNil), 8); + return res; + } + PodObject* serialize_bool(PodBool* obj) { + PodBool* res = (PodBool*)(buf + _offset); + if (!dryrun) { + memcpy(res, obj, sizeof(PodBool)); + } + + _offset += round_up(sizeof(PodBool), 8); + return res; + } + PodObject* serialize_int64(PodInt64* obj) { + PodInt64* res = (PodInt64*)(buf + _offset); + if (!dryrun) { + memcpy(res, obj, sizeof(PodInt64)); + } + + _offset += sizeof(PodInt64); + return res; + } + PodObject* serialize_float(PodFloat* obj) { + PodFloat* res = (PodFloat*)(buf + _offset); + if (!dryrun) { + memcpy(res, obj, sizeof(PodFloat)); + } + + _offset += round_up(sizeof(PodFloat), 8); + return res; + } + PodObject* serialize_string(PodString* obj) { + PodString* res = (PodString*)(buf + _offset); + uint64_t size = sizeof(PodString) + sizeof(char32_t) * obj->size; + if (!dryrun) { + memcpy(res, obj, size); + } + + _offset += round_up(size, 8); + return res; + } + PodObject* serialize_symbol(PodSymbol* obj) { + PodSymbol* res = (PodSymbol*)(buf + _offset); + uint64_t size = sizeof(PodSymbol) + sizeof(char32_t) * obj->size; + if (!dryrun) { + memcpy(res, obj, size); + } + + _offset += round_up(size, 8); + return res; + } + PodObject* serialize_srcloc(PodSrcLoc* obj) { + PodSrcLoc* res = (PodSrcLoc*)(buf + _offset); + uint64_t size = sizeof(PodSrcLoc); + if (!dryrun) { + memcpy(res, obj, size); + } + + _offset += round_up(size, 8); + return res; + } + PodObject* serialize_syntax(PodSyntax* obj) { + PodSyntax* res = (PodSyntax*)(buf + _offset); + uint64_t size = sizeof(PodSyntax); + _offset += round_up(size, 8); + + auto filename = serialize_object(obj->filename.get()); + auto srcloc = serialize_object(obj->srcloc.get()); + auto expression = serialize_object(obj->expression.get()); + if (!dryrun) { + res->header.tag = Tag::Syntax; + res->filename = filename; + res->srcloc = srcloc; + res->expression = expression; + } + + return res; + } + PodObject* serialize_pair(PodPair* obj) { + PodPair* res = (PodPair*)(buf + _offset); + uint64_t size = sizeof(PodPair); + _offset += round_up(size, 8); + + auto first = serialize_object(obj->first.get()); + auto rest = serialize_object(obj->rest.get()); + if (!dryrun) { + res->header.tag = Tag::Pair; + res->first = first; + res->rest = rest; + } + + return res; + } + PodObject* serialize_array(PodArray* obj) { + PodArray* res = (PodArray*)(buf + _offset); + uint64_t size = sizeof(PodArray) + sizeof(OffPtr) * obj->size; + _offset += round_up(size, 8); + + if (!dryrun) { + res->header.tag = Tag::Array; + res->size = obj->size; + } + + for (uint64_t i = 0; i < obj->size; ++i) { + auto val = serialize_object(obj->data[i].get()); + if (!dryrun) { + res->data[i] = val; + } + } + + return res; + } + PodObject* serialize_bytearray(PodByteArray* obj) { + PodByteArray* res = (PodByteArray*)(buf + _offset); + uint64_t size = sizeof(PodArray) + obj->size; + _offset += round_up(size, 8); + + if (!dryrun) { + res->header.tag = Tag::ByteArray; + memcpy(res, obj, size); + } + + return res; + } + PodObject* serialize_dict(PodDict* obj) { + PodDict* res = (PodDict*)(buf + _offset); + uint64_t size = sizeof(PodDict) + sizeof(OffPtr) * obj->size * 2; + _offset += round_up(size, 8); + + if (!dryrun) { + res->header.tag = Tag::Dict; + res->size = obj->size; + } + + for (uint64_t i = 0; i < obj->size * 2; ++i) { + auto val = serialize_object(obj->data[i].get()); + if (!dryrun) { + res->data[i] = val; + } + } + + return res; + } + + PodObject* serialize_opcode(PodOpcode* obj) { + PodOpcode* res = (PodOpcode*)(buf + _offset); + uint64_t size = sizeof(PodOpcode); + _offset += round_up(size, 8); + + if (!dryrun) { + memcpy(res, obj, size); + } + + return res; + } + PodObject* serialize_function(PodFunction* obj) { + PodFunction* res = (PodFunction*)(buf + _offset); + uint64_t size = sizeof(PodFunction); + _offset += round_up(size, 8); + + auto closure = serialize_object(obj->closure.get()); + auto code = serialize_object(obj->code.get()); + auto constants = serialize_object(obj->constants.get()); + auto name = serialize_object(obj->name.get()); + + if (!dryrun) { + res->header.tag = Tag::Function; + res->arity = obj->arity; + res->closure = closure; + res->code = code; + res->constants = constants; + res->name = name; + } + + return res; + } + PodObject* serialize_stdlib_function(PodStdlibFunction* obj) { + PodStdlibFunction* res = (PodStdlibFunction*)(buf + _offset); + uint64_t size = sizeof(PodStdlibFunction); + _offset += round_up(size, 8); + + if (!dryrun) { + memcpy(res, obj, size); + } + + return res; + } + PodObject* serialize_module(PodModule* obj) { + PodModule* res = (PodModule*)(buf + _offset); + uint64_t size = sizeof(PodModule); + _offset += round_up(size, 8); + + auto fun = serialize_object(obj->fun.get()); + auto name = serialize_object(obj->name.get()); + + if (!dryrun) { + res->header.tag = Tag::Module; + res->fun = fun; + res->name = name; + } + + return res; + } + PodObject* serialize_stack(PodStackFrame* obj) { + PodStackFrame* res = (PodStackFrame*)(buf + _offset); + uint64_t size = + sizeof(PodStackFrame) + sizeof(OffPtr) * obj->size; + _offset += round_up(size, 8); + + auto fun = serialize_object(obj->fun.get()); + auto parent = serialize_object(obj->parent.get()); + + if (!dryrun) { + res->header.tag = Tag::StackFrame; + res->fun = fun; + res->parent = parent; + res->pc = obj->pc; + res->size = obj->size; + res->guard = obj->guard; + } + + for (uint64_t i = 0; i < obj->size; i++) { + auto val = serialize_object(obj->data[i].get()); + if (!dryrun) { + res->data[i] = val; + } + } + + return res; + } + PodObject* serialize_error(PodError* obj) { + PodError* res = (PodError*)(buf + _offset); + uint64_t size = sizeof(PodError); + _offset += round_up(size, 8); + + auto name = serialize_object(obj->name.get()); + auto message = serialize_object(obj->message.get()); + + if (!dryrun) { + res->header.tag = Tag::Error; + res->name = name; + res->message = message; + } + + return res; + } + + PodObject* serialize_continuation(PodContinuation* obj) { + PodContinuation* res = (PodContinuation*)(buf + _offset); + uint64_t size = sizeof(PodContinuation); + _offset += round_up(size, 8); + + auto frame = serialize_object(obj->frame.get()); + auto value = serialize_object(obj->value.get()); + + if (!dryrun) { + res->header.tag = Tag::Continuation; + res->frame = frame; + res->value = value; + } + + return res; + } + PodObject* serialize_task(PodTask* obj) { + PodTask* res = (PodTask*)(buf + _offset); + uint64_t size = sizeof(PodTask); + _offset += round_up(size, 8); + + auto params = serialize_object(obj->params.get()); + + if (!dryrun) { + res->header.tag = Tag::Task; + res->task_id = obj->task_id; + res->params = params; + } + + return res; + } + PodObject* serialize_task_result(PodTaskResult* obj) { + PodTaskResult* res = (PodTaskResult*)(buf + _offset); + uint64_t size = sizeof(PodTaskResult); + _offset += round_up(size, 8); + + auto result = serialize_object(obj->result.get()); + auto error = serialize_object(obj->error.get()); + + if (!dryrun) { + res->header.tag = Tag::TaskResult; + res->result = result; + res->error = error; + } + + return res; + } + + PodObject* serialize_object(PodObject* obj) { + switch (obj->header.tag) { + case Tag::Nil: + return serialize_nil((PodNil*)obj); + case Tag::Bool: + return serialize_bool((PodBool*)obj); + case Tag::Int64: + return serialize_int64((PodInt64*)obj); + case Tag::Float: + return serialize_float((PodFloat*)obj); + case Tag::String: + return serialize_string((PodString*)obj); + case Tag::Symbol: + return serialize_symbol((PodSymbol*)obj); + case Tag::SrcLoc: + return serialize_srcloc((PodSrcLoc*)obj); + case Tag::Syntax: + return serialize_syntax((PodSyntax*)obj); + case Tag::Pair: + return serialize_pair((PodPair*)obj); + case Tag::Array: + return serialize_array((PodArray*)obj); + case Tag::ByteArray: + return serialize_bytearray((PodByteArray*)obj); + case Tag::Dict: + return serialize_dict((PodDict*)obj); + case Tag::Opcode: + return serialize_opcode((PodOpcode*)obj); + case Tag::Function: + return serialize_function((PodFunction*)obj); + case Tag::StdlibFunction: + return serialize_stdlib_function((PodStdlibFunction*)obj); + case Tag::Module: + return serialize_module((PodModule*)obj); + case Tag::StackFrame: + return serialize_stack((PodStackFrame*)obj); + case Tag::Error: + return serialize_error((PodError*)obj); + case Tag::Continuation: + return serialize_continuation((PodContinuation*)obj); + case Tag::Task: + return serialize_task((PodTask*)obj); + case Tag::TaskResult: + return serialize_task_result((PodTaskResult*)obj); + }; + + // Should never reach here + return 0; + } + + uint64_t offset() { return _offset; } + + private: + uint8_t* buf; + uint64_t _offset; + bool dryrun; +}; + +Result serialize(const Value& val) { + Serializer s(0, true); + s.serialize_object(val.pod()); + auto size = s.offset(); + + auto pod = TRY(arena_alloc(size * sizeof(char))); + + pod->size = size; + Serializer ss((uint8_t*)pod->data, false); + ss.serialize_object(val.pod()); + + auto ba = ByteArray(TRY(MkGcRoot(pod))); + ba = TRY(ba.to_rle()); + + return ba; +} + +Result deserialize(const ByteArray& val) { + auto decompressed = TRY(val.from_rle()); + + auto size = TRY(decompressed.size()); + PodObject* obj = TRY(arena_alloc_bytes(size)); + memcpy(obj, decompressed.raw_bytes(), size); + + return TRY(Value::create(obj)); +} diff --git a/src/serialize.hpp b/src/serialize.hpp new file mode 100644 index 0000000..230eaac --- /dev/null +++ b/src/serialize.hpp @@ -0,0 +1,9 @@ +#pragma once + +template +class Result; +class Value; +class ByteArray; + +Result serialize(const Value& val); +Result deserialize(const ByteArray& val); diff --git a/src/stdlib.cpp b/src/stdlib.cpp index b5d1e10..b666074 100644 --- a/src/stdlib.cpp +++ b/src/stdlib.cpp @@ -4,6 +4,7 @@ #include "error.hpp" #include "fio.hpp" #include "pod.hpp" +#include "serialize.hpp" #include "sourcerange.hpp" #include "writer.hpp" @@ -391,7 +392,7 @@ Result stdlib_raise(const StackFrame& stack) { auto stack_size = TRY(stack.size()); if (stack_size > 1) { - return task_return(stack, 1); + return TRY(stack.ret(1)); } auto params = TRY(stack.get(0)); @@ -434,6 +435,103 @@ Result stdlib_task(const StackFrame& stack) { return res; } +Result stdlib_guard(const StackFrame& stack) { + auto stack_size = TRY(stack.size()); + auto params = TRY(stack.get(0)); + Array& params_array = *params.to(); + + auto size = TRY(params.size()); + if (size != 2) return ERROR(ArgumentCountMismatch); + + if (stack_size == 1) { + // This is the first entry to "guard", so need to establish + // context and call the guarded function + Value fun = TRY(params_array.get(0)); + + auto new_frame = TRY(stack.set_guard(true)); + // Set stack[1] to "0", meaning that we've entered the guarded + // function + new_frame = TRY(new_frame.set(1, Value(TRY(Int64::create((int64_t)0))))); + new_frame = TRY(new_frame.set(2, fun)); + + return new_frame.call(fun, 2, 3); + } + + auto state = TRY(stack.get(1)); + if (!state.is()) return ERROR(TypeMismatch); + auto state_int = state.to()->value(); + + if (state_int == 0) { + // We've just normally returned from the guarded function. Just return + // its results back to the caller. + + return stack.ret(2); + } + + if (state_int == 1) { + // Exception has occured, and continuation is on the stack. We should + // now call the exception handler. + Value handler = TRY(params_array.get(1)); + + // Set stack[1] to "2", meaning that we've entered the guarded + // function + auto new_frame = TRY(stack.set(1, Value(TRY(Int64::create((int64_t)2))))); + auto cont = TRY(new_frame.get(2)); + if (!cont.is()) return ERROR(TypeMismatch); + auto val = TRY(cont.to()->value()); + new_frame = TRY(new_frame.set(2, handler)); + new_frame = TRY(new_frame.set(3, val)); + new_frame = TRY(new_frame.set(4, cont)); + + return new_frame.call(handler, 2, 5); + } + + if (state_int == 2) { + // We've returned from the exception handler. Propagate its result to + // the caller. + + return stack.ret(2); + } + + return ERROR(NotImplemented); +} + +Result stdlib_serialize(const StackFrame& stack) { + auto stack_size = TRY(stack.size()); + auto params = TRY(stack.get(0)); + Array& params_array = *params.to(); + + auto size = TRY(params.size()); + if (size != 1) return ERROR(ArgumentCountMismatch); + + auto val = TRY(params.get(0)); + auto serialized = Value(TRY(serialize(val))); + + auto res = TRY(stack.set(0, serialized)); + + res = TRY(res.ret(0)); + + return res; +} + +Result stdlib_deserialize(const StackFrame& stack) { + auto stack_size = TRY(stack.size()); + auto params = TRY(stack.get(0)); + Array& params_array = *params.to(); + + auto size = TRY(params.size()); + if (size != 1) return ERROR(ArgumentCountMismatch); + + auto val = TRY(params.get(0)); + if (!val.is()) return ERROR(TypeMismatch); + auto deserialized = TRY(deserialize(*val.to())); + + auto res = TRY(stack.set(0, deserialized)); + + res = TRY(res.ret(0)); + + return res; +} #define STDLIB_FUNCTION(name, id) \ [(uint64_t)StdlibFunctionId::id] = {#name, StdlibFunctionId::id, \ stdlib_##name} @@ -457,6 +555,9 @@ static StdlibFunctionEntry function_entries[] = { STDLIB_FUNCTION(error, Error), STDLIB_FUNCTION(raise, Raise), STDLIB_FUNCTION(task, Task), + STDLIB_FUNCTION(guard, Guard), + STDLIB_FUNCTION(serialize, Serialize), + STDLIB_FUNCTION(deserialize, Deserialize), [(uint64_t)StdlibFunctionId::Max] = {0, StdlibFunctionId::Max, stdlib_unknown}, }; diff --git a/src/stdlib.hpp b/src/stdlib.hpp index 9b91129..d27c56c 100644 --- a/src/stdlib.hpp +++ b/src/stdlib.hpp @@ -23,6 +23,9 @@ enum class StdlibFunctionId : uint64_t { Error, Raise, Task, + Guard, + Serialize, + Deserialize, Max, }; diff --git a/src/valeri.cpp b/src/valeri.cpp index 3391551..4b697f9 100644 --- a/src/valeri.cpp +++ b/src/valeri.cpp @@ -22,6 +22,16 @@ Result print_error(const Result& res, const String& backtrace) { auto message = TRY(errobj.to()->message()); print_string(*message.to()); std::cout << "\n"; + } else if (errobj.is()) { + auto val = TRY(errobj.to()->value()); + if (val.is()) { + auto message = TRY(val.to()->message()); + print_string(*message.to()); + std::cout << "\n"; + } else { + std::cout << "Continuation: "; + debug_print(val); + } } else { std::cout << "Uncaught error: "; debug_print(errobj); @@ -61,6 +71,7 @@ Result run_string(const String& fname, const String& src) { if (maybe_res.has_error()) { auto backtrace = TRY(vm.backtrace(2)); print_error(maybe_res, backtrace); + return maybe_res; } return maybe_res.release_value(); diff --git a/src/vm.cpp b/src/vm.cpp index 0b321db..0fb83da 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -124,6 +124,23 @@ Result VM::vm_call_stdlib(Opcode& oc, StdlibFunction& fun) { return Result(); } +Result VM::vm_call_cont(Opcode& oc, Continuation& fun) { + uint64_t reg_start = (uint64_t)oc.arg1().arg; + uint64_t reg_end = (uint64_t)oc.arg2().arg; + + if (reg_end - reg_start != 2) return ERROR(ArgumentCountMismatch); + + auto param = TRY(_stack.get(reg_start + 1)); + _stack = TRY(_stack.settop(reg_start)); + + auto cont_stack = TRY(fun.frame()); + _stack = TRY(_stack.incpc()); + _stack = TRY(cont_stack.attach(_stack)); + _stack = TRY(_stack.set(TRY(_stack.size()), param)); + + return Result(); +} + Result VM::vm_call(Opcode& oc) { Value fun_value = TRY(get(oc.arg1().is_const, oc.arg1().arg)); if (fun_value.is()) { @@ -136,6 +153,11 @@ Result VM::vm_call(Opcode& oc) { return vm_call_stdlib(oc, fun); } + if (fun_value.is()) { + Continuation& fun = *fun_value.to(); + return vm_call_cont(oc, fun); + } + return ERROR(TypeMismatch); } @@ -280,18 +302,58 @@ Result VM::step_bytecode() { return Result(); } +Result VM::unwind(const Continuation& cont) { + StackFrame cur = TRY(cont.frame()); + + uint64_t depth = 0; + while (true) { + if (cur.guard()) { + auto state = TRY(cur.get(1)); + if (!state.is()) return ERROR(TypeMismatch); + auto state_int = state.to()->value(); + + if (state_int <= 1) { + auto val = TRY(cont.value()); + auto cont_stack = TRY(cont.frame()); + cont_stack = TRY(cont_stack.detach(depth - 1)); + + auto new_cont = TRY(Continuation::create(val, cont_stack)); + + cur = TRY(cur.set(1, Value(TRY(Int64::create((int64_t)1))))); + cur = TRY(cur.set(2, Value(std::move(new_cont)))); + return Value(std::move(cur)); + } + } + + auto parent = TRY(cur.parent()); + if (parent.is()) break; + + cur = TRY(parent.to()->copy()); + ++depth; + } + return Value(TRY(Nil::create())); +} + Result VM::handle_raise(const Continuation& cont) { // TODO: this should do proper stack unwinding + + auto unwind_res = TRY(unwind(cont)); + + if (!unwind_res.is()) { + auto new_stack = TRY(unwind_res.to()->copy()); + return new_stack; + } + auto value = TRY(cont.value()); - if (value.is()) { + if (!value.is()) { auto obj = TRY(cont.copy_value()); return ERROR_OBJ(Raise, obj); } - Value tr; - if (value.is()) { + Value tr; + auto task_id = value.to()->task_id(); auto params = TRY(value.to()->params()); @@ -301,7 +363,7 @@ Result VM::handle_raise(const Continuation& cont) { const auto& err = geterrobj(); Value errobj; if (err.is()) { - errobj = Value(TRY(Error::create("unknown-error", "Unknown error"))); + errobj = TRY(wrap_error(res.error())); } else { errobj = TRY(err.copy()); } @@ -317,6 +379,7 @@ Result VM::handle_raise(const Continuation& cont) { auto new_frame = frame.set(TRY(frame.size()), tr); return new_frame; } + return ERROR(TypeMismatch); } @@ -341,8 +404,12 @@ Result VM::step() { } if (res.has_error()) { + if (res.error() == ErrorCode::EndOfProgram) return res; + auto obj = TRY(geterrobj().copy()); - if (obj.is()) return res; + if (obj.is()) { + obj = TRY(wrap_error(res.error())); + } if (res.error() == ErrorCode::Raise) { if (obj.is()) { diff --git a/src/vm.hpp b/src/vm.hpp index 1b20728..fec214a 100644 --- a/src/vm.hpp +++ b/src/vm.hpp @@ -49,6 +49,7 @@ class VM { Result vm_selfcall(Opcode& oc); Result vm_call_lisp(Opcode& oc, Function& fun); Result vm_call_stdlib(Opcode& oc, StdlibFunction& fun); + Result vm_call_cont(Opcode& oc, Continuation& fun); Result vm_ret(Opcode& oc); Result vm_equal(Opcode& oc); @@ -62,6 +63,7 @@ class VM { Result vm_global_load(Opcode& oc); Result vm_global_store(Opcode& oc); + Result unwind(const Continuation& cont); Result handle_raise(const Continuation& cont); Result get(bool is_const, uint64_t idx); diff --git a/src/writer.cpp b/src/writer.cpp index 492f7c0..b591194 100644 --- a/src/writer.cpp +++ b/src/writer.cpp @@ -122,7 +122,12 @@ Result Writer::write_bool(const Bool& val) { } Result Writer::write_bytearray(const ByteArray& val) { - return ERROR(NotImplemented); + String res = TRY(String::create("#")); + + return res; } Result Writer::write_string(const String& val) { @@ -427,11 +432,11 @@ Result Writer::write_task_result(const TaskResult& val) { String res = TRY(String::create("#()) { - res = TRY(res.concat(" :result ")); + res = TRY(res.concat(":result ")); String s = TRY(write_one(result)); res = TRY(res.concat(s)); } else { - res = TRY(res.concat(" :error ")); + res = TRY(res.concat(":error ")); String s = TRY(write_one(error)); res = TRY(res.concat(s)); } diff --git a/test/continuations.vli b/test/continuations.vli new file mode 100644 index 0000000..3526feb --- /dev/null +++ b/test/continuations.vli @@ -0,0 +1,4 @@ +(assert (= ((guard (fn () (+ 1 (raise 2))) + (fn (e x) x)) + 2) + 3)) diff --git a/test/serialize.vli b/test/serialize.vli new file mode 100644 index 0000000..ebaf238 --- /dev/null +++ b/test/serialize.vli @@ -0,0 +1,2 @@ +(assert (= (deserialize (serialize ["foo" "bar" 1])) + ["foo" "bar" 1]))