functions are replaced with clsoures

Every occurrence of function as first classes citizen is replaced
with closures yet upvalues are still to do.
This commit is contained in:
Thakee Nathees 2022-04-12 02:19:09 +05:30
parent 3b5da9cad3
commit 3feb9ac723
10 changed files with 222 additions and 189 deletions

View File

@ -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;
};

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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]];

View File

@ -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)

View File

@ -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;
}

View File

@ -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).

View File

@ -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.
}
}

View File

@ -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;