mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
functions and classes are runtime initialized
this is necessare to make classes inherit from unknown identifiers which are resolved at the runtime and if we were to write the byte code to a file and run it, the globals should be initialized at the runtime since there isn't any compile time for them (pre compiled).
This commit is contained in:
parent
cb209eb4d4
commit
b5ec401cf4
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
||||
|
31
src/pk_vm.c
31
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);
|
||||
|
Loading…
Reference in New Issue
Block a user