diff --git a/src/arena.cpp b/src/arena.cpp index 6b6aff8..ab0853c 100644 --- a/src/arena.cpp +++ b/src/arena.cpp @@ -98,6 +98,10 @@ Result Arena::gc_pod(PodObject* obj) { return gc_error((PodError*)obj); case Tag::Continuation: return gc_continuation((PodContinuation*)obj); + case Tag::Task: + return gc_task((PodTask*)obj); + case Tag::TaskResult: + return gc_task_result((PodTaskResult*)obj); } return ERROR(TypeMismatch); @@ -282,7 +286,7 @@ Result Arena::gc_error(PodError* obj) { Result Arena::gc_continuation(PodContinuation* obj) { auto nobj = TRY(alloc()); - nobj->header.tag = Tag::Error; + nobj->header.tag = Tag::Continuation; nobj->value = TRY(gc_pod(obj->value.get())); nobj->frame = TRY(gc_pod(obj->frame.get())); @@ -290,6 +294,26 @@ Result Arena::gc_continuation(PodContinuation* obj) { return nobj; } +Result Arena::gc_task(PodTask* obj) { + auto nobj = TRY(alloc()); + nobj->header.tag = Tag::Task; + + nobj->task_id = obj->task_id; + nobj->params = TRY(gc_pod(obj->params.get())); + + return nobj; +} + +Result Arena::gc_task_result(PodTaskResult* obj) { + auto nobj = TRY(alloc()); + nobj->header.tag = Tag::TaskResult; + + nobj->result = TRY(gc_pod(obj->result.get())); + nobj->error = TRY(gc_pod(obj->error.get())); + + return nobj; +} + Arena& get_arena() { if (current_arena == 0) die("Arena not set\n"); return *current_arena; diff --git a/src/arena.hpp b/src/arena.hpp index 1a303fd..fe012f3 100644 --- a/src/arena.hpp +++ b/src/arena.hpp @@ -162,6 +162,8 @@ class Arena { Result gc_stack(PodStackFrame* obj); Result gc_error(PodError* obj); Result gc_continuation(PodContinuation* obj); + Result gc_task(PodTask* obj); + Result gc_task_result(PodTaskResult* obj); void add_root(GcRootList* node) { _gcroot.insert(node); } diff --git a/src/common.cpp b/src/common.cpp index 34fc173..f0ba233 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -48,6 +48,10 @@ Result Value::create(PodObject* obj) { return Value(TRY(Error::create((PodError*)obj))); case Tag::Continuation: return Value(TRY(Continuation::create((PodContinuation*)obj))); + case Tag::Task: + return Value(TRY(Task::create((PodTask*)obj))); + case Tag::TaskResult: + return Value(TRY(TaskResult::create((PodTaskResult*)obj))); }; return Value(); } @@ -521,8 +525,8 @@ Result Continuation::copy() const { Result Continuation::value() const { return Value::create(_value->value.get()); } -Result Continuation::frame() const { - return Value::create(_value->frame.get()); +Result Continuation::frame() const { + return StackFrame::create((PodStackFrame*)_value->frame.get()); } Result Continuation::cmp(const Continuation& rhs) const { @@ -537,6 +541,79 @@ Result Continuation::cmp(const Continuation& rhs) const { return res; } +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(); + + return Task(TRY(MkGcRoot(pod))); +} + +Result Task::copy_value() const { + return Value(Task(TRY(_value.copy()))); +} +Result Task::copy() const { return Task(TRY(_value.copy())); } + +Result Task::name() const { + const char* task_name = + TRY(get_stdlib_task_name(StdlibTaskId(_value->task_id))); + return Symbol::create(task_name); +} + +uint64_t Task::task_id() const { return _value->task_id; } + +Result Task::params() const { + return Array::create((PodArray*)_value->params.get()); +} + +Result Task::cmp(const Task& rhs) const { + short res = (task_id() > rhs.task_id()) - (task_id() < rhs.task_id()); + if (res != 0) return res; + + auto lhs_params = TRY(params()); + auto rhs_params = TRY(rhs.params()); + res = TRY(lhs_params.cmp(rhs_params)); + return res; +} + +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(); + + return TaskResult(TRY(MkGcRoot(pod))); +} + +Result TaskResult::copy_value() const { + return Value(TaskResult(TRY(_value.copy()))); +} +Result TaskResult::copy() const { + return TaskResult(TRY(_value.copy())); +} + +Result TaskResult::result() const { + return Value::create(_value->result.get()); +} +Result TaskResult::error() const { + return Value::create(_value->error.get()); +} + +Result TaskResult::cmp(const TaskResult& rhs) const { + auto lhs_result = TRY(result()); + auto rhs_result = TRY(rhs.result()); + short res = TRY(lhs_result.cmp(rhs_result)); + if (res != 0) return res; + + auto lhs_error = TRY(error()); + auto rhs_error = TRY(rhs.error()); + res = TRY(lhs_error.cmp(rhs_error)); + return res; +} + Result Pair::create(const Value& first, const Value& rest) { auto pod = TRY(arena_alloc()); pod->header.tag = Tag::Pair; diff --git a/src/common.hpp b/src/common.hpp index 4631104..16aebc3 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -38,6 +38,8 @@ class Function; class StdlibFunction; class Module; class Continuation; +class Task; +class TaskResult; short cmp_tag(Tag lhs, Tag rhs); @@ -110,6 +112,12 @@ class Object { virtual Result cmp(const Continuation& rhs) const { return cmp_tag(tag(), Tag::Continuation); } + virtual Result cmp(const Task& rhs) const { + return cmp_tag(tag(), Tag::Task); + } + virtual Result cmp(const TaskResult& rhs) const { + return cmp_tag(tag(), Tag::TaskResult); + } virtual Result add(const Object&) const; virtual Result add(const Int64&) const; @@ -1239,7 +1247,7 @@ class Continuation : public Object { const StackFrame& frame); Result value() const; - Result frame() const; + Result frame() const; virtual Result copy_value() const final; Result copy() const; @@ -1248,6 +1256,69 @@ class Continuation : public Object { GcRoot _value; }; +class Task : public Object { + public: + Task() {} + Task(Task&& rhs) : _value(std::move(rhs._value)) {} + Task(GcRoot&& val) : _value(std::move(val)) {} + + virtual Tag tag() const final { return Tag::Task; } + virtual PodObject* pod() const final { return _value.get(); } + virtual Result cmp(const Object& rhs) const final { + return -TRY(rhs.cmp(*this)); + } + virtual Result cmp(const Task& rhs) const final; + + virtual void move(Object* obj) final { new (obj) Task(std::move(_value)); } + + static Result create(PodTask* obj) { return Task(TRY(MkGcRoot(obj))); } + + static Result create(uint64_t task_id, const Array& params); + + Result name() const; + uint64_t task_id() const; + Result params() const; + + virtual Result copy_value() const final; + Result copy() const; + + private: + GcRoot _value; +}; + +class TaskResult : public Object { + public: + TaskResult() {} + TaskResult(TaskResult&& rhs) : _value(std::move(rhs._value)) {} + TaskResult(GcRoot&& val) : _value(std::move(val)) {} + + virtual Tag tag() const final { return Tag::TaskResult; } + virtual PodObject* pod() const final { return _value.get(); } + virtual Result cmp(const Object& rhs) const final { + return -TRY(rhs.cmp(*this)); + } + virtual Result cmp(const TaskResult& rhs) const final; + + virtual void move(Object* obj) final { + new (obj) TaskResult(std::move(_value)); + } + + static Result create(PodTaskResult* obj) { + return TaskResult(TRY(MkGcRoot(obj))); + } + + static Result create(const Value& result, const Value& error); + + Result result() const; + Result error() const; + + virtual Result copy_value() const final; + Result copy() const; + + private: + GcRoot _value; +}; + // note: this class doesn't perform proper destruction of objects in some cases class Value { public: diff --git a/src/compiler.cpp b/src/compiler.cpp index ecc1c04..1357346 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -240,6 +240,8 @@ Result Compiler::compile_expr(Context& context, const Value& expr) { case Tag::StackFrame: case Tag::Error: case Tag::Continuation: + case Tag::Task: + case Tag::TaskResult: return ERROR(TypeMismatch); } return ERROR(TypeMismatch); diff --git a/src/error.hpp b/src/error.hpp index 83a0e79..bdd49d3 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -2,6 +2,7 @@ enum class ErrorCode { Success, + Unknown, OutOfMemory, IndexOutOfRange, TypeMismatch, diff --git a/src/pod.hpp b/src/pod.hpp index 6a12e21..2953121 100644 --- a/src/pod.hpp +++ b/src/pod.hpp @@ -26,6 +26,8 @@ enum class Tag : uint8_t { StackFrame, Error, Continuation, + Task, + TaskResult, }; template @@ -220,6 +222,22 @@ class PodContinuation final : public PodObject { OffPtr frame; }; +class PodTask final : public PodObject { + public: + PodTask() : PodObject(Tag::Task) {}; + + uint64_t task_id; + OffPtr params; +}; + +class PodTaskResult final : public PodObject { + public: + PodTaskResult() : PodObject(Tag::TaskResult) {}; + + OffPtr result; + OffPtr error; +}; + template class Ptr { public: diff --git a/src/result.hpp b/src/result.hpp index 2aa6407..ae3bb38 100644 --- a/src/result.hpp +++ b/src/result.hpp @@ -10,6 +10,7 @@ class Result { Result(const T& res) : _value(res), _error(ErrorCode::Success) {} Result(T&& res) : _value(std::move(res)), _error(ErrorCode::Success) {} Result(ErrorCode err) : _error(err) {} + Result() : _error(ErrorCode::Unknown) {} bool has_error() const { return _error != ErrorCode::Success; } bool has_value() const { return !has_error(); } diff --git a/src/stdlib.cpp b/src/stdlib.cpp index 4813289..311cac6 100644 --- a/src/stdlib.cpp +++ b/src/stdlib.cpp @@ -14,6 +14,14 @@ struct StdlibFunctionEntry { StdlibFunctionIdPtr fun_ptr; }; +typedef Result (*StdlibTaskIdPtr)(const Array& params); + +struct StdlibTaskEntry { + const char* name; + StdlibTaskId task_id; + StdlibTaskIdPtr task_ptr; +}; + Result stdlib_unknown(const StackFrame& stack) { return ERROR(NotImplemented); } @@ -290,7 +298,9 @@ Result stdlib_error(const StackFrame& stack) { auto val = Value(TRY( Error::create(*error_name.to(), *error_message.to()))); - return ERROR_OBJ(RuntimeError, val); + auto cont = Value(TRY(Continuation::create(val, stack))); + + return ERROR_OBJ(Raise, cont); } Result stdlib_raise(const StackFrame& stack) { @@ -315,6 +325,27 @@ Result stdlib_raise(const StackFrame& stack) { return ERROR_OBJ(Raise, cont); } +Result stdlib_task(const StackFrame& stack) { + auto params = TRY(stack.get(0)); + Array& params_array = *params.to(); + + auto size = TRY(params.size()); + if (size == 0) return ERROR(ArgumentCountMismatch); + + Value task_id = TRY(params_array.get(0)); + if (!task_id.is()) return ERROR(TypeMismatch); + + Value task_params = TRY(params_array.slice(1, size)); + + auto val = Value(TRY(Task::create((uint64_t)task_id.to()->value(), *task_params.to()))); + + auto res = TRY(stack.set(0, val)); + + res = TRY(res.ret(0)); + + return res; +} + #define STDLIB_FUNCTION(name, id) \ [(uint64_t)StdlibFunctionId::id] = {#name, StdlibFunctionId::id, \ stdlib_##name} @@ -335,10 +366,30 @@ static StdlibFunctionEntry function_entries[] = { STDLIB_FUNCTION(map, Map), STDLIB_FUNCTION(error, Error), STDLIB_FUNCTION(raise, Raise), + STDLIB_FUNCTION(task, Task), [(uint64_t)StdlibFunctionId::Max] = {0, StdlibFunctionId::Max, stdlib_unknown}, }; +Result stdlib_task_unknown(const Array& params) { + return ERROR(NotImplemented); +} + +Result stdlib_task_print(const Array& params) { + return ERROR(NotImplemented); +} + +#define STDLIB_TASK(name, id) \ + [(uint64_t)StdlibTaskId::id] = {#name, StdlibTaskId::id, \ + stdlib_task_##name} + +static StdlibTaskEntry task_entries[] = { + STDLIB_TASK(unknown, Unknown), + STDLIB_TASK(print, Print), + [(uint64_t)StdlibTaskId::Max] = {0, StdlibTaskId::Max, + stdlib_task_unknown}, +}; + // TODO: this just scans through the array of functions linearly every time. // It doesn't have much effect at runtime, because such lookup is likely to // happen on compile-time only. But anyway, this is worth speeding up @@ -389,3 +440,51 @@ Result get_stdlib_function_name(StdlibFunctionId fun_id) { return function_entries[(uint64_t)fun_id].name; } + + +StdlibTaskEntry get_task_entry(const char* name) { + for (uint64_t i = 1; i < (uint64_t)StdlibTaskId::Max; i++) { + if (strcmp(task_entries[i].name, name) == 0) { + return task_entries[i]; + } + } + return task_entries[(uint64_t)StdlibTaskId::Unknown]; +} + +Result get_stdlib_task(const Symbol& name) { + const uint64_t bufsize = 256; + char buf[bufsize]; + auto size = TRY(name.size()); + if (size + 1 > bufsize) { + return ERROR(KeyError); + } + + for (uint64_t i = 0; i < size; i++) { + buf[i] = (char)TRY(name[i]); + } + buf[size] = 0; + + StdlibTaskId task_id = get_task_entry(buf).task_id; + if (task_id == StdlibTaskId::Unknown) { + return ERROR(KeyError); + } + + return task_id; +} + +Result call_stdlib_task(StdlibTaskId fun_id, + const Array& params) { + if (fun_id == StdlibTaskId(0) || fun_id >= StdlibTaskId::Max) { + return ERROR(KeyError); + } + + return task_entries[(uint64_t)fun_id].task_ptr(params); +} + +Result get_stdlib_task_name(StdlibTaskId fun_id) { + if (fun_id == StdlibTaskId(0) || fun_id >= StdlibTaskId::Max) { + return ERROR(KeyError); + } + + return task_entries[(uint64_t)fun_id].name; +} diff --git a/src/stdlib.hpp b/src/stdlib.hpp index 2248a1d..ae16ce4 100644 --- a/src/stdlib.hpp +++ b/src/stdlib.hpp @@ -20,6 +20,13 @@ enum class StdlibFunctionId : uint64_t { Map, Error, Raise, + Task, + Max, +}; + +enum class StdlibTaskId : uint64_t { + Unknown, + Print, Max, }; @@ -27,8 +34,13 @@ class Value; class Symbol; class Array; class StackFrame; +class Continuation; Result get_stdlib_function_name(StdlibFunctionId fun_id); Result get_stdlib_function(const Symbol& name); Result call_stdlib_function(StdlibFunctionId fun_id, const StackFrame& stack); + +Result get_stdlib_task_name(StdlibTaskId task_id); +Result get_stdlib_task(const Symbol& name); +Result call_stdlib_task(StdlibTaskId task_id, const Array& params); diff --git a/src/vm.cpp b/src/vm.cpp index 0c9a690..d1dd962 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -1,6 +1,7 @@ #include "vm.hpp" #include "common.hpp" +#include "error.hpp" #include "stdlib.hpp" Result VM::getreg(uint64_t idx) { return _stack.get(idx); } @@ -279,6 +280,19 @@ Result VM::step_bytecode() { return Result(); } +Result VM::handle_raise(const Continuation& cont) { + // TODO: this should do proper stack unwinding + auto value = TRY(cont.value()); + + std::cout << "Handle raise\n"; + + if (value.is()) { + auto obj = TRY(cont.copy_value()); + return ERROR_OBJ(Raise, obj); + } + return ERROR(TypeMismatch); +} + Result VM::step_native() { auto fun = TRY(_stack.fun()); if (!fun.is()) return ERROR(TypeMismatch); @@ -290,13 +304,32 @@ Result VM::step_native() { Result VM::step() { auto fun = TRY(_stack.fun()); + Result res; if (fun.is()) { - return step_bytecode(); + res = std::move(step_bytecode()); } else if (fun.is()) { - return step_native(); + res = std::move(step_native()); } else { return ERROR(NotImplemented); } + + if (res.has_error()) { + auto obj = TRY(geterrobj().copy()); + if (obj.is()) return res; + + if (res.error() == ErrorCode::Raise) { + if (obj.is()) { + _stack = TRY(handle_raise(*obj.to())); + return Result(); + } + } + + auto cont = Value(TRY(Continuation::create(obj, _stack))); + _stack = TRY(handle_raise(*obj.to())); + return Result(); + } + + return res; } Result VM::run() { diff --git a/src/vm.hpp b/src/vm.hpp index 64180fc..1b20728 100644 --- a/src/vm.hpp +++ b/src/vm.hpp @@ -62,6 +62,8 @@ class VM { Result vm_global_load(Opcode& oc); Result vm_global_store(Opcode& oc); + Result handle_raise(const Continuation& cont); + Result get(bool is_const, uint64_t idx); Result getconst(uint64_t idx); Result getreg(uint64_t idx); diff --git a/src/writer.cpp b/src/writer.cpp index 5a3a5bf..492f7c0 100644 --- a/src/writer.cpp +++ b/src/writer.cpp @@ -45,6 +45,10 @@ Result Writer::write_one(const Value& obj) { return write_error(*obj.to()); case Tag::Continuation: return write_continuation(*obj.to()); + case Tag::Task: + return write_task(*obj.to()); + case Tag::TaskResult: + return write_task_result(*obj.to()); }; return String(); } @@ -403,6 +407,39 @@ Result Writer::write_continuation(const Continuation& val) { return TRY(String::create("#")); } +Result Writer::write_task(const Task& val) { + auto name = TRY(val.name()); + String name_str = TRY(write_symbol(name)); + String params_str = TRY(write_one(TRY(val.params()))); + + String res = TRY(String::create("#")); + return res; +} + +Result Writer::write_task_result(const TaskResult& val) { + auto error = TRY(val.error()); + auto result = TRY(val.result()); + + String res = TRY(String::create("#()) { + res = TRY(res.concat(" :result ")); + String s = TRY(write_one(result)); + res = TRY(res.concat(s)); + } else { + res = TRY(res.concat(" :error ")); + String s = TRY(write_one(error)); + res = TRY(res.concat(s)); + } + + res = TRY(res.concat(">")); + return res; +} + Result write_one(const Value& value) { auto w = Writer(); return w.write_one(value); diff --git a/src/writer.hpp b/src/writer.hpp index e5d44b7..e7421b2 100644 --- a/src/writer.hpp +++ b/src/writer.hpp @@ -30,6 +30,8 @@ class Writer { Result write_stack(const StackFrame& val); Result write_error(const Error& val); Result write_continuation(const Continuation& val); + Result write_task(const Task& val); + Result write_task_result(const TaskResult& val); }; Result write_one(const Value& value);