diff --git a/src/pk_compiler.c b/src/pk_compiler.c index e6dff49..1660132 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -1505,7 +1505,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 void compileFunction(Compiler* compiler, FuncType fn_type); static void compileExpression(Compiler* compiler); static void exprLiteral(Compiler* compiler); @@ -1627,8 +1627,8 @@ static void emitStoreGlobal(Compiler* compiler, int index) { emitByte(compiler, index); } -// Emit opcode to push the named value at the [index] in it's array. -static void emitPushName(Compiler* compiler, NameDefnType type, int index) { +// Emit opcode to push the value of [type] at the [index] in it's array. +static void emitPushValue(Compiler* compiler, NameDefnType type, int index) { ASSERT(index >= 0, OOPS); switch (type) { @@ -1670,9 +1670,9 @@ static void emitPushName(Compiler* compiler, NameDefnType type, int index) { } } -// Emit opcode to store the stack top value to the named value at the [index] -// in it's array. -static void emitStoreName(Compiler* compiler, NameDefnType type, int index) { +// Emit opcode to store the stack top value to the named value to the [type] +// at the [index] in it's array. +static void emitStoreValue(Compiler* compiler, NameDefnType type, int index) { ASSERT(index >= 0, OOPS); switch (type) { @@ -1831,7 +1831,7 @@ static void exprName(Compiler* compiler) { } // Push the named value. - emitPushName(compiler, name_type, index); + emitPushValue(compiler, name_type, index); // Compile the RHS of the assigned operation. compileExpression(compiler); @@ -1854,7 +1854,7 @@ static void exprName(Compiler* compiler) { } else { // The assigned value or the result of the operator will be at the top of // the stack by now. Store it. - emitStoreName(compiler, name_type, index); + emitStoreValue(compiler, name_type, index); } } else { // Just the name and no assignment followed by. @@ -1873,7 +1873,7 @@ static void exprName(Compiler* compiler) { compilerAddForward(compiler, index, _FN, &tkname); } } else { - emitPushName(compiler, result.type, result.index); + emitPushValue(compiler, result.type, result.index); } } @@ -2442,6 +2442,9 @@ static int compileClass(Compiler* compiler) { checkMaxConstantsReached(compiler, cls_index); + emitOpcode(compiler, OP_PUSH_CLASS); + emitShort(compiler, cls_index); + skipNewLines(compiler); while (!match(compiler, TK_END)) { @@ -2451,50 +2454,41 @@ static int compileClass(Compiler* compiler) { break; } - // At the top level the stack size should be 0, before and after compiling - // a top level statement, since there aren't any locals at the top level. + // At the top level the stack size should be 1 -- the class, before and + // after compiling the class. ASSERT(compiler->parser.has_errors || - compiler->func->stack_size == 0, OOPS); + compiler->func->stack_size == 1, OOPS); consume(compiler, TK_DEF, "Expected method definition."); if (compiler->parser.has_syntax_error) break; - int fn_index = compileFunction(compiler, FUNC_METHOD); + compileFunction(compiler, FUNC_METHOD); if (compiler->parser.has_syntax_error) break; - Var fn_var = compiler->module->constants.data[fn_index]; - ASSERT(IS_OBJ_TYPE(fn_var, OBJ_FUNC), OOPS); - - // TODO: check if the constructor or method already exists and report - // error. Make sure the error report line match the name token's line. - - Closure* method = newClosure(_vm, (Function*)AS_OBJ(fn_var)); - if (strcmp(method->fn->name, "_init") == 0) { - cls->ctor = method; - - } else { - vmPushTempRef(_vm, &method->_super); // method. - pkClosureBufferWrite(&cls->methods, _vm, method); - vmPopTempRef(_vm); // method. - } - - // At the top level the stack size should be 0, before and after compiling - // a top level statement, since there aren't any locals at the top level. + // At the top level the stack size should be 1 -- the class, before and + // after compiling the class. ASSERT(compiler->parser.has_errors || - compiler->func->stack_size == 0, OOPS); + compiler->func->stack_size == 1, OOPS); skipNewLines(compiler); if (compiler->parser.has_syntax_error) break; } + int global_index = compilerAddVariable(compiler, name, name_len, name_line); + emitStoreValue(compiler, NAME_GLOBAL_VAR, global_index); + emitOpcode(compiler, OP_POP); // Pop the class. + compiler->parser.parsing_class = false; vmPopTempRef(_vm); // cls. return cls_index; } -// Compile a function and return it's index in the module's function buffer. -static int compileFunction(Compiler* compiler, FuncType fn_type) { +// Compile a function, if it's a literal function after this call a closure of +// the function will be at the stack top, toplevel functions will be assigned +// to a global variable and popped, and methods will be bind to the class and +// popped. +static void compileFunction(Compiler* compiler, FuncType fn_type) { const char* name; int name_length; @@ -2515,15 +2509,14 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) { checkMaxConstantsReached(compiler, fn_index); - if (fn_type != FUNC_LITERAL) { + // Only to be used by the toplevle function to define itself on the globals + // of the module. + int global_index = -1; + + if (fn_type == FUNC_TOPLEVEL) { ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS); int name_line = compiler->parser.previous.line; - int g_index = compilerAddVariable(compiler, name, name_length, name_line); - - 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. + global_index = compilerAddVariable(compiler, name, name_length, name_line); } if (fn_type == FUNC_METHOD && strncmp(name, "_init", name_length) == 0) { @@ -2597,18 +2590,23 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) { // function of this function, and the bellow emit calls will write to the // outer function. If it's a literal function, we need to push a closure // of it on the stack. - if (fn_type == FUNC_LITERAL) { - emitOpcode(compiler, OP_PUSH_CLOSURE); - emitShort(compiler, fn_index); + emitOpcode(compiler, OP_PUSH_CLOSURE); + emitShort(compiler, fn_index); - // Capture the upvalues when the closure is created. - for (int i = 0; i < curr_fn.ptr->upvalue_count; i++) { - emitByte(compiler, (curr_fn.upvalues[i].is_immediate) ? 1 : 0); - emitByte(compiler, curr_fn.upvalues[i].index); - } + // Capture the upvalues when the closure is created. + for (int i = 0; i < curr_fn.ptr->upvalue_count; i++) { + emitByte(compiler, (curr_fn.upvalues[i].is_immediate) ? 1 : 0); + emitByte(compiler, curr_fn.upvalues[i].index); } - return fn_index; + if (fn_type == FUNC_TOPLEVEL) { + emitStoreValue(compiler, NAME_GLOBAL_VAR, global_index); + emitOpcode(compiler, OP_POP); + + } else if (fn_type == FUNC_METHOD || fn_type == FUNC_CONSTRUCTOR) { + // Bind opcode will also pop the method so, we shouldn't do it here. + emitOpcode(compiler, OP_BIND_METHOD); + } } // Finish a block body. @@ -3381,6 +3379,7 @@ PkResult compile(PKVM* vm, Module* module, const char* source, #if DUMP_BYTECODE dumpFunctionCode(compiler->parser.vm, module->body->fn); + DEBUG_BREAK(); #endif // Return the compilation result. diff --git a/src/pk_debug.c b/src/pk_debug.c index 30b7e3c..cf8f251 100644 --- a/src/pk_debug.c +++ b/src/pk_debug.c @@ -407,10 +407,17 @@ void dumpFunctionCode(PKVM* vm, Function* func) { case OP_STORE_GLOBAL: { int index = READ_BYTE(); + ASSERT_INDEX(index, (int)func->owner->global_names.count); + int name_index = func->owner->global_names.data[index]; + ASSERT_INDEX(name_index, (int)func->owner->constants.count); + + Var name = func->owner->constants.data[name_index]; + ASSERT(IS_OBJ_TYPE(name, OBJ_STRING), OOPS); + // Prints: %5d '%s'\n PRINT_INT(index); PRINT(" '"); - PRINT(func->owner->name->data); + PRINT(((String*)AS_OBJ(name))->data); PRINT("'\n"); break; } @@ -465,6 +472,22 @@ void dumpFunctionCode(PKVM* vm, Function* func) { break; } + case OP_PUSH_CLASS: + { + 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_CLASS), OOPS); + + // Prints: %5d [val]\n + PRINT_INT(index); + PRINT(" "); + dumpValue(vm, value); + NEWLINE(); + break; + } + + case OP_BIND_METHOD: case OP_CLOSE_UPVALUE: case OP_POP: NO_ARGS(); diff --git a/src/pk_opcodes.h b/src/pk_opcodes.h index bf6fd73..d033fb2 100644 --- a/src/pk_opcodes.h +++ b/src/pk_opcodes.h @@ -111,6 +111,14 @@ OPCODE(STORE_UPVALUE, 1, 0) // params: 2 byte index. OPCODE(PUSH_CLOSURE, 2, 1) +// Push a class at the constant pool with the index of the two bytes argument. +// params: 2 byte index. +OPCODE(PUSH_CLASS, 2, 1) + +// At the stack top, a closure and a class should be there. Add the method to +// the class and pop it. +OPCODE(BIND_METHOD, 0, -1) + // Close the upvalue for the local at the stack top and pop it. OPCODE(CLOSE_UPVALUE, 0, -1) diff --git a/src/pk_vm.c b/src/pk_vm.c index 177d821..4bcc36c 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -799,6 +799,37 @@ L_vm_main_loop: DISPATCH(); } + OPCODE(PUSH_CLASS): + { + uint16_t index = READ_SHORT(); + ASSERT_INDEX(index, module->constants.count); + ASSERT(IS_OBJ_TYPE(module->constants.data[index], OBJ_CLASS), OOPS); + PUSH(module->constants.data[index]); + DISPATCH(); + } + + OPCODE(BIND_METHOD): + { + ASSERT(IS_OBJ_TYPE(PEEK(-1), OBJ_CLOSURE), OOPS); + ASSERT(IS_OBJ_TYPE(PEEK(-2), OBJ_CLASS), OOPS); + + Closure* method = (Closure*)AS_OBJ(PEEK(-1)); + Class* cls = (Class*)AS_OBJ(PEEK(-2)); + + // FIXME: literal string "_inint". + if (strcmp(method->fn->name, "_init") == 0) { + cls->ctor = method; + } else { + // TODO: The method buffer should be ordered with it's name and + // inserted in a way to preserve the order to implement binary search + // to find a method. + pkClosureBufferWrite(&cls->methods, vm, method); + } + + DROP(); + DISPATCH(); + } + OPCODE(CLOSE_UPVALUE): { closeUpvalues(fiber, fiber->sp - 1);