Merge pull request #210 from ThakeeNathees/globals-init

functions and classes are runtime initialized
This commit is contained in:
Thakee Nathees 2022-04-26 19:23:53 +05:30 committed by GitHub
commit 745387e307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 112 additions and 51 deletions

View File

@ -1505,7 +1505,7 @@ static void compilerChangeStack(Compiler* compiler, int num);
// Forward declaration of grammar functions. // Forward declaration of grammar functions.
static void parsePrecedence(Compiler* compiler, Precedence precedence); 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 compileExpression(Compiler* compiler);
static void exprLiteral(Compiler* compiler); static void exprLiteral(Compiler* compiler);
@ -1627,8 +1627,8 @@ static void emitStoreGlobal(Compiler* compiler, int index) {
emitByte(compiler, index); emitByte(compiler, index);
} }
// Emit opcode to push the named value at the [index] in it's array. // Emit opcode to push the value of [type] at the [index] in it's array.
static void emitPushName(Compiler* compiler, NameDefnType type, int index) { static void emitPushValue(Compiler* compiler, NameDefnType type, int index) {
ASSERT(index >= 0, OOPS); ASSERT(index >= 0, OOPS);
switch (type) { 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] // Emit opcode to store the stack top value to the named value to the [type]
// in it's array. // at the [index] in it's array.
static void emitStoreName(Compiler* compiler, NameDefnType type, int index) { static void emitStoreValue(Compiler* compiler, NameDefnType type, int index) {
ASSERT(index >= 0, OOPS); ASSERT(index >= 0, OOPS);
switch (type) { switch (type) {
@ -1831,7 +1831,7 @@ static void exprName(Compiler* compiler) {
} }
// Push the named value. // Push the named value.
emitPushName(compiler, name_type, index); emitPushValue(compiler, name_type, index);
// Compile the RHS of the assigned operation. // Compile the RHS of the assigned operation.
compileExpression(compiler); compileExpression(compiler);
@ -1854,7 +1854,7 @@ static void exprName(Compiler* compiler) {
} else { } else {
// The assigned value or the result of the operator will be at the top of // The assigned value or the result of the operator will be at the top of
// the stack by now. Store it. // 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. } else { // Just the name and no assignment followed by.
@ -1873,7 +1873,7 @@ static void exprName(Compiler* compiler) {
compilerAddForward(compiler, index, _FN, &tkname); compilerAddForward(compiler, index, _FN, &tkname);
} }
} else { } 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); checkMaxConstantsReached(compiler, cls_index);
emitOpcode(compiler, OP_PUSH_CLASS);
emitShort(compiler, cls_index);
skipNewLines(compiler); skipNewLines(compiler);
while (!match(compiler, TK_END)) { while (!match(compiler, TK_END)) {
@ -2451,50 +2454,41 @@ static int compileClass(Compiler* compiler) {
break; break;
} }
// At the top level the stack size should be 0, before and after compiling // At the top level the stack size should be 1 -- the class, before and
// a top level statement, since there aren't any locals at the top level. // after compiling the class.
ASSERT(compiler->parser.has_errors || ASSERT(compiler->parser.has_errors ||
compiler->func->stack_size == 0, OOPS); compiler->func->stack_size == 1, OOPS);
consume(compiler, TK_DEF, "Expected method definition."); consume(compiler, TK_DEF, "Expected method definition.");
if (compiler->parser.has_syntax_error) break; 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; if (compiler->parser.has_syntax_error) break;
Var fn_var = compiler->module->constants.data[fn_index]; // At the top level the stack size should be 1 -- the class, before and
ASSERT(IS_OBJ_TYPE(fn_var, OBJ_FUNC), OOPS); // after compiling the class.
// 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.
ASSERT(compiler->parser.has_errors || ASSERT(compiler->parser.has_errors ||
compiler->func->stack_size == 0, OOPS); compiler->func->stack_size == 1, OOPS);
skipNewLines(compiler); skipNewLines(compiler);
if (compiler->parser.has_syntax_error) break; 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; compiler->parser.parsing_class = false;
vmPopTempRef(_vm); // cls. vmPopTempRef(_vm); // cls.
return cls_index; return cls_index;
} }
// Compile a function and return it's index in the module's function buffer. // Compile a function, if it's a literal function after this call a closure of
static int compileFunction(Compiler* compiler, FuncType fn_type) { // 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; const char* name;
int name_length; int name_length;
@ -2515,15 +2509,14 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
checkMaxConstantsReached(compiler, fn_index); 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); ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
int name_line = compiler->parser.previous.line; int name_line = compiler->parser.previous.line;
int g_index = compilerAddVariable(compiler, name, name_length, name_line); global_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.
} }
if (fn_type == FUNC_METHOD && strncmp(name, "_init", name_length) == 0) { 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 // 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 // outer function. If it's a literal function, we need to push a closure
// of it on the stack. // of it on the stack.
if (fn_type == FUNC_LITERAL) { emitOpcode(compiler, OP_PUSH_CLOSURE);
emitOpcode(compiler, OP_PUSH_CLOSURE); emitShort(compiler, fn_index);
emitShort(compiler, fn_index);
// Capture the upvalues when the closure is created. // Capture the upvalues when the closure is created.
for (int i = 0; i < curr_fn.ptr->upvalue_count; i++) { 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].is_immediate) ? 1 : 0);
emitByte(compiler, curr_fn.upvalues[i].index); 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. // Finish a block body.
@ -3381,6 +3379,7 @@ PkResult compile(PKVM* vm, Module* module, const char* source,
#if DUMP_BYTECODE #if DUMP_BYTECODE
dumpFunctionCode(compiler->parser.vm, module->body->fn); dumpFunctionCode(compiler->parser.vm, module->body->fn);
DEBUG_BREAK();
#endif #endif
// Return the compilation result. // Return the compilation result.

View File

@ -407,10 +407,17 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
case OP_STORE_GLOBAL: case OP_STORE_GLOBAL:
{ {
int index = READ_BYTE(); 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 // Prints: %5d '%s'\n
PRINT_INT(index); PRINT_INT(index);
PRINT(" '"); PRINT(" '");
PRINT(func->owner->name->data); PRINT(((String*)AS_OBJ(name))->data);
PRINT("'\n"); PRINT("'\n");
break; break;
} }
@ -465,6 +472,22 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
break; 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_CLOSE_UPVALUE:
case OP_POP: case OP_POP:
NO_ARGS(); NO_ARGS();

View File

@ -111,6 +111,14 @@ OPCODE(STORE_UPVALUE, 1, 0)
// params: 2 byte index. // params: 2 byte index.
OPCODE(PUSH_CLOSURE, 2, 1) 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. // Close the upvalue for the local at the stack top and pop it.
OPCODE(CLOSE_UPVALUE, 0, -1) OPCODE(CLOSE_UPVALUE, 0, -1)

View File

@ -799,6 +799,37 @@ L_vm_main_loop:
DISPATCH(); 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): OPCODE(CLOSE_UPVALUE):
{ {
closeUpvalues(fiber, fiber->sp - 1); closeUpvalues(fiber, fiber->sp - 1);