From 9686b1fb75590b084fe2fc984c9c568fa188714a Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Thu, 26 May 2022 08:30:28 +0530 Subject: [PATCH] Register builtin function implementation --- src/core/public.c | 36 +++++++++++++++++++++++++++--------- src/core/value.c | 2 +- src/core/vm.c | 9 +++++---- src/core/vm.h | 2 +- src/include/pocketlang.h | 6 ++++++ src/libs/std_io.c | 20 ++++++++++++++++++++ 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/core/public.c b/src/core/public.c index 5ed4124..c7938fb 100644 --- a/src/core/public.c +++ b/src/core/public.c @@ -177,6 +177,30 @@ void pkSetUserData(PKVM* vm, void* user_data) { vm->config.user_data = user_data; } +void pkRegisterBuiltinFn(PKVM* vm, const char* name, pkNativeFn fn, + int arity, const char* docstring) { + ASSERT(vm->builtins_count < BUILTIN_FN_CAPACITY, + "Maximum builtin function limit reached, To increase the limit set " + "BUILTIN_FN_CAPACITY and recompile."); + + // TODO: if the functions are sorted, we can do a binary search, but builtin + // functions are not searched at runtime, just looked up using it's index + // O(1) However it'll decrease the compile time. + for (int i = 0; i < vm->builtins_count; i++) { + Closure* bfn = vm->builtins_funcs[i]; + ASSERT(strcmp(bfn->fn->name, name) != 0, + "Overriding existing function not supported yet."); + } + + Function* fptr = newFunction(vm, name, (int) strlen(name), NULL, + true, docstring, NULL); + vmPushTempRef(vm, &fptr->_super); // fptr. + fptr->native = fn; + fptr->arity = arity; + vm->builtins_funcs[vm->builtins_count++] = newClosure(vm, fptr); + vmPopTempRef(vm); // fptr. +} + PkHandle* pkNewModule(PKVM* vm, const char* name) { CHECK_ARG_NULL(name); Module* module = newModuleInternal(vm, name); @@ -260,14 +284,8 @@ void pkClassAddMethod(PKVM* vm, PkHandle* cls, vmPopTempRef(vm); // fn. vmPushTempRef(vm, &method->_super); // method. { - if (strcmp(name, CTOR_NAME) == 0) { - class_->ctor = method; - - } else { - vmPushTempRef(vm, &method->_super); // method. - pkClosureBufferWrite(&class_->methods, vm, method); - vmPopTempRef(vm); // method. - } + pkClosureBufferWrite(&class_->methods, vm, method); + if (!strcmp(name, CTOR_NAME)) class_->ctor = method; } vmPopTempRef(vm); // method. } @@ -670,7 +688,7 @@ bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val) { void pkReserveSlots(PKVM* vm, int count) { if (vm->fiber == NULL) vm->fiber = newFiber(vm, NULL); int needed = (int)(vm->fiber->ret - vm->fiber->stack) + count; - vmEnsureStackSize(vm, needed); + vmEnsureStackSize(vm, vm->fiber, needed); } int pkGetSlotsCount(PKVM* vm) { diff --git a/src/core/value.c b/src/core/value.c index 2b78862..8a18dbc 100644 --- a/src/core/value.c +++ b/src/core/value.c @@ -1506,7 +1506,7 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff, for (uint32_t i = 0; i < str->length; i++) { char c = str->data[i]; switch (c) { - case '"': pkByteBufferAddString(buff, vm, "\\\"", 2); break; + case '"': pkByteBufferAddString(buff, vm, "\\\"", 2); break; case '\\': pkByteBufferAddString(buff, vm, "\\\\", 2); break; case '\n': pkByteBufferAddString(buff, vm, "\\n", 2); break; case '\r': pkByteBufferAddString(buff, vm, "\\r", 2); break; diff --git a/src/core/vm.c b/src/core/vm.c index e0d9826..8861f51 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -231,6 +231,8 @@ bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var* argv) { ASSERT(fiber->stack != NULL && fiber->sp == fiber->stack + 1, OOPS); ASSERT(fiber->ret == fiber->stack, OOPS); + + vmEnsureStackSize(vm, fiber, (int) (fiber->sp - fiber->stack) + argc); ASSERT((fiber->stack + fiber->stack_size) - fiber->sp >= argc, OOPS); // Pass the function arguments. @@ -454,14 +456,13 @@ Var vmImportModule(PKVM* vm, String* from, String* path) { return VAR_OBJ(module); } -void vmEnsureStackSize(PKVM* vm, int size) { +void vmEnsureStackSize(PKVM* vm, Fiber* fiber, int size) { if (size >= (MAX_STACK_SIZE / sizeof(Var))) { VM_SET_ERROR(vm, newString(vm, "Maximum stack limit reached.")); return; } - Fiber* fiber = vm->fiber; if (fiber->stack_size >= size) return; int new_size = utilPowerOf2Ceil(size); @@ -524,7 +525,7 @@ static inline void pushCallFrame(PKVM* vm, const Closure* closure) { // Grow the stack if needed. int current_stack_slots = (int)(vm->fiber->sp - vm->fiber->stack) + 1; int needed = closure->fn->fn->stack_size + current_stack_slots; - vmEnsureStackSize(vm, needed); + vmEnsureStackSize(vm, vm->fiber, needed); CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++; frame->rbp = vm->fiber->ret; @@ -567,7 +568,7 @@ static inline void reuseCallFrame(PKVM* vm, const Closure* closure) { // Grow the stack if needed (least probably). int needed = (closure->fn->fn->stack_size + (int)(vm->fiber->sp - vm->fiber->stack)); - vmEnsureStackSize(vm, needed); + vmEnsureStackSize(vm, vm->fiber, needed); } // Capture the [local] into an upvalue and return it. If the upvalue already diff --git a/src/core/vm.h b/src/core/vm.h index 605bc0e..ca0ccdf 100644 --- a/src/core/vm.h +++ b/src/core/vm.h @@ -147,7 +147,7 @@ PkHandle* vmNewHandle(PKVM* vm, Var value); // If the stack size is less than [size], the stack will grow to keep more // values on it. -void vmEnsureStackSize(PKVM* vm, int size); +void vmEnsureStackSize(PKVM* vm, Fiber* fiber, int size); // Trigger garbage collection. This is an implementation of mark and sweep // garbage collection (https://en.wikipedia.org/wiki/Tracing_garbage_collection). diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index 1d311af..328af25 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -212,6 +212,12 @@ PK_PUBLIC void pkSetUserData(PKVM* vm, void* user_data); // Returns the associated user data. PK_PUBLIC void* pkGetUserData(const PKVM* vm); +// Register a new builtin function with the given [name]. [docstring] could be +// NULL or will always valid pointer since PKVM doesn't allocate a string for +// docstrings. +PK_PUBLIC void pkRegisterBuiltinFn(PKVM* vm, const char* name, pkNativeFn fn, + int arity, const char* docstring); + // Invoke pocketlang's allocator directly. This function should be called // when the host application want to send strings to the PKVM that are claimed // by the VM once the caller returned it. For other uses you **should** call diff --git a/src/libs/std_io.c b/src/libs/std_io.c index 433ef1a..36db0a0 100644 --- a/src/libs/std_io.c +++ b/src/libs/std_io.c @@ -402,12 +402,32 @@ DEF(_fileTell, "") { pkSetSlotNumber(vm, 0, (double) ftell(file->fp)); } +// open(path, mode='r') is equal to: +// +// from io import File +// return File().open(path, mode) +DEF(_open, NULL /* == _fileOpen */) { + + // slots[1] = path + // slots[2] = mode + int argc = pkGetArgc(vm); + if (!pkCheckArgcRange(vm, argc, 1, 2)) return; + if (argc == 1) pkSetSlotString(vm, 2, "r"); + + if (!pkImportModule(vm, "io", 0)) return; // slots[0] = io + if (!pkGetAttribute(vm, 0, "File", 0)) return; // slots[0] = File + if (!pkNewInstance(vm, 0, 0, 0, 0)) return; // slots[0] = File() + if (!pkCallMethod(vm, 0, "open", 2, 1, -1)) return; // slots[0] = opened file +} + /*****************************************************************************/ /* MODULE REGISTER */ /*****************************************************************************/ void registerModuleIO(PKVM* vm) { + pkRegisterBuiltinFn(vm, "open", _open, -1, DOCSTRING(_fileOpen)); + PkHandle* io = pkNewModule(vm, "io"); pkReserveSlots(vm, 2);