650 lines
17 KiB
C++
650 lines
17 KiB
C++
#include "stdlib.hpp"
|
|
|
|
#include "common.hpp"
|
|
#include "error.hpp"
|
|
#include "fio.hpp"
|
|
#include "pod.hpp"
|
|
#include "sourcerange.hpp"
|
|
#include "writer.hpp"
|
|
|
|
typedef Result<StackFrame> (*StdlibFunctionIdPtr)(const StackFrame& stack);
|
|
|
|
struct StdlibFunctionEntry {
|
|
const char* name;
|
|
StdlibFunctionId fun_id;
|
|
StdlibFunctionIdPtr fun_ptr;
|
|
};
|
|
|
|
typedef Result<Value> (*StdlibTaskIdPtr)(const Array& params);
|
|
|
|
struct StdlibTaskEntry {
|
|
const char* name;
|
|
StdlibTaskId task_id;
|
|
StdlibTaskIdPtr task_ptr;
|
|
};
|
|
|
|
Result<StackFrame> raise_task(const StackFrame& stack, StdlibTaskId task_id,
|
|
const Array& params) {
|
|
auto val = Value(TRY(Task::create((uint64_t)task_id, params)));
|
|
auto cont = Value(TRY(Continuation::create(val, stack)));
|
|
|
|
return ERROR_OBJ(Raise, cont);
|
|
}
|
|
|
|
Result<StackFrame> raise_task(const StackFrame& stack, StdlibTaskId task_id,
|
|
const Value& param) {
|
|
Array params = TRY(Array::create());
|
|
params = TRY(params.append(param));
|
|
auto val = Value(TRY(Task::create((uint64_t)task_id, params)));
|
|
auto cont = Value(TRY(Continuation::create(val, stack)));
|
|
|
|
return ERROR_OBJ(Raise, cont);
|
|
}
|
|
|
|
Result<StackFrame> task_return(const StackFrame& stack, uint64_t idx) {
|
|
auto result = TRY(stack.get(idx));
|
|
if (!result.is<TaskResult>()) return ERROR(TypeMismatch);
|
|
|
|
const auto& task_result = *result.to<TaskResult>();
|
|
|
|
auto error = TRY(task_result.error());
|
|
|
|
if (!error.is<Nil>()) {
|
|
return ERROR_OBJ(TaskFailed, error);
|
|
}
|
|
|
|
auto res = TRY(stack.set(0, TRY(task_result.result())));
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_unknown(const StackFrame& stack) {
|
|
return ERROR(NotImplemented);
|
|
}
|
|
|
|
Result<String> tostring(const Value& params, bool newline = false) {
|
|
auto size = TRY(params.size());
|
|
String str = TRY(String::create(""));
|
|
|
|
for (uint64_t i = 0; i < size; i++) {
|
|
Value param = TRY(params.get(i));
|
|
|
|
if (i != 0) {
|
|
str = TRY(str.concat(" "));
|
|
}
|
|
if (param.is<String>()) {
|
|
str = TRY(str.concat(*param.to<String>()));
|
|
} else {
|
|
auto s = TRY(write_one(param));
|
|
str = TRY(str.concat(s));
|
|
}
|
|
}
|
|
|
|
if (newline) {
|
|
str = TRY(str.concat("\n"));
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_print(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto stack_size = TRY(stack.size());
|
|
|
|
if (stack_size > 1) {
|
|
return task_return(stack, 1);
|
|
}
|
|
|
|
auto str = Value(TRY(tostring(params)));
|
|
|
|
return raise_task(stack, StdlibTaskId::Print, str);
|
|
}
|
|
|
|
Result<StackFrame> stdlib_println(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto stack_size = TRY(stack.size());
|
|
|
|
if (stack_size > 1) {
|
|
return task_return(stack, 1);
|
|
}
|
|
|
|
auto str = Value(TRY(tostring(params, true)));
|
|
return raise_task(stack, StdlibTaskId::Print, str);
|
|
}
|
|
|
|
Result<StackFrame> stdlib_prn(const StackFrame& stack) {
|
|
return ERROR(NotImplemented);
|
|
}
|
|
|
|
Result<StackFrame> stdlib_slurp(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto stack_size = TRY(stack.size());
|
|
|
|
if (stack_size > 1) {
|
|
return task_return(stack, 1);
|
|
}
|
|
|
|
if (TRY(params.size()) != 1) {
|
|
return ERROR(ArgumentCountMismatch);
|
|
}
|
|
|
|
auto fname = TRY(params.get(0));
|
|
return raise_task(stack, StdlibTaskId::ReadFile, fname);
|
|
}
|
|
|
|
Result<StackFrame> stdlib_spit(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto stack_size = TRY(stack.size());
|
|
|
|
if (stack_size > 1) {
|
|
return task_return(stack, 1);
|
|
}
|
|
|
|
if (TRY(params.size()) != 2) {
|
|
return ERROR(ArgumentCountMismatch);
|
|
}
|
|
|
|
if (!params.is<Array>()) {
|
|
return ERROR(TypeMismatch);
|
|
}
|
|
return raise_task(stack, StdlibTaskId::WriteFile, *params.to<Array>());
|
|
}
|
|
|
|
Result<StackFrame> stdlib_assert(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto size = TRY(params.size());
|
|
for (uint64_t i = 0; i < size; i++) {
|
|
Value param = TRY(params.get(i));
|
|
if (!param.is<Bool>()) return ERROR(AssertionFailed);
|
|
auto v = param.to<Bool>()->value();
|
|
if (!v) return ERROR(AssertionFailed);
|
|
}
|
|
|
|
auto nil = Value(TRY(Nil::create()));
|
|
auto res = TRY(stack.set(0, nil));
|
|
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_dict(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
if (!params.is<Array>()) return ERROR(TypeMismatch);
|
|
Value d = TRY(Dict::create(*params.to<Array>()));
|
|
|
|
auto res = TRY(stack.set(0, d));
|
|
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_list(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
if (!params.is<Array>()) return ERROR(TypeMismatch);
|
|
Value d = TRY(Pair::create(*params.to<Array>()));
|
|
|
|
auto res = TRY(stack.set(0, d));
|
|
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_array(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
|
|
auto res = TRY(stack.set(0, params));
|
|
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_get(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto size = TRY(params.size());
|
|
if (size != 2) return ERROR(ArgumentCountMismatch);
|
|
|
|
Value collection = TRY(params.get(0));
|
|
Value key = TRY(params.get(1));
|
|
|
|
auto val = TRY(collection.get(key));
|
|
|
|
auto res = TRY(stack.set(0, val));
|
|
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_set(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto size = TRY(params.size());
|
|
if (size != 3) return ERROR(ArgumentCountMismatch);
|
|
|
|
Value collection = TRY(params.get(0));
|
|
Value key = TRY(params.get(1));
|
|
Value value = TRY(params.get(2));
|
|
|
|
collection = TRY(collection.set(key, value));
|
|
|
|
auto res = TRY(stack.set(0, collection));
|
|
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_srcloc(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto size = TRY(params.size());
|
|
if (size != 6) return ERROR(ArgumentCountMismatch);
|
|
|
|
SourceRange sr;
|
|
for (uint64_t i = 0; i < 6; i++) {
|
|
auto val = TRY(params.get(i));
|
|
if (!val.is<Int64>()) return ERROR(TypeMismatch);
|
|
int64_t intval = val.to<Int64>()->value();
|
|
switch (i) {
|
|
case 0:
|
|
sr.start.line = intval;
|
|
break;
|
|
case 1:
|
|
sr.start.column = intval;
|
|
break;
|
|
case 2:
|
|
sr.start.offset = intval;
|
|
break;
|
|
case 3:
|
|
sr.end.line = intval;
|
|
break;
|
|
case 4:
|
|
sr.end.column = intval;
|
|
break;
|
|
case 5:
|
|
sr.end.offset = intval;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto val = Value(TRY(SrcLoc::create(sr)));
|
|
|
|
auto res = TRY(stack.set(0, val));
|
|
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_size(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto size = TRY(params.size());
|
|
|
|
if (size != 1) return ERROR(ArgumentCountMismatch);
|
|
|
|
auto param = TRY(params.get(0));
|
|
auto psize = TRY(param.size());
|
|
|
|
auto val = Value(TRY(Int64::create((int64_t)psize)));
|
|
|
|
auto res = TRY(stack.set(0, val));
|
|
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_map(const StackFrame& stack_param) {
|
|
auto stack = TRY(stack_param.copy());
|
|
auto params = TRY(stack.get(0));
|
|
auto stack_size = TRY(stack.size());
|
|
|
|
auto fun = TRY(params.get(1));
|
|
|
|
if (stack_size == 1) {
|
|
// This is the first call, need to set up arguments on the stack
|
|
auto collection = TRY(params.get(0));
|
|
|
|
auto num_params = TRY(params.size());
|
|
if (num_params != 2) return ERROR(ArgumentCountMismatch);
|
|
|
|
if (!collection.is<Pair>()) return ERROR(TypeMismatch);
|
|
|
|
// Result accumulator will contain nil value initially
|
|
auto nil = Value(TRY(Nil::create()));
|
|
stack = TRY(stack.set(1, nil));
|
|
|
|
// Remaining elements in the collection
|
|
stack = TRY(stack.set(2, TRY(collection.rest())));
|
|
|
|
stack = TRY(stack.set(3, fun));
|
|
stack = TRY(stack.set(4, TRY(collection.first())));
|
|
|
|
return TRY(stack.call(fun, 3, 5));
|
|
}
|
|
|
|
auto accumulator = TRY(stack.get(1));
|
|
auto remaining = TRY(stack.get(2));
|
|
auto fun_result = TRY(stack.get(3));
|
|
|
|
accumulator = Value(TRY(Pair::create(fun_result, accumulator)));
|
|
|
|
stack = TRY(stack.set(1, accumulator));
|
|
|
|
if (!remaining.is<Nil>()) {
|
|
stack = TRY(stack.set(2, TRY(remaining.rest())));
|
|
stack = TRY(stack.set(3, fun));
|
|
stack = TRY(stack.set(4, TRY(remaining.first())));
|
|
|
|
return TRY(stack.call(fun, 3, 5));
|
|
}
|
|
|
|
accumulator = TRY(reverse(accumulator));
|
|
|
|
auto res = TRY(stack.set(0, accumulator));
|
|
res = TRY(res.ret(0));
|
|
|
|
return res;
|
|
}
|
|
|
|
Result<StackFrame> stdlib_error(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
auto size = TRY(params.size());
|
|
if (size == 0 || size > 2) return ERROR(ArgumentCountMismatch);
|
|
|
|
Value error_name;
|
|
Value error_message;
|
|
|
|
if (size == 2) {
|
|
error_name = TRY(params.get(0));
|
|
error_message = TRY(params.get(1));
|
|
} else {
|
|
auto param = TRY(params.get(0));
|
|
if (param.is<Symbol>()) {
|
|
error_name = std::move(param);
|
|
error_message = TRY(String::create(""));
|
|
} else if (param.is<String>()) {
|
|
error_name = TRY(Symbol::create("error"));
|
|
error_message = std::move(param);
|
|
} else {
|
|
return ERROR(TypeMismatch);
|
|
}
|
|
}
|
|
|
|
if (!error_name.is<Symbol>() || !error_message.is<String>())
|
|
return ERROR(TypeMismatch);
|
|
|
|
auto val = Value(TRY(
|
|
Error::create(*error_name.to<Symbol>(), *error_message.to<String>())));
|
|
|
|
auto cont = Value(TRY(Continuation::create(val, stack)));
|
|
|
|
return ERROR_OBJ(Raise, cont);
|
|
}
|
|
|
|
Result<StackFrame> stdlib_raise(const StackFrame& stack) {
|
|
auto stack_size = TRY(stack.size());
|
|
|
|
if (stack_size > 1) {
|
|
return task_return(stack, 1);
|
|
}
|
|
|
|
auto params = TRY(stack.get(0));
|
|
auto size = TRY(params.size());
|
|
if (size != 1) return ERROR(ArgumentCountMismatch);
|
|
|
|
auto val = TRY(params.get(0));
|
|
|
|
auto cont = Value(TRY(Continuation::create(val, stack)));
|
|
|
|
return ERROR_OBJ(Raise, cont);
|
|
}
|
|
|
|
Result<StackFrame> stdlib_task(const StackFrame& stack) {
|
|
auto params = TRY(stack.get(0));
|
|
Array& params_array = *params.to<Array>();
|
|
|
|
auto size = TRY(params.size());
|
|
if (size == 0) return ERROR(ArgumentCountMismatch);
|
|
|
|
Value task_id = TRY(params_array.get(0));
|
|
uint64_t num_task_id = 0;
|
|
|
|
if (task_id.is<Int64>()) {
|
|
num_task_id = (uint64_t)task_id.to<Int64>()->value();
|
|
} else if (task_id.is<Symbol>()) {
|
|
num_task_id = (uint64_t)TRY(get_stdlib_task(*task_id.to<Symbol>()));
|
|
} else {
|
|
return ERROR(TypeMismatch);
|
|
}
|
|
|
|
Value task_params = TRY(params_array.slice(1, size));
|
|
|
|
auto val = Value(TRY(Task::create(num_task_id, *task_params.to<Array>())));
|
|
|
|
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}
|
|
|
|
static StdlibFunctionEntry function_entries[] = {
|
|
STDLIB_FUNCTION(unknown, Unknown),
|
|
STDLIB_FUNCTION(print, Print),
|
|
STDLIB_FUNCTION(println, PrintLn),
|
|
STDLIB_FUNCTION(prn, Prn),
|
|
STDLIB_FUNCTION(slurp, Slurp),
|
|
STDLIB_FUNCTION(spit, Spit),
|
|
STDLIB_FUNCTION(assert, Assert),
|
|
STDLIB_FUNCTION(dict, Dict),
|
|
STDLIB_FUNCTION(list, List),
|
|
STDLIB_FUNCTION(array, Array),
|
|
STDLIB_FUNCTION(get, Get),
|
|
STDLIB_FUNCTION(set, Set),
|
|
STDLIB_FUNCTION(srcloc, SrcLoc),
|
|
STDLIB_FUNCTION(size, Size),
|
|
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<Value> stdlib_task_unknown(const Array& params) {
|
|
return ERROR(NotImplemented);
|
|
}
|
|
|
|
Result<Value> stdlib_task_print(const Array& params) {
|
|
auto size = TRY(params.size());
|
|
|
|
if (size != 1) {
|
|
auto err = Value(TRY(
|
|
Error::create("argument-count-mismatch", "Argument count mismatch")));
|
|
return ERROR_OBJ(ArgumentCountMismatch, err);
|
|
}
|
|
|
|
auto obj = TRY(params.get(0));
|
|
|
|
if (!obj.is<String>()) {
|
|
auto err = Value(TRY(Error::create("type-mismatch", "Type mismatch")));
|
|
return ERROR_OBJ(TypeMismatch, err);
|
|
}
|
|
|
|
print_string(*obj.to<String>());
|
|
return Value(TRY(Nil::create()));
|
|
}
|
|
|
|
Result<Value> stdlib_task_read_file(const Array& params) {
|
|
auto size = TRY(params.size());
|
|
|
|
if (size != 1) {
|
|
auto err = Value(TRY(
|
|
Error::create("argument-count-mismatch", "Argument count mismatch")));
|
|
return ERROR_OBJ(ArgumentCountMismatch, err);
|
|
}
|
|
|
|
auto obj = TRY(params.get(0));
|
|
|
|
if (!obj.is<String>()) {
|
|
auto err = Value(TRY(Error::create("type-mismatch", "Type mismatch")));
|
|
return ERROR_OBJ(TypeMismatch, err);
|
|
}
|
|
|
|
String& fname = *obj.to<String>();
|
|
ByteArray fname_bytes = TRY(ByteArray::create(fname));
|
|
auto contents_bytes = TRY(read_file(fname_bytes));
|
|
auto contents = Value(TRY(String::create(contents_bytes)));
|
|
return contents;
|
|
}
|
|
|
|
Result<Value> stdlib_task_write_file(const Array& params) {
|
|
auto size = TRY(params.size());
|
|
|
|
if (size != 2) {
|
|
auto err = Value(TRY(
|
|
Error::create("argument-count-mismatch", "Argument count mismatch")));
|
|
return ERROR_OBJ(ArgumentCountMismatch, err);
|
|
}
|
|
|
|
auto fname = TRY(params.get(0));
|
|
|
|
if (!fname.is<String>()) {
|
|
auto err = Value(TRY(Error::create("type-mismatch", "Type mismatch")));
|
|
return ERROR_OBJ(TypeMismatch, err);
|
|
}
|
|
|
|
auto contents = TRY(params.get(1));
|
|
|
|
if (!contents.is<String>()) {
|
|
auto err = Value(TRY(Error::create("type-mismatch", "Type mismatch")));
|
|
return ERROR_OBJ(TypeMismatch, err);
|
|
}
|
|
|
|
String& fname_string = *fname.to<String>();
|
|
ByteArray fname_bytes = TRY(ByteArray::create(fname_string));
|
|
|
|
String& contents_string = *contents.to<String>();
|
|
ByteArray contents_bytes = TRY(ByteArray::create(contents_string));
|
|
|
|
TRY(write_file(fname_bytes, contents_bytes));
|
|
return Value(TRY(Nil::create()));
|
|
}
|
|
|
|
#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),
|
|
STDLIB_TASK(read_file, ReadFile),
|
|
STDLIB_TASK(write_file, WriteFile),
|
|
[(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
|
|
// eventually.
|
|
StdlibFunctionEntry get_function_entry(const char* name) {
|
|
for (uint64_t i = 1; i < (uint64_t)StdlibFunctionId::Max; i++) {
|
|
if (strcmp(function_entries[i].name, name) == 0) {
|
|
return function_entries[i];
|
|
}
|
|
}
|
|
return function_entries[(uint64_t)StdlibFunctionId::Unknown];
|
|
}
|
|
|
|
Result<StdlibFunctionId> get_stdlib_function(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;
|
|
|
|
StdlibFunctionId fun_id = get_function_entry(buf).fun_id;
|
|
if (fun_id == StdlibFunctionId::Unknown) {
|
|
return ERROR(KeyError);
|
|
}
|
|
|
|
return fun_id;
|
|
}
|
|
|
|
Result<StackFrame> call_stdlib_function(StdlibFunctionId fun_id,
|
|
const StackFrame& stack) {
|
|
if (fun_id == StdlibFunctionId(0) || fun_id >= StdlibFunctionId::Max) {
|
|
return ERROR(KeyError);
|
|
}
|
|
|
|
return function_entries[(uint64_t)fun_id].fun_ptr(stack);
|
|
}
|
|
|
|
Result<const char*> get_stdlib_function_name(StdlibFunctionId fun_id) {
|
|
if (fun_id == StdlibFunctionId(0) || fun_id >= StdlibFunctionId::Max) {
|
|
return ERROR(KeyError);
|
|
}
|
|
|
|
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<StdlibTaskId> 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<Value> 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<const char*> 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;
|
|
}
|