#include "stdlib.hpp" #include "common.hpp" #include "pod.hpp" #include "sourcerange.hpp" #include "writer.hpp" typedef Result (*StdlibFunctionIdPtr)(const StackFrame& stack); struct StdlibFunctionEntry { const char* name; StdlibFunctionId fun_id; StdlibFunctionIdPtr fun_ptr; }; Result stdlib_unknown(const StackFrame& stack) { return ERROR(NotImplemented); } Result stdlib_print(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 (i != 0) { std::cout << " "; } if (param.is()) { TRY(print_string(*param.to())); } else { auto s = TRY(write_one(param)); TRY(print_string(s)); } } auto nil = Value(TRY(Nil::create())); auto res = TRY(stack.set(0, nil)); return res.ret(0); } Result stdlib_println(const StackFrame& stack) { auto res = TRY(stdlib_print(stack)); std::cout << "\n"; return res; } Result stdlib_prn(const StackFrame& stack) { return ERROR(NotImplemented); } Result 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()) return ERROR(AssertionFailed); auto v = param.to()->value(); if (!v) return ERROR(AssertionFailed); } auto nil = Value(TRY(Nil::create())); auto res = TRY(stack.set(0, nil)); return res.ret(0); } Result stdlib_dict(const StackFrame& stack) { auto params = TRY(stack.get(0)); if (!params.is()) return ERROR(TypeMismatch); Value d = TRY(Dict::create(*params.to())); auto res = TRY(stack.set(0, d)); return res.ret(0); } Result stdlib_list(const StackFrame& stack) { auto params = TRY(stack.get(0)); if (!params.is()) return ERROR(TypeMismatch); Value d = TRY(Pair::create(*params.to())); auto res = TRY(stack.set(0, d)); return res.ret(0); } Result stdlib_array(const StackFrame& stack) { auto params = TRY(stack.get(0)); auto res = TRY(stack.set(0, params)); return res.ret(0); } Result 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)); return res.ret(0); } Result 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()) return ERROR(TypeMismatch); int64_t intval = val.to()->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)); return res.ret(0); } Result 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)); return res.ret(0); } #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(assert, Assert), STDLIB_FUNCTION(dict, Dict), STDLIB_FUNCTION(list, List), STDLIB_FUNCTION(array, Array), STDLIB_FUNCTION(get, Get), STDLIB_FUNCTION(srcloc, SrcLoc), STDLIB_FUNCTION(size, Size), [(uint64_t)StdlibFunctionId::Max] = {0, StdlibFunctionId::Max, stdlib_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 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 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 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; }