diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index 8cb55fc..c742d25 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -328,9 +328,7 @@ struct PkCompileOptions { bool debug; // Set to true if compiling in REPL mode, This will print repr version of - // each evaluated non-null values. Note that if [repl_mode] is true the - // [expression] should also be true otherwise it's incompatible, (will fail - // an assertion). + // each evaluated non-null values. bool repl_mode; }; diff --git a/src/pk_compiler.c b/src/pk_compiler.c index 6556d15..035d428 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -242,12 +242,6 @@ typedef enum { DEPTH_LOCAL, //< Local scope. Increase with inner scope. } Depth; -typedef enum { - FN_NATIVE, //< Native C function. - FN_SCRIPT, //< Script functions defined with 'def'. - FN_LITERAL, //< Literal functions defined with 'function(){...}' -} FuncType; - typedef struct { const char* name; //< Directly points into the source string. uint32_t length; //< Length of the name. @@ -465,6 +459,13 @@ static OpInfo opcode_info[] = { /* INITALIZATION FUNCTIONS */ /*****************************************************************************/ +// FIXME: +// This forward declaration can be removed once the interpolated string's +// "list_join" function replaced with BUILD_STRING opcode. (The declaration +// needed at compiler initialization function to find the "list_join" function. +static int findBuiltinFunction(const PKVM* vm, + const char* name, uint32_t length); + // This should be called once the compiler initialized (to access it's fields). static void parserInit(Parser* parser, PKVM* vm, Compiler* compiler, const char* source, const char* path) { @@ -1220,6 +1221,22 @@ static bool matchAssignment(Compiler* compiler) { /* NAME SEARCH (AT COMPILATION PHASE) */ /*****************************************************************************/ +// Find the builtin function name and returns it's index in the builtins array +// if not found returns -1. +static int findBuiltinFunction(const PKVM* vm, + const char* name, uint32_t length) { + + for (int i = 0; i < vm->builtins_count; i++) { + + uint32_t bfn_length = (uint32_t)strlen(vm->builtins[i]->fn->name); + if (bfn_length != length) continue; + if (strncmp(name, vm->builtins[i]->fn->name, length) == 0) { + return i; + } + } + return -1; +} + // Result type for an identifier definition. typedef enum { NAME_NOT_DEFINED, @@ -1314,7 +1331,7 @@ static void compilerChangeStack(Compiler* compiler, int num); // Forward declaration of grammar functions. static void parsePrecedence(Compiler* compiler, Precedence precedence); -static int compileFunction(Compiler* compiler, FuncType fn_type); +static int compileFunction(Compiler* compiler, bool is_literal); static void compileExpression(Compiler* compiler); static void exprLiteral(Compiler* compiler); @@ -1520,8 +1537,8 @@ static void exprInterpolation(Compiler* compiler) { } static void exprFunc(Compiler* compiler) { - int fn_index = compileFunction(compiler, FN_LITERAL); - emitOpcode(compiler, OP_PUSH_CONSTANT); + int fn_index = compileFunction(compiler, true); + emitOpcode(compiler, OP_PUSH_CLOSURE); emitShort(compiler, fn_index); } @@ -2116,7 +2133,7 @@ static int compileClass(Compiler* compiler) { int cls_index, ctor_index; Class* cls = newClass(compiler->parser.vm, compiler->module, name, (uint32_t)name_len, &cls_index, &ctor_index); - cls->ctor->arity = 0; + cls->ctor->fn->arity = 0; // FIXME: // Temproary patch for moving functions and classes to constant buffer. @@ -2134,9 +2151,9 @@ static int compileClass(Compiler* compiler) { } // Compile the constructor function. - ASSERT(compiler->func->ptr == compiler->module->body, OOPS); + ASSERT(compiler->func->ptr == compiler->module->body->fn, OOPS); Func curr_fn; - compilerPushFunc(compiler, &curr_fn, cls->ctor, ctor_index); + compilerPushFunc(compiler, &curr_fn, cls->ctor->fn, ctor_index); compilerEnterBlock(compiler); // Push an instance on the stack. @@ -2194,12 +2211,12 @@ static int compileClass(Compiler* compiler) { } // Compile a function and return it's index in the module's function buffer. -static int compileFunction(Compiler* compiler, FuncType fn_type) { +static int compileFunction(Compiler* compiler, bool is_literal) { const char* name; int name_length; - if (fn_type != FN_LITERAL) { + if (!is_literal) { consume(compiler, TK_NAME, "Expected a function name."); name = compiler->parser.previous.start; name_length = compiler->parser.previous.length; @@ -2211,20 +2228,21 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) { int fn_index; Function* func = newFunction(compiler->parser.vm, name, name_length, - compiler->module, fn_type == FN_NATIVE, NULL, - &fn_index); + compiler->module, false, NULL, &fn_index); if (fn_index >= MAX_CONSTANTS) { parseError(compiler, "A module should contain at most %d " "unique constants.", MAX_CONSTANTS); } - if (fn_type != FN_LITERAL) { - // FIXME: remove native keyword for functions. - ASSERT(fn_type != FN_NATIVE, OOPS); + if (!is_literal) { ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS); int name_line = compiler->parser.previous.line; int g_index = compilerAddVariable(compiler, name, name_length, name_line); - moduleSetGlobal(compiler->module, g_index, VAR_OBJ(func)); + + vmPushTempRef(compiler->parser.vm, &func->_super); // func. + Closure* closure = newClosure(compiler->parser.vm, func); + moduleSetGlobal(compiler->module, g_index, VAR_OBJ(closure)); + vmPopTempRef(compiler->parser.vm); // func. } Func curr_fn; @@ -2270,16 +2288,11 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) { func->arity = argc; compilerChangeStack(compiler, argc); - if (fn_type != FN_NATIVE) { - compileBlockBody(compiler, BLOCK_FUNC); + compileBlockBody(compiler, BLOCK_FUNC); - consume(compiler, TK_END, "Expected 'end' after function definition end."); - compilerExitBlock(compiler); // Parameter depth. - emitFunctionEnd(compiler); - - } else { - compilerExitBlock(compiler); // Parameter depth. - } + consume(compiler, TK_END, "Expected 'end' after function definition end."); + compilerExitBlock(compiler); // Parameter depth. + emitFunctionEnd(compiler); #if DUMP_BYTECODE // FIXME: @@ -2879,7 +2892,7 @@ static void compileStatement(Compiler* compiler) { // If running REPL mode, print the expression's evaluated value. if (compiler->options && compiler->options->repl_mode && - compiler->func->ptr == compiler->module->body && + compiler->func->ptr == compiler->module->body->fn && is_expression /*&& compiler->scope_depth == DEPTH_GLOBAL*/) { emitOpcode(compiler, OP_REPL_PRINT); } @@ -2899,11 +2912,8 @@ static void compileTopLevelStatement(Compiler* compiler) { if (match(compiler, TK_CLASS)) { compileClass(compiler); - } else if (match(compiler, TK_NATIVE)) { - compileFunction(compiler, FN_NATIVE); - } else if (match(compiler, TK_DEF)) { - compileFunction(compiler, FN_SCRIPT); + compileFunction(compiler, false); } else if (match(compiler, TK_FROM)) { compileFromImport(compiler); @@ -2949,7 +2959,7 @@ PkResult compile(PKVM* vm, Module* module, const char* source, // If we're compiling for a module that was already compiled (when running // REPL or evaluating an expression) we don't need the old main anymore. // just use the globals and functions of the module and use a new body func. - pkByteBufferClear(&module->body->fn->opcodes, vm); + pkByteBufferClear(&module->body->fn->fn->opcodes, vm); // Remember the count of constants, names, and globals, If the compilation // failed discard all of them and roll back. @@ -2959,7 +2969,7 @@ PkResult compile(PKVM* vm, Module* module, const char* source, Func curr_fn; curr_fn.depth = DEPTH_MODULE; - curr_fn.ptr = module->body; + curr_fn.ptr = module->body->fn; curr_fn.outer_func = NULL; compiler->func = &curr_fn; diff --git a/src/pk_core.c b/src/pk_core.c index a497985..20071b3 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -81,8 +81,10 @@ PkHandle* pkGetMainFunction(PKVM* vm, PkHandle* module) { int main_index = moduleGetGlobalIndex(_module, IMPLICIT_MAIN_NAME, (uint32_t)strlen(IMPLICIT_MAIN_NAME)); if (main_index == -1) return NULL; - ASSERT_INDEX(main_index, (int)_module->constants.count); - return vmNewHandle(vm, _module->constants.data[main_index]); + ASSERT_INDEX(main_index, _module->globals.count); + Var main_fn = _module->globals.data[main_index]; + ASSERT(IS_OBJ_TYPE(main_fn, OBJ_CLOSURE), OOPS); + return vmNewHandle(vm, main_fn); } // A convenient macro to get the nth (1 based) argument of the current @@ -389,9 +391,7 @@ static inline bool validateCond(PKVM* vm, bool condition, const char* err) { VALIDATE_ARG_OBJ(String, OBJ_STRING, "string") VALIDATE_ARG_OBJ(List, OBJ_LIST, "list") VALIDATE_ARG_OBJ(Map, OBJ_MAP, "map") - VALIDATE_ARG_OBJ(Function, OBJ_FUNC, "function") VALIDATE_ARG_OBJ(Closure, OBJ_CLOSURE, "closure") - VALIDATE_ARG_OBJ(Upvalue, OBJ_UPVALUE, "upvalue") VALIDATE_ARG_OBJ(Fiber, OBJ_FIBER, "fiber") VALIDATE_ARG_OBJ(Class, OBJ_CLASS, "class") @@ -399,26 +399,6 @@ static inline bool validateCond(PKVM* vm, bool condition, const char* err) { /* SHARED FUNCTIONS */ /*****************************************************************************/ -int findBuiltinFunction(const PKVM* vm, const char* name, uint32_t length) { - for (uint32_t i = 0; i < vm->builtins_count; i++) { - if (length == vm->builtins[i].length && - strncmp(name, vm->builtins[i].name, length) == 0) { - return i; - } - } - return -1; - } - -Function* getBuiltinFunction(const PKVM* vm, int index) { - ASSERT_INDEX((uint32_t)index, vm->builtins_count); - return vm->builtins[index].fn; -} - -const char* getBuiltinFunctionName(const PKVM* vm, int index) { - ASSERT_INDEX((uint32_t)index, vm->builtins_count); - return vm->builtins[index].name; -} - Module* getCoreLib(const PKVM* vm, String* name) { Var lib = mapGet(vm->core_libs, VAR_OBJ(name)); if (IS_UNDEF(lib)) return NULL; @@ -438,7 +418,7 @@ DEF(coreTypeName, } DEF(coreHelp, - "help([fn]) -> null\n" + "help([fn:Closure]) -> null\n" "This will write an error message to stdout and return null.") { int argc = ARGC; @@ -456,18 +436,18 @@ DEF(coreHelp, // TODO: Extend help() to work with modules and classes. // Add docstring (like python) to support it in pocketlang. - Function* fn; - if (!validateArgFunction(vm, 1, &fn)) return; + Closure* closure; + if (!validateArgClosure(vm, 1, &closure)) return; // If there ins't an io function callback, we're done. if (vm->config.write_fn == NULL) RET(VAR_NULL); - if (fn->docstring != NULL) { - vm->config.write_fn(vm, fn->docstring); + if (closure->fn->docstring != NULL) { + vm->config.write_fn(vm, closure->fn->docstring); vm->config.write_fn(vm, "\n\n"); } else { vm->config.write_fn(vm, "function '"); - vm->config.write_fn(vm, fn->name); + vm->config.write_fn(vm, closure->fn->name); vm->config.write_fn(vm, "()' doesn't have a docstring.\n"); } } @@ -788,7 +768,11 @@ static void moduleAddFunctionInternal(PKVM* vm, Module* module, module, true, docstring, NULL); fn->native = fptr; fn->arity = arity; - moduleAddGlobal(vm, module, name, (uint32_t)strlen(name), VAR_OBJ(fn)); + + vmPushTempRef(vm, &fn->_super); // fn. + Closure* closure = newClosure(vm, fn); + moduleAddGlobal(vm, module, name, (uint32_t)strlen(name), VAR_OBJ(closure)); + vmPopTempRef(vm); // fn. } // 'lang' library methods. @@ -812,18 +796,18 @@ DEF(stdLangGC, } DEF(stdLangDisas, - "disas(fn:Function) -> String\n" + "disas(fn:Closure) -> String\n" "Returns the disassembled opcode of the function [fn].") { // TODO: support dissasemble class constructors and module main body. - Function* fn; - if (!validateArgFunction(vm, 1, &fn)) return; + Closure* closure; + if (!validateArgClosure(vm, 1, &closure)) return; - if (!validateCond(vm, !fn->is_native, + if (!validateCond(vm, !closure->fn->is_native, "Cannot disassemble native functions.")) return; - dumpFunctionCode(vm, fn); + dumpFunctionCode(vm, closure->fn); } #ifdef DEBUG @@ -1123,12 +1107,12 @@ DEF(stdMathErfc, // ----------------------- DEF(stdFiberNew, - "new(fn:Function) -> fiber\n" + "new(fn:Closure) -> fiber\n" "Create and return a new fiber from the given function [fn].") { - Function* fn; - if (!validateArgFunction(vm, 1, &fn)) return; - RET(VAR_OBJ(newFiber(vm, fn))); + Closure* closure; + if (!validateArgClosure(vm, 1, &closure)) return; + RET(VAR_OBJ(newFiber(vm, closure))); } DEF(stdFiberRun, @@ -1185,15 +1169,17 @@ DEF(stdFiberResume, /* CORE INITIALIZATION */ /*****************************************************************************/ -static void initializeBuiltinFN(PKVM* vm, BuiltinFn* bfn, const char* name, +static void initializeBuiltinFN(PKVM* vm, Closure** bfn, const char* name, int length, int arity, pkNativeFn ptr, const char* docstring) { - bfn->name = name; - bfn->length = length; - bfn->fn = newFunction(vm, name, length, NULL, true, docstring, NULL); - bfn->fn->arity = arity; - bfn->fn->native = ptr; + Function* fn = newFunction(vm, name, length, NULL, true, docstring, NULL); + fn->arity = arity; + fn->native = ptr; + + vmPushTempRef(vm, &fn->_super); // fn. + *bfn = newClosure(vm, fn); + vmPopTempRef(vm); // fn. } void initializeCore(PKVM* vm) { @@ -1663,25 +1649,29 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { case OBJ_FUNC: { - Function* fn = (Function*)obj; + // Functions aren't first class objects. + UNREACHABLE(); + } + + case OBJ_CLOSURE: + { + Closure* closure = (Closure*)obj; switch (attrib->hash) { case CHECK_HASH("arity", 0x3e96bd7a): - return VAR_NUM((double)(fn->arity)); + return VAR_NUM((double)(closure->fn->arity)); case CHECK_HASH("name", 0x8d39bde6): - return VAR_OBJ(newString(vm, fn->name)); + return VAR_OBJ(newString(vm, closure->fn->name)); default: ERR_NO_ATTRIB(vm, on, attrib); return VAR_NULL; } - UNREACHABLE(); } - case OBJ_CLOSURE: case OBJ_UPVALUE: - TODO; + // Upvalues aren't first class objects. UNREACHABLE(); case OBJ_FIBER: @@ -1693,7 +1683,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { return VAR_BOOL(fb->state == FIBER_DONE); case CHECK_HASH("function", 0x9ed64249): - return VAR_OBJ(fb->func); + return VAR_OBJ(fb->closure); default: ERR_NO_ATTRIB(vm, on, attrib); @@ -1787,15 +1777,19 @@ do { \ } case OBJ_FUNC: + // Functions aren't first class objects. + UNREACHABLE(); + return; + + case OBJ_CLOSURE: ATTRIB_IMMUTABLE("arity"); ATTRIB_IMMUTABLE("name"); ERR_NO_ATTRIB(vm, on, attrib); return; - case OBJ_CLOSURE: case OBJ_UPVALUE: - TODO; - ERR_NO_ATTRIB(vm, on, attrib); + // Upvalues aren't first class objects. + UNREACHABLE(); return; case OBJ_FIBER: diff --git a/src/pk_core.h b/src/pk_core.h index a0ab019..a061f86 100644 --- a/src/pk_core.h +++ b/src/pk_core.h @@ -13,16 +13,6 @@ // Initialize core language, builtin function and core libs. void initializeCore(PKVM* vm); -// Find the builtin function name and returns it's index in the builtins array -// if not found returns -1. -int findBuiltinFunction(const PKVM* vm, const char* name, uint32_t length); - -// Returns the builtin function at index [index]. -Function* getBuiltinFunction(const PKVM* vm, int index); - -// Returns the builtin function's name at index [index]. -const char* getBuiltinFunctionName(const PKVM* vm, int index); - // Return the core library with the [name] if exists in the core libs, // otherwise returns NULL. Module* getCoreLib(const PKVM* vm, String* name); diff --git a/src/pk_debug.c b/src/pk_debug.c index 25e2a0a..454aed7 100644 --- a/src/pk_debug.c +++ b/src/pk_debug.c @@ -231,7 +231,8 @@ void dumpFunctionCode(PKVM* vm, Function* func) { case OP_PUSH_BUILTIN_FN: { int index = READ_BYTE(); - const char* name = getBuiltinFunctionName(vm, index); + ASSERT_INDEX(index, vm->builtins_count); + const char* name = vm->builtins[index]->fn->name; // Prints: %5d [Fn:%s]\n PRINT_INT(index); PRINT(" [Fn:"); @@ -240,6 +241,21 @@ void dumpFunctionCode(PKVM* vm, Function* func) { break; } + case OP_PUSH_CLOSURE: + { + int index = READ_SHORT(); + ASSERT_INDEX((uint32_t)index, func->owner->constants.count); + Var value = func->owner->constants.data[index]; + ASSERT(IS_OBJ_TYPE(value, OBJ_FUNC), OOPS); + + // Prints: %5d [val]\n + PRINT_INT(index); + PRINT(" "); + dumpValue(vm, value); + NEWLINE(); + break; + } + case OP_POP: NO_ARGS(); break; case OP_IMPORT: { @@ -365,7 +381,7 @@ void dumpGlobalValues(PKVM* vm) { int frame_ind = fiber->frame_count - 1; ASSERT(frame_ind >= 0, OOPS); CallFrame* frame = &fiber->frames[frame_ind]; - Module* module = frame->fn->owner; + Module* module = frame->closure->fn->owner; for (uint32_t i = 0; i < module->global_names.count; i++) { String* name = module->names.data[module->global_names.data[i]]; diff --git a/src/pk_opcodes.h b/src/pk_opcodes.h index f092cf5..4706f72 100644 --- a/src/pk_opcodes.h +++ b/src/pk_opcodes.h @@ -97,6 +97,11 @@ OPCODE(STORE_GLOBAL, 1, 0) // params: 1 bytes index. OPCODE(PUSH_BUILTIN_FN, 1, 1) +// Push a closure for the function at the constant pool with index of the +// first 2 bytes arguments. +// params: 2 byte index. +OPCODE(PUSH_CLOSURE, 2, 1) + // Pop the stack top. OPCODE(POP, 0, -1) diff --git a/src/pk_value.c b/src/pk_value.c index 34463b0..2e2c244 100644 --- a/src/pk_value.c +++ b/src/pk_value.c @@ -75,9 +75,10 @@ PkHandle* pkNewMap(PKVM* vm) { } PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) { - __ASSERT(IS_OBJ_TYPE(fn->value, OBJ_FUNC), "Fn should be of type function."); + __ASSERT(IS_OBJ_TYPE(fn->value, OBJ_CLOSURE), + "Handle should be of type function."); - Fiber* fiber = newFiber(vm, (Function*)AS_OBJ(fn->value)); + Fiber* fiber = newFiber(vm, (Closure*)AS_OBJ(fn->value)); vmPushTempRef(vm, &fiber->_super); // fiber PkHandle* handle = vmNewHandle(vm, VAR_OBJ(fiber)); vmPopTempRef(vm); // fiber @@ -264,18 +265,17 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) { Fiber* fiber = (Fiber*)obj; vm->bytes_allocated += sizeof(Fiber); - markObject(vm, &fiber->func->_super); + markObject(vm, &fiber->closure->_super); - // Blacken the stack. + // Mark the stack. for (Var* local = fiber->stack; local < fiber->sp; local++) { markValue(vm, *local); } vm->bytes_allocated += sizeof(Var) * fiber->stack_size; - // Blacken call frames. + // Mark call frames. for (int i = 0; i < fiber->frame_count; i++) { - markObject(vm, (Object*)&fiber->frames[i].fn->_super); - markObject(vm, &fiber->frames[i].fn->owner->_super); + markObject(vm, (Object*)&fiber->frames[i].closure->_super); } vm->bytes_allocated += sizeof(CallFrame) * fiber->frame_capacity; @@ -379,7 +379,7 @@ Range* newRange(PKVM* vm, double from, double to) { return range; } -Module* newModule(PKVM* vm, String* name, bool is_core) { +Module* newModule(PKVM* vm, String* name, bool is_native) { Module* module = ALLOCATE(vm, Module); varInitObject(&module->_super, vm, OBJ_MODULE); @@ -387,11 +387,11 @@ Module* newModule(PKVM* vm, String* name, bool is_core) { module->path = name; module->name = NULL; - module->initialized = is_core; + module->initialized = is_native; module->body = NULL; // Core modules has its name as the module name. - if (is_core) module->name = name; + if (is_native) module->name = name; pkVarBufferInit(&module->globals); pkUintBufferInit(&module->global_names); @@ -400,7 +400,7 @@ Module* newModule(PKVM* vm, String* name, bool is_core) { // Add a implicit main function and the '__file__' global to the module, only // if it's not a core module. - if (!is_core) { + if (!is_native) { vmPushTempRef(vm, &module->_super); // module. moduleAddMain(vm, module); @@ -488,7 +488,7 @@ Upvalue* newUpvalue(PKVM* vm, Var* value) { return upvalue; } -Fiber* newFiber(PKVM* vm, Function* fn) { +Fiber* newFiber(PKVM* vm, Closure* closure) { Fiber* fiber = ALLOCATE(vm, Fiber); // Not sure why this memset is needed here. If it doesn't then remove it. @@ -497,13 +497,13 @@ Fiber* newFiber(PKVM* vm, Function* fn) { varInitObject(&fiber->_super, vm, OBJ_FIBER); fiber->state = FIBER_NEW; - fiber->func = fn; + fiber->closure = closure; - if (fn->is_native) { + if (closure->fn->is_native) { // For native functions, we're only using stack for parameters, // there won't be any locals or temps (which are belongs to the // native "C" stack). - int stack_size = utilPowerOf2Ceil(fn->arity + 1); + int stack_size = utilPowerOf2Ceil(closure->fn->arity + 1); fiber->stack = ALLOCATE_ARRAY(vm, Var, stack_size); fiber->stack_size = stack_size; fiber->ret = fiber->stack; @@ -511,7 +511,7 @@ Fiber* newFiber(PKVM* vm, Function* fn) { } else { // Allocate stack. - int stack_size = utilPowerOf2Ceil(fn->fn->stack_size + 1); + int stack_size = utilPowerOf2Ceil(closure->fn->fn->stack_size + 1); if (stack_size < MIN_STACK_SIZE) stack_size = MIN_STACK_SIZE; fiber->stack = ALLOCATE_ARRAY(vm, Var, stack_size); fiber->stack_size = stack_size; @@ -524,8 +524,8 @@ Fiber* newFiber(PKVM* vm, Function* fn) { fiber->frame_count = 1; // Initialize the first frame. - fiber->frames[0].fn = fn; - fiber->frames[0].ip = fn->fn->opcodes.data; + fiber->frames[0].closure = closure; + fiber->frames[0].ip = closure->fn->fn->opcodes.data; fiber->frames[0].rbp = fiber->ret; } @@ -559,10 +559,15 @@ Class* newClass(PKVM* vm, Module* module, const char* name, uint32_t length, String* ctor_name = stringFormat(vm, "$(Ctor:@)", special, cls_name); // Constructor. - vmPushTempRef(vm, &ctor_name->_super); // ctor_name - cls->ctor = newFunction(vm, ctor_name->data, ctor_name->length, - module, false, NULL, ctor_index); - vmPopTempRef(vm); // ctor_name + vmPushTempRef(vm, &ctor_name->_super); // ctor_name. + { + Function* ctor_fn = newFunction(vm, ctor_name->data, ctor_name->length, + module, false, NULL, ctor_index); + vmPushTempRef(vm, &ctor_fn->_super); // ctor_fn. + cls->ctor = newClosure(vm, ctor_fn); + vmPopTempRef(vm); // ctor_fn. + } + vmPopTempRef(vm); // ctor_name. vmPopTempRef(vm); // class. return cls; @@ -1193,12 +1198,17 @@ void moduleSetGlobal(Module* module, int index, Var value) { void moduleAddMain(PKVM* vm, Module* module) { ASSERT(module->body == NULL, OOPS); - const char* fn_name = IMPLICIT_MAIN_NAME; - module->body = newFunction(vm, fn_name, (int)strlen(fn_name), - module, false, NULL/*TODO*/, NULL); - module->body->arity = 0; module->initialized = false; + const char* fn_name = IMPLICIT_MAIN_NAME; + Function* body_fn = newFunction(vm, fn_name, (int)strlen(fn_name), + module, false, NULL/*TODO*/, NULL); + body_fn->arity = 0; + + vmPushTempRef(vm, &body_fn->_super); // body_fn. + module->body = newClosure(vm, body_fn); + vmPopTempRef(vm); // body_fn. + moduleAddGlobal(vm, module, IMPLICIT_MAIN_NAME, (uint32_t)strlen(IMPLICIT_MAIN_NAME), VAR_OBJ(module->body)); @@ -1653,8 +1663,8 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff, case OBJ_FIBER: { const Fiber* fb = (const Fiber*)obj; pkByteBufferAddString(buff, vm, "[Fiber:", 7); - pkByteBufferAddString(buff, vm, fb->func->name, - (uint32_t)strlen(fb->func->name)); + pkByteBufferAddString(buff, vm, fb->closure->fn->name, + (uint32_t)strlen(fb->closure->fn->name)); pkByteBufferWrite(buff, vm, ']'); return; } diff --git a/src/pk_value.h b/src/pk_value.h index 2d76534..1387161 100644 --- a/src/pk_value.h +++ b/src/pk_value.h @@ -307,8 +307,8 @@ struct Module { pkUintBuffer global_names; // Top level statements of a module are compiled to an implicit function - // body whic will be executed if it's imported for the first time. - Function* body; + // body which will be executed if it's imported for the first time. + Closure* body; // If the [initialized] boolean is false, the body function of the module // will be executed when it's first imported and the 'initialized' boolean @@ -437,9 +437,9 @@ struct Upvalue { }; typedef struct { - const uint8_t* ip; //< Pointer to the next instruction byte code. - const Function* fn; //< Function of the frame. - Var* rbp; //< Stack base pointer. (%rbp) + const uint8_t* ip; //< Pointer to the next instruction byte code. + const Closure* closure; //< Closure of the frame. + Var* rbp; //< Stack base pointer. (%rbp) } CallFrame; typedef enum { @@ -454,8 +454,8 @@ struct Fiber { FiberState state; - // The root function of the fiber. - Function* func; + // The root closure of the fiber. + Closure* closure; // The stack of the execution holding locals and temps. A heap will be // allocated and grow as needed. @@ -493,7 +493,7 @@ struct Class { // buffer. uint32_t name; - Function* ctor; //< The constructor function. + Closure* ctor; //< The constructor function. pkUintBuffer field_names; //< Buffer of field names. // TODO: ordered names buffer for binary search. }; @@ -554,7 +554,7 @@ Range* newRange(PKVM* vm, double from, double to); // FIXME: // We may need 2 different constructor for native and script modules. -Module* newModule(PKVM* vm, String* name, bool is_core); +Module* newModule(PKVM* vm, String* name, bool is_native); // FIXME: // We may need 2 different constuctor for native and script functions. @@ -568,8 +568,8 @@ Closure* newClosure(PKVM* vm, Function* fn); // Allocate a new upvalue object for the [value] and return it. Upvalue* newUpvalue(PKVM* vm, Var* value); -// Allocate new Fiber object around the function [fn] and return Fiber*. -Fiber* newFiber(PKVM* vm, Function* fn); +// Allocate new Fiber object for the [closure] and return Fiber*. +Fiber* newFiber(PKVM* vm, Closure* closure); // FIXME: // Same fix has to applied as newFunction() (see above). diff --git a/src/pk_vm.c b/src/pk_vm.c index a0140a2..cf4e283 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -256,8 +256,8 @@ void vmCollectGarbage(PKVM* vm) { // Mark the core libs and builtin functions. markObject(vm, &vm->core_libs->_super); - for (uint32_t i = 0; i < vm->builtins_count; i++) { - markObject(vm, &vm->builtins[i].fn->_super); + for (int i = 0; i < vm->builtins_count; i++) { + markObject(vm, &vm->builtins[i]->_super); } // Mark the modules cache. @@ -323,10 +323,12 @@ void vmCollectGarbage(PKVM* vm) { } while (false) bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var** argv) { - ASSERT(fiber->func->arity >= -1, OOPS " (Forget to initialize arity.)"); + ASSERT(fiber->closure->fn->arity >= -1, + OOPS " (Forget to initialize arity.)"); - if (argc != fiber->func->arity) { - char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", fiber->func->arity); + if (argc != fiber->closure->fn->arity) { + char buff[STR_INT_BUFF_SIZE]; + sprintf(buff, "%d", fiber->closure->fn->arity); _ERR_FAIL(stringFormat(vm, "Expected exactly $ argument(s).", buff)); } @@ -513,8 +515,8 @@ static inline void growStack(PKVM* vm, int size) { } } -static inline void pushCallFrame(PKVM* vm, const Function* fn, Var* rbp) { - ASSERT(!fn->is_native, "Native function shouldn't use call frames."); +static inline void pushCallFrame(PKVM* vm, const Closure* closure, Var* rbp) { + ASSERT(!closure->fn->is_native, OOPS); // Grow the stack frame if needed. if (vm->fiber->frame_count + 1 > vm->fiber->frame_capacity) { @@ -526,31 +528,32 @@ static inline void pushCallFrame(PKVM* vm, const Function* fn, Var* rbp) { } // Grow the stack if needed. - int needed = fn->fn->stack_size + (int)(vm->fiber->sp - vm->fiber->stack); + int needed = (closure->fn->fn->stack_size + + (int)(vm->fiber->sp - vm->fiber->stack)); if (vm->fiber->stack_size <= needed) growStack(vm, needed); CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++; frame->rbp = rbp; - frame->fn = fn; - frame->ip = fn->fn->opcodes.data; + frame->closure = closure; + frame->ip = closure->fn->fn->opcodes.data; } -static inline void reuseCallFrame(PKVM* vm, const Function* fn) { +static inline void reuseCallFrame(PKVM* vm, const Closure* closure) { - ASSERT(!fn->is_native, "Native function shouldn't use call frames."); - ASSERT(fn->arity >= 0, OOPS); + ASSERT(!closure->fn->is_native, OOPS); + ASSERT(closure->fn->arity >= 0, OOPS); ASSERT(vm->fiber->frame_count > 0, OOPS); Fiber* fb = vm->fiber; CallFrame* frame = fb->frames + fb->frame_count - 1; - frame->fn = fn; - frame->ip = fn->fn->opcodes.data; + frame->closure = closure; + frame->ip = closure->fn->fn->opcodes.data; ASSERT(*frame->rbp == VAR_NULL, OOPS); // Move all the argument(s) to the base of the current frame. - Var* arg = fb->sp - fn->arity; + Var* arg = fb->sp - closure->fn->arity; Var* target = frame->rbp + 1; for (; arg < fb->sp; arg++, target++) { *target = *arg; @@ -560,7 +563,8 @@ static inline void reuseCallFrame(PKVM* vm, const Function* fn) { fb->sp = target; // Grow the stack if needed (least probably). - int needed = fn->fn->stack_size + (int)(vm->fiber->sp - vm->fiber->stack); + int needed = (closure->fn->fn->stack_size + + (int)(vm->fiber->sp - vm->fiber->stack)); if (vm->fiber->stack_size <= needed) growStack(vm, needed); } @@ -574,7 +578,7 @@ static void reportError(PKVM* vm) { vm->config.error_fn(vm, PK_ERROR_RUNTIME, NULL, -1, fiber->error->data); for (int i = fiber->frame_count - 1; i >= 0; i--) { CallFrame* frame = &fiber->frames[i]; - const Function* fn = frame->fn; + const Function* fn = frame->closure->fn; ASSERT(!fn->is_native, OOPS); int line = fn->fn->oplines.data[frame->ip - fn->fn->opcodes.data - 1]; vm->config.error_fn(vm, PK_ERROR_STACKTRACE, fn->owner->path->data, line, @@ -658,7 +662,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { frame = &vm->fiber->frames[vm->fiber->frame_count-1]; \ ip = frame->ip; \ rbp = frame->rbp; \ - module = frame->fn->owner; \ + module = frame->closure->fn->owner; \ } while (false) // Update the frame's execution variables before pushing another call frame. @@ -854,8 +858,18 @@ L_vm_main_loop: { uint8_t index = READ_BYTE(); ASSERT_INDEX(index, vm->builtins_count); - Function* fn = vm->builtins[index].fn; - PUSH(VAR_OBJ(fn)); + Closure* closure = vm->builtins[index]; + PUSH(VAR_OBJ(closure)); + DISPATCH(); + } + + OPCODE(PUSH_CLOSURE): + { + uint16_t index = READ_SHORT(); + ASSERT_INDEX(index, module->constants.count); + ASSERT(IS_OBJ_TYPE(module->constants.data[index], OBJ_FUNC), OOPS); + Function* fn = (Function*)AS_OBJ(module->constants.data[index]); + PUSH(VAR_OBJ(newClosure(vm, fn))); DISPATCH(); } @@ -907,13 +921,17 @@ L_vm_main_loop: Fiber* call_fiber = vm->fiber; Var* callable = call_fiber->sp - argc - 1; - const Function* fn = NULL; + const Closure* closure = NULL; - if (IS_OBJ_TYPE(*callable, OBJ_FUNC)) { - fn = (const Function*)AS_OBJ(*callable); + // Raw functions cannot be on the stack, since they're not first class + // citizens. + ASSERT(!IS_OBJ_TYPE(*callable, OBJ_FUNC), OOPS); + + if (IS_OBJ_TYPE(*callable, OBJ_CLOSURE)) { + closure = (const Closure*)AS_OBJ(*callable); } else if (IS_OBJ_TYPE(*callable, OBJ_CLASS)) { - fn = (const Function*)((Class*)AS_OBJ(*callable))->ctor; + closure = (const Closure*)((Class*)AS_OBJ(*callable))->ctor; } else { RUNTIME_ERROR(stringFormat(vm, "$ $(@).", "Expected a callable to " @@ -925,8 +943,8 @@ L_vm_main_loop: // If we reached here it's a valid callable. // -1 argument means multiple number of args. - if (fn->arity != -1 && fn->arity != argc) { - char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", fn->arity); + if (closure->fn->arity != -1 && closure->fn->arity != argc) { + char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", closure->fn->arity); String* msg = stringFormat(vm, "Expected exactly $ argument(s).", buff); RUNTIME_ERROR(msg); @@ -936,17 +954,17 @@ L_vm_main_loop: call_fiber->ret = callable; *(call_fiber->ret) = VAR_NULL; //< Set the return value to null. - if (fn->is_native) { + if (closure->fn->is_native) { - if (fn->native == NULL) { + if (closure->fn->native == NULL) { RUNTIME_ERROR(stringFormat(vm, - "Native function pointer of $ was NULL.", fn->name)); + "Native function pointer of $ was NULL.", closure->fn->name)); } // Update the current frame's ip. UPDATE_FRAME(); - fn->native(vm); //< Call the native function. + closure->fn->native(vm); //< Call the native function. // Calling yield() will change vm->fiber to it's caller fiber, which // would be null if we're not running the function with a fiber. @@ -965,13 +983,13 @@ L_vm_main_loop: if (instruction == OP_CALL) { UPDATE_FRAME(); //< Update the current frame's ip. - pushCallFrame(vm, fn, callable); + pushCallFrame(vm, closure, callable); LOAD_FRAME(); //< Load the top frame to vm's execution variables. } else { ASSERT(instruction == OP_TAIL_CALL, OOPS); - reuseCallFrame(vm, fn); + reuseCallFrame(vm, closure); LOAD_FRAME(); //< Re-load the frame to vm's execution variables. } } diff --git a/src/pk_vm.h b/src/pk_vm.h index 63d1dda..08f586e 100644 --- a/src/pk_vm.h +++ b/src/pk_vm.h @@ -46,14 +46,6 @@ (vm->fiber->error = err); \ } while (false) -// Builtin functions are stored in an array in the VM unlike other functions -// builtin function's doesn't belongs to any module. -typedef struct { - const char* name; //< Name of the function. - uint32_t length; //< Length of the name. - Function* fn; //< Native function pointer. -} BuiltinFn; - // A doubly link list of vars that have reference in the host application. // Handles are wrapper around Var that lives on the host application. struct PkHandle { @@ -122,8 +114,8 @@ struct PKVM { Map* core_libs; // Array of all builtin functions. - BuiltinFn builtins[BUILTIN_FN_CAPACITY]; - uint32_t builtins_count; + Closure* builtins[BUILTIN_FN_CAPACITY]; + int builtins_count; // Current fiber. Fiber* fiber;