mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
Merge pull request #210 from ThakeeNathees/globals-init
functions and classes are runtime initialized
This commit is contained in:
commit
745387e307
@ -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.
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
31
src/pk_vm.c
31
src/pk_vm.c
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user