script renamed to modules

The name script is missleading as it only refering to the scripts
(the files that contain the statements) where as it could also
be the native module objects containing collection of native
functions.

After this commit, native functions can also have set owner module
and it won't be as confusing as before.
This commit is contained in:
Thakee Nathees 2022-04-11 13:34:22 +05:30
parent 133a560283
commit 20b99c60b3
11 changed files with 447 additions and 440 deletions

View File

@ -27,7 +27,8 @@
#define CLI_NOTICE \
"PocketLang " PK_VERSION_STRING " (https://github.com/ThakeeNathees/pocketlang/)\n" \
"Copyright(c) 2020 - 2021 ThakeeNathees.\n" \
"Copyright (c) 2020 - 2021 ThakeeNathees\n" \
"Copyright (c) 2021-2022 Pocketlang Contributors\n" \
"Free and open source software under the terms of the MIT license.\n"
// FIXME: the vm user data of cli.

View File

@ -84,7 +84,7 @@ typedef enum {
PK_LIST,
PK_MAP,
PK_RANGE,
PK_SCRIPT,
PK_MODULE,
PK_FUNCTION,
PK_FIBER,
PK_CLASS,

View File

@ -12,12 +12,12 @@
#include "pk_vm.h"
#include "pk_debug.h"
// The maximum number of variables (or global if compiling top level script)
// The maximum number of locals or global (if compiling top level module)
// to lookup from the compiling context. Also it's limited by it's opcode
// which is using a single byte value to identify the local.
#define MAX_VARIABLES 256
// The maximum number of constant literal a script can contain. Also it's
// The maximum number of constant literal a module can contain. Also it's
// limited by it's opcode which is using a short value to identify.
#define MAX_CONSTANTS (1 << 16)
@ -237,14 +237,14 @@ typedef struct {
} GrammarRule;
typedef enum {
DEPTH_SCRIPT = -2, //< Only used for script body function's depth.
DEPTH_MODULE = -2, //< Only used for module body function's depth.
DEPTH_GLOBAL = -1, //< Global variables.
DEPTH_LOCAL, //< Local scope. Increase with inner scope.
} Depth;
typedef enum {
FN_NATIVE, //< Native C function.
FN_SCRIPT, //< Script level functions defined with 'def'.
FN_SCRIPT, //< Script functions defined with 'def'.
FN_LITERAL, //< Literal functions defined with 'function(){...}'
} FuncType;
@ -285,8 +285,8 @@ typedef struct sLoop {
// compile time we can allow access to them at the global scope.
typedef struct sForwardName {
// Index of the short instruction that has the value of the name (in the
// names buffer of the script).
// Index of the short instruction that has the value of the global's name
// (in the names buffer of the module).
int instruction;
// The function where the name is used, and the instruction is belongs to.
@ -303,8 +303,8 @@ typedef struct sForwardName {
typedef struct sFunc {
// Scope of the function. -2 for script body, -1 for top level function and
// literal functions will have the scope where it declared.
// Scope of the function. -2 for module body function, -1 for top level
// function and literal functions will have the scope where it declared.
int depth;
// The actual function pointer which is being compiled.
@ -313,8 +313,8 @@ typedef struct sFunc {
// The index of the function in its module.
int index;
// If outer function of a literal or the script body function of a script
// function. Null for script body function.
// If outer function of this function, for top level function the outer
// function will be the module's body function.
struct sFunc* outer_func;
} Func;
@ -377,7 +377,7 @@ typedef struct sParser {
char si_name_quote;
// An array of implicitly forward declared names, which will be resolved once
// the script is completely compiled.
// the module is completely compiled.
ForwardName forwards[MAX_FORWARD_NAMES];
int forwards_count;
@ -393,11 +393,11 @@ struct Compiler {
// current compilation.
Parser parser;
// Each module (ie. script) will be compiled with it's own compiler and a
// module is imported, a new compiler is created for that module and it'll
// be added to the linked list of compilers at the begining. PKVM will use
// this compiler reference as a root object (objects which won't garbage
// collected) and the chain of compilers will be marked at the marking phase.
// Each module will be compiled with it's own compiler and a module is
// imported, a new compiler is created for that module and it'll be added to
// the linked list of compilers at the begining. PKVM will use this compiler
// reference as a root object (objects which won't garbage collected) and
// the chain of compilers will be marked at the marking phase.
//
// Here is how the chain change when a new compiler (compiler_3) created.
//
@ -409,9 +409,9 @@ struct Compiler {
const PkCompileOptions* options; //< To configure the compilation.
Script* script; //< Current script.
Loop* loop; //< Current loop.
Func* func; //< Current function.
Module* module; //< Current module that's being compiled.
Loop* loop; //< Current loop the we're parsing.
Func* func; //< Current function we're parsing.
// Current depth the compiler in (-1 means top level) 0 means function
// level and > 0 is inner scope.
@ -495,11 +495,11 @@ static void parserInit(Parser* parser, PKVM* vm, Compiler* compiler,
}
static void compilerInit(Compiler* compiler, PKVM* vm, const char* source,
Script* script, const PkCompileOptions* options) {
Module* module, const PkCompileOptions* options) {
compiler->next_compiler = NULL;
compiler->script = script;
compiler->module = module;
compiler->options = options;
compiler->scope_depth = DEPTH_GLOBAL;
@ -512,7 +512,7 @@ static void compilerInit(Compiler* compiler, PKVM* vm, const char* source,
compiler->new_local = false;
compiler->is_last_call = false;
parserInit(&compiler->parser, vm, compiler, source, script->path->data);
parserInit(&compiler->parser, vm, compiler, source, module->path->data);
// Cache the required built functions.
compiler->bifn_list_join = findBuiltinFunction(vm, "list_join", 9);
@ -578,7 +578,7 @@ static void parseError(Compiler* compiler, const char* fmt, ...) {
}
// Error caused when trying to resolve forward names (maybe more in the
// future), Which will be called once after compiling the script and thus we
// future), Which will be called once after compiling the module and thus we
// need to pass the line number the error originated from.
static void resolveError(Compiler* compiler, int line, const char* fmt, ...) {
va_list args;
@ -1270,7 +1270,7 @@ static NameSearchResult compilerSearchName(Compiler* compiler,
int index; // For storing the search result below.
// Search through globals.
index = scriptGetGlobalIndex(compiler->script, name, length);
index = moduleGetGlobalIndex(compiler->module, name, length);
if (index != -1) {
result.type = NAME_GLOBAL_VAR;
result.index = index;
@ -1525,7 +1525,6 @@ static void exprFunc(Compiler* compiler) {
emitShort(compiler, fn_index);
}
// Local/global variables, script/native/builtin functions name.
static void exprName(Compiler* compiler) {
const char* start = compiler->parser.previous.start;
@ -1767,8 +1766,8 @@ static void exprAttrib(Compiler* compiler) {
const char* name = compiler->parser.previous.start;
int length = compiler->parser.previous.length;
// Store the name in script's names.
int index = scriptAddName(compiler->script, compiler->parser.vm,
// Store the name in module's names buffer.
int index = moduleAddName(compiler->module, compiler->parser.vm,
name, length);
if (compiler->l_value && matchAssignment(compiler)) {
@ -1875,7 +1874,7 @@ static int compilerAddVariable(Compiler* compiler, const char* name,
bool max_vars_reached = false;
const char* var_type = ""; // For max variables reached error message.
if (compiler->scope_depth == DEPTH_GLOBAL) {
if (compiler->script->globals.count >= MAX_VARIABLES) {
if (compiler->module->globals.count >= MAX_VARIABLES) {
max_vars_reached = true;
var_type = "globals";
}
@ -1886,7 +1885,7 @@ static int compilerAddVariable(Compiler* compiler, const char* name,
}
}
if (max_vars_reached) {
parseError(compiler, "A script should contain at most %d %s.",
parseError(compiler, "A module should contain at most %d %s.",
MAX_VARIABLES, var_type);
return -1;
}
@ -1894,7 +1893,7 @@ static int compilerAddVariable(Compiler* compiler, const char* name,
// Add the variable and return it's index.
if (compiler->scope_depth == DEPTH_GLOBAL) {
return (int)scriptAddGlobal(compiler->parser.vm, compiler->script,
return (int)moduleAddGlobal(compiler->parser.vm, compiler->module,
name, length, VAR_NULL);
} else {
Local* local = &compiler->locals [compiler->local_count];
@ -1911,7 +1910,7 @@ static int compilerAddVariable(Compiler* compiler, const char* name,
static void compilerAddForward(Compiler* compiler, int instruction, Fn* fn,
const char* name, int length, int line) {
if (compiler->parser.forwards_count == MAX_FORWARD_NAMES) {
parseError(compiler, "A script should contain at most %d implicit forward "
parseError(compiler, "A module should contain at most %d implicit forward "
"function declarations.", MAX_FORWARD_NAMES);
return;
}
@ -1925,14 +1924,14 @@ static void compilerAddForward(Compiler* compiler, int instruction, Fn* fn,
forward->line = line;
}
// Add a literal constant to scripts literals and return it's index.
// Add a literal constant to module literals and return it's index.
static int compilerAddConstant(Compiler* compiler, Var value) {
pkVarBuffer* constants = &compiler->script->constants;
pkVarBuffer* constants = &compiler->module->constants;
uint32_t index = scriptAddConstant(compiler->parser.vm,
compiler->script, value);
uint32_t index = moduleAddConstant(compiler->parser.vm,
compiler->module, value);
if (index >= MAX_CONSTANTS) {
parseError(compiler, "A script should contain at most %d "
parseError(compiler, "A module should contain at most %d "
"unique constants.", MAX_CONSTANTS);
}
return (int)index;
@ -2105,7 +2104,7 @@ typedef enum {
static void compileStatement(Compiler* compiler);
static void compileBlockBody(Compiler* compiler, BlockType type);
// Compile a class and return it's index in the script's types buffer.
// Compile a class and return it's index in the module's types buffer.
static int compileClass(Compiler* compiler) {
// Consume the name of the type.
@ -2115,7 +2114,7 @@ static int compileClass(Compiler* compiler) {
// Create a new class.
int cls_index, ctor_index;
Class* cls = newClass(compiler->parser.vm, compiler->script,
Class* cls = newClass(compiler->parser.vm, compiler->module,
name, (uint32_t)name_len, &cls_index, &ctor_index);
cls->ctor->arity = 0;
@ -2126,16 +2125,16 @@ static int compileClass(Compiler* compiler) {
compiler->parser.previous.start,
compiler->parser.previous.length,
compiler->parser.previous.line);
scriptSetGlobal(compiler->script, index, VAR_OBJ(cls));
moduleSetGlobal(compiler->module, index, VAR_OBJ(cls));
// Check count exceeded.
if (cls_index >= MAX_CONSTANTS || ctor_index >= MAX_CONSTANTS) {
parseError(compiler, "A script should contain at most %d "
parseError(compiler, "A module should contain at most %d "
"unique constants.", MAX_CONSTANTS);
}
// Compile the constructor function.
ASSERT(compiler->func->ptr == compiler->script->body, OOPS);
ASSERT(compiler->func->ptr == compiler->module->body, OOPS);
Func curr_fn;
compilerPushFunc(compiler, &curr_fn, cls->ctor, ctor_index);
compilerEnterBlock(compiler);
@ -2153,12 +2152,12 @@ static int compileClass(Compiler* compiler) {
const char* f_name = compiler->parser.previous.start;
int f_len = compiler->parser.previous.length;
uint32_t f_index = scriptAddName(compiler->script, compiler->parser.vm,
uint32_t f_index = moduleAddName(compiler->module, compiler->parser.vm,
f_name, f_len);
String* new_name = compiler->script->names.data[f_index];
String* new_name = compiler->module->names.data[f_index];
for (uint32_t i = 0; i < cls->field_names.count; i++) {
String* prev = compiler->script->names.data[cls->field_names.data[i]];
String* prev = compiler->module->names.data[cls->field_names.data[i]];
if (IS_STR_EQ(new_name, prev)) {
parseError(compiler, "Class field with name '%s' already exists.",
new_name->data);
@ -2194,7 +2193,7 @@ static int compileClass(Compiler* compiler) {
return -1; // TODO;
}
// Compile a function and return it's index in the script's function buffer.
// Compile a function and return it's index in the module's function buffer.
static int compileFunction(Compiler* compiler, FuncType fn_type) {
const char* name;
@ -2212,10 +2211,10 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
int fn_index;
Function* func = newFunction(compiler->parser.vm, name, name_length,
compiler->script, fn_type == FN_NATIVE, NULL,
compiler->module, fn_type == FN_NATIVE, NULL,
&fn_index);
if (fn_index >= MAX_CONSTANTS) {
parseError(compiler, "A script should contain at most %d "
parseError(compiler, "A module should contain at most %d "
"unique constants.", MAX_CONSTANTS);
}
@ -2225,7 +2224,7 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
int name_line = compiler->parser.previous.line;
int g_index = compilerAddVariable(compiler, name, name_length, name_line);
scriptSetGlobal(compiler->script, g_index, VAR_OBJ(func));
moduleSetGlobal(compiler->module, g_index, VAR_OBJ(func));
}
Func curr_fn;
@ -2330,9 +2329,9 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
}
// Import a file at the given path (first it'll be resolved from the current
// path) and return it as a script pointer. And it'll emit opcodes to push
// that script to the stack.
static Script* importFile(Compiler* compiler, const char* path) {
// path) and return it as a module pointer. And it'll emit opcodes to push
// that module to the stack.
static Module* importFile(Compiler* compiler, const char* path) {
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
PKVM* vm = compiler->parser.vm;
@ -2340,33 +2339,34 @@ static Script* importFile(Compiler* compiler, const char* path) {
// Resolve the path.
PkStringPtr resolved = { path, NULL, NULL };
if (vm->config.resolve_path_fn != NULL) {
resolved = vm->config.resolve_path_fn(vm, compiler->script->path->data,
resolved = vm->config.resolve_path_fn(vm, compiler->module->path->data,
path);
}
if (resolved.string == NULL) {
parseError(compiler, "Cannot resolve path '%s' from '%s'", path,
compiler->script->path->data);
compiler->module->path->data);
}
// Create new string for the resolved path. And free the resolved path.
int index = (int)scriptAddName(compiler->script, compiler->parser.vm,
int index = (int)moduleAddName(compiler->module, compiler->parser.vm,
resolved.string, (uint32_t)strlen(resolved.string));
String* path_name = compiler->script->names.data[index];
String* path_name = compiler->module->names.data[index];
if (resolved.on_done != NULL) resolved.on_done(vm, resolved);
// Check if the script already exists.
Var entry = mapGet(vm->scripts, VAR_OBJ(path_name));
// Check if the script already compiled and cached in the PKVM.
Var entry = mapGet(vm->modules, VAR_OBJ(path_name));
if (!IS_UNDEF(entry)) {
ASSERT(IS_OBJ_TYPE(entry, OBJ_SCRIPT), OOPS);
ASSERT(IS_OBJ_TYPE(entry, OBJ_MODULE), OOPS);
// Push the script on the stack.
// Push the compiled script on the stack.
emitOpcode(compiler, OP_IMPORT);
emitShort(compiler, index);
return (Script*)AS_OBJ(entry);
return (Module*)AS_OBJ(entry);
}
// The script not exists, make sure we have the script loading api function.
// The script not exists in the VM, make sure we have the script loading
// api function.
if (vm->config.load_script_fn == NULL) {
parseError(compiler, "Cannot import. The hosting application haven't "
"registered the script loading API");
@ -2380,24 +2380,24 @@ static Script* importFile(Compiler* compiler, const char* path) {
return NULL;
}
// Make a new script and to compile it.
Script* scr = newScript(vm, path_name, false);
vmPushTempRef(vm, &scr->_super); // scr.
mapSet(vm, vm->scripts, VAR_OBJ(path_name), VAR_OBJ(scr));
// Make a new module and to compile it.
Module* module = newModule(vm, path_name, false);
vmPushTempRef(vm, &module->_super); // scr.
mapSet(vm, vm->modules, VAR_OBJ(path_name), VAR_OBJ(module));
vmPopTempRef(vm); // scr.
// Push the script on the stack.
// Push the compiled script on the stack.
emitOpcode(compiler, OP_IMPORT);
emitShort(compiler, index);
// Option for the compilation, even if we're running on repl mode the
// imported script cannot run on repl mode.
// Even if we're running on repl mode the imported module cannot run on
// repl mode.
PkCompileOptions options = pkNewCompilerOptions();
if (compiler->options) options = *compiler->options;
options.repl_mode = false;
// Compile the source to the script and clean the source.
PkResult result = compile(vm, scr, source.string, &options);
// Compile the source to the module and clean the source.
PkResult result = compile(vm, module, source.string, &options);
if (source.on_done != NULL) source.on_done(vm, source);
if (result != PK_RESULT_SUCCESS) {
@ -2405,41 +2405,41 @@ static Script* importFile(Compiler* compiler, const char* path) {
path_name->data);
}
return scr;
return module;
}
// Import the core library from the vm's core_libs and it'll emit opcodes to
// push that script to the stack.
static Script* importCoreLib(Compiler* compiler, const char* name_start,
// Import the native module from the PKVM's core_libs and it'll emit opcodes
// to push that module to the stack.
static Module* importCoreLib(Compiler* compiler, const char* name_start,
int name_length) {
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
// Add the name to the script's name buffer, we need it as a key to the
// vm's script cache.
int index = (int)scriptAddName(compiler->script, compiler->parser.vm,
// Add the name to the module's name buffer, we need it as a key to the
// PKVM's module cache.
int index = (int)moduleAddName(compiler->module, compiler->parser.vm,
name_start, name_length);
String* module = compiler->script->names.data[index];
String* module_name = compiler->module->names.data[index];
Var entry = mapGet(compiler->parser.vm->core_libs, VAR_OBJ(module));
Var entry = mapGet(compiler->parser.vm->core_libs, VAR_OBJ(module_name));
if (IS_UNDEF(entry)) {
parseError(compiler, "No module named '%s' exists.", module->data);
parseError(compiler, "No module named '%s' exists.", module_name->data);
return NULL;
}
// Push the script on the stack.
// Push the module on the stack.
emitOpcode(compiler, OP_IMPORT);
emitShort(compiler, index);
ASSERT(IS_OBJ_TYPE(entry, OBJ_SCRIPT), OOPS);
return (Script*)AS_OBJ(entry);
ASSERT(IS_OBJ_TYPE(entry, OBJ_MODULE), OOPS);
return (Module*)AS_OBJ(entry);
}
// Push the imported script on the stack and return the pointer. It could be
// Push the imported module on the stack and return the pointer. It could be
// either core library or a local import.
static inline Script* compilerImport(Compiler* compiler) {
static inline Module* compilerImport(Compiler* compiler) {
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
// Get the script (from core libs or vm's cache or compile new one).
// Get the module (from native libs or VM's cache or compile new one).
// And push it on the stack.
if (match(compiler, TK_NAME)) { //< Core library.
return importCoreLib(compiler, compiler->parser.previous.start,
@ -2489,7 +2489,7 @@ static int compilerImportName(Compiler* compiler, int line,
}
// This will called by the compilerImportAll() function to import a single
// entry from the imported script. (could be a function or global variable).
// entry from the imported module. (could be a function or global variable).
static void compilerImportSingleEntry(Compiler* compiler,
const char* name, uint32_t length) {
@ -2500,11 +2500,11 @@ static void compilerImportSingleEntry(Compiler* compiler,
// Line number of the variables which will be bind to the imported symbol.
int line = compiler->parser.previous.line;
// Add the name to the **current** script's name buffer.
int name_index = (int)scriptAddName(compiler->script, compiler->parser.vm,
// Add the name to the **current** module's name buffer.
int name_index = (int)moduleAddName(compiler->module, compiler->parser.vm,
name, length);
// Get the function from the script.
// Get the global/function/class from the module.
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
emitShort(compiler, name_index);
@ -2513,18 +2513,18 @@ static void compilerImportSingleEntry(Compiler* compiler,
emitOpcode(compiler, OP_POP);
}
// Import all from the script, which is also would be at the top of the stack
// Import all from the module, which is also would be at the top of the stack
// before executing the below instructions.
static void compilerImportAll(Compiler* compiler, Script* script) {
static void compilerImportAll(Compiler* compiler, Module* module) {
ASSERT(script != NULL, OOPS);
ASSERT(module != NULL, OOPS);
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
// Import all globals.
ASSERT(script->global_names.count == script->globals.count, OOPS);
for (uint32_t i = 0; i < script->globals.count; i++) {
ASSERT(script->global_names.data[i] < script->names.count, OOPS);
const String* name = script->names.data[script->global_names.data[i]];
ASSERT(module->global_names.count == module->globals.count, OOPS);
for (uint32_t i = 0; i < module->globals.count; i++) {
ASSERT(module->global_names.data[i] < module->names.count, OOPS);
const String* name = module->names.data[module->global_names.data[i]];
compilerImportSingleEntry(compiler, name->data, name->length);
}
@ -2536,9 +2536,9 @@ static void compileFromImport(Compiler* compiler) {
// Import the library and push it on the stack. If the import failed
// lib_from would be NULL.
Script* lib_from = compilerImport(compiler);
Module* lib_from = compilerImport(compiler);
// At this point the script would be on the stack before executing the next
// At this point the module would be on the stack before executing the next
// instruction.
consume(compiler, TK_IMPORT, "Expected keyword 'import'.");
@ -2548,14 +2548,14 @@ static void compileFromImport(Compiler* compiler) {
} else {
do {
// Consume the symbol name to import from the script.
// Consume the symbol name to import from the module.
consume(compiler, TK_NAME, "Expected symbol to import.");
const char* name = compiler->parser.previous.start;
uint32_t length = (uint32_t)compiler->parser.previous.length;
int line = compiler->parser.previous.line;
// Add the name of the symbol to the names buffer.
int name_index = (int)scriptAddName(compiler->script,
int name_index = (int)moduleAddName(compiler->module,
compiler->parser.vm,
name, length);
@ -2600,9 +2600,9 @@ static void compileRegularImport(Compiler* compiler) {
// Import the library and push it on the stack. If it cannot import,
// the lib would be null, but we're not terminating here, just continue
// parsing for cascaded errors.
Script* lib = compilerImport(compiler);
Module* lib = compilerImport(compiler);
// variable to bind the imported script.
// variable to bind the imported module.
int var_index = -1;
// Check if it has an alias, if so bind the variable with that name.
@ -2621,7 +2621,7 @@ static void compileRegularImport(Compiler* compiler) {
} else {
// If it has a module name use it as binding variable.
// Core libs names are it's module name but for local libs it's optional
// to define a module name for a script.
// to define a module name for a module.
if (lib && lib->name != NULL) {
// Get the variable to bind the imported symbol, if we already have a
@ -2879,7 +2879,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->script->body &&
compiler->func->ptr == compiler->module->body &&
is_expression /*&& compiler->scope_depth == DEPTH_GLOBAL*/) {
emitOpcode(compiler, OP_REPL_PRINT);
}
@ -2887,7 +2887,7 @@ static void compileStatement(Compiler* compiler) {
if (is_temporary) emitOpcode(compiler, OP_POP);
}
// Compile statements that are only valid at the top level of the script. Such
// Compile statements that are only valid at the top level of the module. Such
// as import statement, function define, and if we're running REPL mode top
// level expression's evaluated value will be printed.
static void compileTopLevelStatement(Compiler* compiler) {
@ -2925,7 +2925,7 @@ static void compileTopLevelStatement(Compiler* compiler) {
}
PkResult compile(PKVM* vm, Script* script, const char* source,
PkResult compile(PKVM* vm, Module* module, const char* source,
const PkCompileOptions* options) {
// Skip utf8 BOM if there is any.
@ -2933,33 +2933,33 @@ PkResult compile(PKVM* vm, Script* script, const char* source,
Compiler _compiler;
Compiler* compiler = &_compiler; //< Compiler pointer for quick access.
compilerInit(compiler, vm, source, script, options);
compilerInit(compiler, vm, source, module, options);
// If compiling for an imported script the vm->compiler would be the compiler
// of the script that imported this script. Add the all the compilers into a
// If compiling for an imported module the vm->compiler would be the compiler
// of the module that imported this module. Add the all the compilers into a
// link list.
compiler->next_compiler = vm->compiler;
vm->compiler = compiler;
// If the script doesn't has a body by default, it's probably was created by
// If the module doesn't has a body by default, it's probably was created by
// the native api function (pkNewModule() that'll return a module without a
// main function) so just create and add the function here.
if (script->body == NULL) scriptAddMain(vm, script);
if (module->body == NULL) moduleAddMain(vm, module);
// If we're compiling for a script that was already compiled (when running
// 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 script and use a new body func.
pkByteBufferClear(&script->body->fn->opcodes, vm);
// just use the globals and functions of the module and use a new body func.
pkByteBufferClear(&module->body->fn->opcodes, vm);
// Remember the count of constants, names, and globals, If the compilation
// failed discard all of them and roll back.
uint32_t constants_count = script->constants.count;
uint32_t names_count = script->names.count;
uint32_t globals_count = script->globals.count;
uint32_t constants_count = module->constants.count;
uint32_t names_count = module->names.count;
uint32_t globals_count = module->globals.count;
Func curr_fn;
curr_fn.depth = DEPTH_SCRIPT;
curr_fn.ptr = script->body;
curr_fn.depth = DEPTH_MODULE;
curr_fn.ptr = module->body;
curr_fn.outer_func = NULL;
compiler->func = &curr_fn;
@ -2970,17 +2970,17 @@ PkResult compile(PKVM* vm, Script* script, const char* source,
if (match(compiler, TK_MODULE)) {
// If the script running a REPL or compiled multiple times by hosting
// If the module running a REPL or compiled multiple times by hosting
// application module attribute might already set. In that case make it
// Compile error.
if (script->name != NULL) {
if (module->name != NULL) {
parseError(compiler, "Module name already defined.");
} else {
consume(compiler, TK_NAME, "Expected a name for the module.");
const char* name = compiler->parser.previous.start;
uint32_t len = compiler->parser.previous.length;
script->name = newStringLength(vm, name, len);
module->name = newStringLength(vm, name, len);
consumeEndStatement(compiler);
}
}
@ -2997,7 +2997,7 @@ PkResult compile(PKVM* vm, Script* script, const char* source,
ForwardName* forward = &compiler->parser.forwards[i];
const char* name = forward->name;
int length = forward->length;
int index = scriptGetGlobalIndex(compiler->script, name, (uint32_t)length);
int index = moduleGetGlobalIndex(compiler->module, name, (uint32_t)length);
if (index != -1) {
patchForward(compiler, forward->func, forward->instruction, index);
} else {
@ -3014,13 +3014,13 @@ PkResult compile(PKVM* vm, Script* script, const char* source,
// If compilation failed, discard all the invalid functions and globals.
if (compiler->parser.has_errors) {
script->constants.count = constants_count;
script->names.count = names_count;
script->globals.count = script->global_names.count = globals_count;
module->constants.count = constants_count;
module->names.count = names_count;
module->globals.count = module->global_names.count = globals_count;
}
#if DUMP_BYTECODE
dumpFunctionCode(compiler->parser.vm, script->body);
dumpFunctionCode(compiler->parser.vm, module->body);
#endif
// Return the compilation result.
@ -3033,24 +3033,24 @@ PkResult compile(PKVM* vm, Script* script, const char* source,
return PK_RESULT_SUCCESS;
}
PkResult pkCompileModule(PKVM* vm, PkHandle* module, PkStringPtr source,
PkResult pkCompileModule(PKVM* vm, PkHandle* module_handle, PkStringPtr source,
const PkCompileOptions* options) {
__ASSERT(module != NULL, "Argument module was NULL.");
Var scr = module->value;
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
Script* script = (Script*)AS_OBJ(scr);
__ASSERT(module_handle != NULL, "Argument module was NULL.");
__ASSERT(IS_OBJ_TYPE(module_handle->value, OBJ_MODULE),
"Given handle is not a module.");
Module* module = (Module*)AS_OBJ(module_handle->value);
PkResult result = compile(vm, script, source.string, options);
PkResult result = compile(vm, module, source.string, options);
if (source.on_done) source.on_done(vm, source);
return result;
}
void compilerMarkObjects(PKVM* vm, Compiler* compiler) {
// Mark the script which is currently being compiled.
markObject(vm, &compiler->script->_super);
// Mark the module which is currently being compiled.
markObject(vm, &compiler->module->_super);
// Mark the string literals (they haven't added to the script's literal
// Mark the string literals (they haven't added to the module's literal
// buffer yet).
markValue(vm, compiler->parser.current.value);
markValue(vm, compiler->parser.previous.value);

View File

@ -25,11 +25,11 @@ typedef enum {
typedef struct Compiler Compiler;
// This will take source code as a cstring, compiles it to pocketlang bytecodes
// and append them to the script's implicit main function ("$(SourceBody)").
// On a successfull compilation it'll return PK_RESULT_SUCCESS, otherwise it'll
// return PK_RESULT_COMPILE_ERROR but if repl_mode set in the [options], and
// we've reached and unexpected EOF it'll return PK_RESULT_UNEXPECTED_EOF.
PkResult compile(PKVM* vm, Script* script, const char* source,
// and append them to the module's implicit main function. On a successfull
// compilation it'll return PK_RESULT_SUCCESS, otherwise it'll return
// PK_RESULT_COMPILE_ERROR but if repl_mode set in the [options], and we've
// reached and unexpected EOF it'll return PK_RESULT_UNEXPECTED_EOF.
PkResult compile(PKVM* vm, Module* module, const char* source,
const PkCompileOptions* options);
// Mark the heap allocated objects of the compiler at the garbage collection

View File

@ -36,55 +36,53 @@
/* CORE PUBLIC API */
/*****************************************************************************/
// Create a new module with the given [name] and returns as a Script* for
// Create a new module with the given [name] and returns as a Module* for
// internal. Which will be wrapped by pkNewModule to return a pkHandle*.
static Script* newModuleInternal(PKVM* vm, const char* name);
static Module* newModuleInternal(PKVM* vm, const char* name);
// Adds a function to the script with the give properties and add the function
// Adds a function to the module with the give properties and add the function
// to the module's globals variables.
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
static void moduleAddFunctionInternal(PKVM* vm, Module* module,
const char* name, pkNativeFn fptr,
int arity, const char* docstring);
// pkNewModule implementation (see pocketlang.h for description).
PkHandle* pkNewModule(PKVM* vm, const char* name) {
Script* module = newModuleInternal(vm, name);
Module* module = newModuleInternal(vm, name);
return vmNewHandle(vm, VAR_OBJ(module));
}
// pkModuleAddGlobal implementation (see pocketlang.h for description).
PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
const char* name, PkHandle* value) {
__ASSERT(module != NULL, "Argument module was NULL.");
__ASSERT(value != NULL, "Argument value was NULL.");
Var scr = module->value;
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
__ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
scriptAddGlobal(vm, (Script*)AS_OBJ(scr), name, (uint32_t)strlen(name),
value->value);
moduleAddGlobal(vm, (Module*)AS_OBJ(module->value),
name, (uint32_t)strlen(name), value->value);
}
// pkModuleAddFunction implementation (see pocketlang.h for description).
void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
pkNativeFn fptr, int arity) {
__ASSERT(module != NULL, "Argument module was NULL.");
Var scr = module->value;
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
moduleAddFunctionInternal(vm, (Script*)AS_OBJ(scr), name, fptr, arity,
__ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
moduleAddFunctionInternal(vm, (Module*)AS_OBJ(module->value),
name, fptr, arity,
NULL /*TODO: Public API for function docstring.*/);
}
PkHandle* pkGetMainFunction(PKVM* vm, PkHandle* module) {
__ASSERT(module != NULL, "Argument module was NULL.");
Var scr = module->value;
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
Script* script = (Script*)AS_OBJ(scr);
__ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
Module* _module = (Module*)AS_OBJ(module->value);
int main_index = scriptGetGlobalIndex(script, IMPLICIT_MAIN_NAME,
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)script->constants.count);
return vmNewHandle(vm, script->constants.data[main_index]);
ASSERT_INDEX(main_index, (int)_module->constants.count);
return vmNewHandle(vm, _module->constants.data[main_index]);
}
// A convenient macro to get the nth (1 based) argument of the current
@ -131,13 +129,11 @@ do { \
} \
} while (false)
// pkGetArgc implementation (see pocketlang.h for description).
int pkGetArgc(const PKVM* vm) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
return ARGC;
}
// pkCheckArgcRange implementation (see pocketlang.h for description).
bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) {
ASSERT(min <= max, "invalid argc range (min > max).");
@ -157,7 +153,6 @@ bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) {
return true;
}
// pkGetArg implementation (see pocketlang.h for description).
PkVar pkGetArg(const PKVM* vm, int arg) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index.");
@ -165,7 +160,6 @@ PkVar pkGetArg(const PKVM* vm, int arg) {
return &(ARG(arg));
}
// pkGetArgBool implementation (see pocketlang.h for description).
bool pkGetArgBool(PKVM* vm, int arg, bool* value) {
CHECK_GET_ARG_API_ERRORS();
@ -174,7 +168,6 @@ bool pkGetArgBool(PKVM* vm, int arg, bool* value) {
return true;
}
// pkGetArgNumber implementation (see pocketlang.h for description).
bool pkGetArgNumber(PKVM* vm, int arg, double* value) {
CHECK_GET_ARG_API_ERRORS();
@ -193,7 +186,6 @@ bool pkGetArgNumber(PKVM* vm, int arg, double* value) {
return true;
}
// pkGetArgString implementation (see pocketlang.h for description).
bool pkGetArgString(PKVM* vm, int arg, const char** value, uint32_t* length) {
CHECK_GET_ARG_API_ERRORS();
@ -211,7 +203,6 @@ bool pkGetArgString(PKVM* vm, int arg, const char** value, uint32_t* length) {
return true;
}
// pkGetArgInstance implementation (see pocketlang.h for description).
bool pkGetArgInst(PKVM* vm, int arg, uint32_t id, void** value) {
CHECK_GET_ARG_API_ERRORS();
@ -239,7 +230,6 @@ bool pkGetArgInst(PKVM* vm, int arg, uint32_t id, void** value) {
return true;
}
// pkGetArgValue implementation (see pocketlang.h for description).
bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
CHECK_GET_ARG_API_ERRORS();
@ -255,42 +245,34 @@ bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
return true;
}
// pkReturnNull implementation (see pocketlang.h for description).
void pkReturnNull(PKVM* vm) {
RET(VAR_NULL);
}
// pkReturnBool implementation (see pocketlang.h for description).
void pkReturnBool(PKVM* vm, bool value) {
RET(VAR_BOOL(value));
}
// pkReturnNumber implementation (see pocketlang.h for description).
void pkReturnNumber(PKVM* vm, double value) {
RET(VAR_NUM(value));
}
// pkReturnString implementation (see pocketlang.h for description).
void pkReturnString(PKVM* vm, const char* value) {
RET(VAR_OBJ(newString(vm, value)));
}
// pkReturnStringLength implementation (see pocketlang.h for description).
void pkReturnStringLength(PKVM* vm, const char* value, size_t length) {
RET(VAR_OBJ(newStringLength(vm, value, (uint32_t)length)));
}
// pkReturnValue implementation (see pocketlang.h for description).
void pkReturnValue(PKVM* vm, PkVar value) {
RET(*(Var*)value);
}
// pkReturnHandle implementation (see pocketlang.h for description).
void pkReturnHandle(PKVM* vm, PkHandle* handle) {
RET(handle->value);
}
// pkReturnInstNative implementation (see pocketlang.h for description).
void pkReturnInstNative(PKVM* vm, void* data, uint32_t id) {
RET(VAR_OBJ(newInstanceNative(vm, data, id)));
}
@ -417,7 +399,6 @@ static inline bool validateCond(PKVM* vm, bool condition, const char* err) {
/* SHARED FUNCTIONS */
/*****************************************************************************/
// findBuiltinFunction implementation (see core.h for description).
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 &&
@ -428,24 +409,21 @@ int findBuiltinFunction(const PKVM* vm, const char* name, uint32_t length) {
return -1;
}
// getBuiltinFunction implementation (see core.h for description).
Function* getBuiltinFunction(const PKVM* vm, int index) {
ASSERT_INDEX((uint32_t)index, vm->builtins_count);
return vm->builtins[index].fn;
}
// getBuiltinFunctionName implementation (see core.h for description).
const char* getBuiltinFunctionName(const PKVM* vm, int index) {
ASSERT_INDEX((uint32_t)index, vm->builtins_count);
return vm->builtins[index].name;
}
// getCoreLib implementation (see core.h for description).
Script* getCoreLib(const PKVM* vm, String* name) {
Module* getCoreLib(const PKVM* vm, String* name) {
Var lib = mapGet(vm->core_libs, VAR_OBJ(name));
if (IS_UNDEF(lib)) return NULL;
ASSERT(IS_OBJ_TYPE(lib, OBJ_SCRIPT), OOPS);
return (Script*)AS_OBJ(lib);
ASSERT(IS_OBJ_TYPE(lib, OBJ_MODULE), OOPS);
return (Module*)AS_OBJ(lib);
}
/*****************************************************************************/
@ -777,42 +755,40 @@ DEF(coreMapRemove,
/* CORE MODULE METHODS */
/*****************************************************************************/
// Create a module and add it to the vm's core modules, returns the script.
static Script* newModuleInternal(PKVM* vm, const char* name) {
// Create a module and add it to the vm's core modules, returns the module.
static Module* newModuleInternal(PKVM* vm, const char* name) {
// Create a new Script for the module.
String* _name = newString(vm, name);
vmPushTempRef(vm, &_name->_super);
vmPushTempRef(vm, &_name->_super); // _name
// Check if any module with the same name already exists and assert to the
// hosting application.
if (!IS_UNDEF(mapGet(vm->core_libs, VAR_OBJ(_name)))) {
vmPopTempRef(vm); // _name
__ASSERT(false, stringFormat(vm,
"A module named '$' already exists", name)->data);
}
Script* scr = newScript(vm, _name, true);
Module* module = newModule(vm, _name, true);
vmPopTempRef(vm); // _name
// Add the script to core_libs.
vmPushTempRef(vm, &scr->_super);
mapSet(vm, vm->core_libs, VAR_OBJ(_name), VAR_OBJ(scr));
vmPopTempRef(vm);
// Add the module to core_libs.
vmPushTempRef(vm, &module->_super); // module.
mapSet(vm, vm->core_libs, VAR_OBJ(_name), VAR_OBJ(module));
vmPopTempRef(vm); // module.
return scr;
return module;
}
// An internal function to add a function to the given [script].
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
// An internal function to add a function to the given [module].
static void moduleAddFunctionInternal(PKVM* vm, Module* module,
const char* name, pkNativeFn fptr,
int arity, const char* docstring) {
Function* fn = newFunction(vm, name, (int)strlen(name),
script, true, docstring, NULL);
module, true, docstring, NULL);
fn->native = fptr;
fn->arity = arity;
scriptAddGlobal(vm, script, name, (uint32_t)strlen(name), VAR_OBJ(fn));
moduleAddGlobal(vm, module, name, (uint32_t)strlen(name), VAR_OBJ(fn));
}
// 'lang' library methods.
@ -1261,7 +1237,7 @@ void initializeCore(PKVM* vm) {
// Core Modules /////////////////////////////////////////////////////////////
Script* lang = newModuleInternal(vm, "lang");
Module* lang = newModuleInternal(vm, "lang");
MODULE_ADD_FN(lang, "clock", stdLangClock, 0);
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
MODULE_ADD_FN(lang, "disas", stdLangDisas, 1);
@ -1270,7 +1246,7 @@ void initializeCore(PKVM* vm) {
MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0);
#endif
Script* math = newModuleInternal(vm, "math");
Module* math = newModuleInternal(vm, "math");
MODULE_ADD_FN(math, "floor", stdMathFloor, 1);
MODULE_ADD_FN(math, "ceil", stdMathCeil, 1);
MODULE_ADD_FN(math, "pow", stdMathPow, 2);
@ -1300,9 +1276,9 @@ void initializeCore(PKVM* vm) {
// Note that currently it's mutable (since it's a global variable, not
// constant and pocketlang doesn't support constant) so the user shouldn't
// modify the PI, like in python.
scriptAddGlobal(vm, math, "PI", 2, VAR_NUM(M_PI));
moduleAddGlobal(vm, math, "PI", 2, VAR_NUM(M_PI));
Script* fiber = newModuleInternal(vm, "Fiber");
Module* fiber = newModuleInternal(vm, "Fiber");
MODULE_ADD_FN(fiber, "new", stdFiberNew, 1);
MODULE_ADD_FN(fiber, "run", stdFiberRun, -1);
MODULE_ADD_FN(fiber, "resume", stdFiberResume, -1);
@ -1349,7 +1325,7 @@ Var varAdd(PKVM* vm, Var v1, Var v2) {
case OBJ_MAP:
case OBJ_RANGE:
case OBJ_SCRIPT:
case OBJ_MODULE:
case OBJ_FUNC:
case OBJ_CLOSURE:
case OBJ_UPVALUE:
@ -1562,7 +1538,7 @@ bool varContains(PKVM* vm, Var elem, Var container) {
} break;
case OBJ_RANGE:
case OBJ_SCRIPT:
case OBJ_MODULE:
case OBJ_FUNC:
case OBJ_CLOSURE:
case OBJ_UPVALUE:
@ -1670,15 +1646,15 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
UNREACHABLE();
}
case OBJ_SCRIPT:
case OBJ_MODULE:
{
Script* scr = (Script*)obj;
Module* module = (Module*)obj;
// Search in globals.
int index = scriptGetGlobalIndex(scr, attrib->data, attrib->length);
int index = moduleGetGlobalIndex(module, attrib->data, attrib->length);
if (index != -1) {
ASSERT_INDEX((uint32_t)index, scr->globals.count);
return scr->globals.data[index];
ASSERT_INDEX((uint32_t)index, module->globals.count);
return module->globals.data[index];
}
ERR_NO_ATTRIB(vm, on, attrib);
@ -1795,14 +1771,14 @@ do { \
ERR_NO_ATTRIB(vm, on, attrib);
return;
case OBJ_SCRIPT: {
Script* scr = (Script*)obj;
case OBJ_MODULE: {
Module* module = (Module*)obj;
// Check globals.
int index = scriptGetGlobalIndex(scr, attrib->data, attrib->length);
int index = moduleGetGlobalIndex(module, attrib->data, attrib->length);
if (index != -1) {
ASSERT_INDEX((uint32_t)index, scr->globals.count);
scr->globals.data[index] = value;
ASSERT_INDEX((uint32_t)index, module->globals.count);
module->globals.data[index] = value;
return;
}
@ -1909,7 +1885,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
}
case OBJ_RANGE:
case OBJ_SCRIPT:
case OBJ_MODULE:
case OBJ_FUNC:
case OBJ_CLOSURE:
case OBJ_UPVALUE:
@ -1961,7 +1937,7 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
}
case OBJ_RANGE:
case OBJ_SCRIPT:
case OBJ_MODULE:
case OBJ_FUNC:
case OBJ_CLOSURE:
case OBJ_UPVALUE:

View File

@ -25,7 +25,7 @@ const char* getBuiltinFunctionName(const PKVM* vm, int index);
// Return the core library with the [name] if exists in the core libs,
// otherwise returns NULL.
Script* getCoreLib(const PKVM* vm, String* name);
Module* getCoreLib(const PKVM* vm, String* name);
/*****************************************************************************/
/* OPERATORS */

View File

@ -365,11 +365,11 @@ void dumpGlobalValues(PKVM* vm) {
int frame_ind = fiber->frame_count - 1;
ASSERT(frame_ind >= 0, OOPS);
CallFrame* frame = &fiber->frames[frame_ind];
Script* scr = frame->fn->owner;
Module* module = frame->fn->owner;
for (uint32_t i = 0; i < scr->global_names.count; i++) {
String* name = scr->names.data[scr->global_names.data[i]];
Var value = scr->globals.data[i];
for (uint32_t i = 0; i < module->global_names.count; i++) {
String* name = module->names.data[module->global_names.data[i]];
Var value = module->globals.data[i];
printf("%10s = ", name->data);
dumpValue(vm, value);
printf("\n");

View File

@ -32,7 +32,7 @@ PkVarType pkGetValueType(const PkVar value) {
case OBJ_LIST: return PK_LIST;
case OBJ_MAP: return PK_MAP;
case OBJ_RANGE: return PK_RANGE;
case OBJ_SCRIPT: return PK_SCRIPT;
case OBJ_MODULE: return PK_MODULE;
case OBJ_FUNC: return PK_FUNCTION;
case OBJ_FIBER: return PK_FIBER;
case OBJ_CLASS: return PK_CLASS;
@ -194,27 +194,27 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) {
vm->bytes_allocated += sizeof(Range);
} break;
case OBJ_SCRIPT:
case OBJ_MODULE:
{
Script* scr = (Script*)obj;
vm->bytes_allocated += sizeof(Script);
Module* module = (Module*)obj;
vm->bytes_allocated += sizeof(Module);
markObject(vm, &scr->path->_super);
markObject(vm, &scr->name->_super);
markObject(vm, &module->path->_super);
markObject(vm, &module->name->_super);
markVarBuffer(vm, &scr->globals);
vm->bytes_allocated += sizeof(Var) * scr->globals.capacity;
markVarBuffer(vm, &module->globals);
vm->bytes_allocated += sizeof(Var) * module->globals.capacity;
// Integer buffer has no gray call.
vm->bytes_allocated += sizeof(uint32_t) * scr->global_names.capacity;
vm->bytes_allocated += sizeof(uint32_t) * module->global_names.capacity;
markVarBuffer(vm, &scr->constants);
vm->bytes_allocated += sizeof(Var) * scr->constants.capacity;
markVarBuffer(vm, &module->constants);
vm->bytes_allocated += sizeof(Var) * module->constants.capacity;
markStringBuffer(vm, &scr->names);
vm->bytes_allocated += sizeof(String*) * scr->names.capacity;
markStringBuffer(vm, &module->names);
vm->bytes_allocated += sizeof(String*) * module->names.capacity;
markObject(vm, &scr->body->_super);
markObject(vm, &module->body->_super);
} break;
case OBJ_FUNC:
@ -379,46 +379,47 @@ Range* newRange(PKVM* vm, double from, double to) {
return range;
}
Script* newScript(PKVM* vm, String* name, bool is_core) {
Script* script = ALLOCATE(vm, Script);
varInitObject(&script->_super, vm, OBJ_SCRIPT);
Module* newModule(PKVM* vm, String* name, bool is_core) {
Module* module = ALLOCATE(vm, Module);
varInitObject(&module->_super, vm, OBJ_MODULE);
ASSERT(name != NULL && name->length > 0, OOPS);
script->path = name;
script->name = NULL;
script->initialized = is_core;
script->body = NULL;
module->path = name;
module->name = NULL;
module->initialized = is_core;
module->body = NULL;
// Core modules has its name as the module name.
if (is_core) script->name = name;
if (is_core) module->name = name;
pkVarBufferInit(&script->globals);
pkUintBufferInit(&script->global_names);
pkVarBufferInit(&script->constants);
pkStringBufferInit(&script->names);
pkVarBufferInit(&module->globals);
pkUintBufferInit(&module->global_names);
pkVarBufferInit(&module->constants);
pkStringBufferInit(&module->names);
// Add a implicit main function and the '__file__' global to the module, only
// if it's not a core module.
if (!is_core) {
vmPushTempRef(vm, &script->_super);
scriptAddMain(vm, script);
vmPushTempRef(vm, &module->_super); // module.
moduleAddMain(vm, module);
// Add '__file__' variable with it's path as value. If the path starts with
// '@' It's a special file (@(REPL) or @(TRY)) and don't define __file__.
if (script->path->data[0] != SPECIAL_NAME_CHAR) {
scriptAddGlobal(vm, script, "__file__", 8, VAR_OBJ(script->path));
if (module->path->data[0] != SPECIAL_NAME_CHAR) {
moduleAddGlobal(vm, module, "__file__", 8, VAR_OBJ(module->path));
}
// TODO: Add ARGV as a global.
vmPopTempRef(vm); // script.
vmPopTempRef(vm); // module.
}
return script;
return module;
}
Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
Function* newFunction(PKVM* vm, const char* name, int length, Module* owner,
bool is_native, const char* docstring,
int* fn_index) {
@ -433,10 +434,10 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
func->owner = NULL;
} else {
uint32_t _fn_index = scriptAddConstant(vm, owner, VAR_OBJ(func));
uint32_t _fn_index = moduleAddConstant(vm, owner, VAR_OBJ(func));
if (fn_index) *fn_index = _fn_index;
uint32_t name_index = scriptAddName(owner, vm, name, length);
uint32_t name_index = moduleAddName(owner, vm, name, length);
func->name = owner->names.data[name_index]->data;
func->owner = owner;
@ -457,7 +458,6 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
func->fn = fn;
}
// Both native and script (TODO:) functions support docstring.
func->docstring = docstring;
vmPopTempRef(vm); // func
@ -536,34 +536,35 @@ Fiber* newFiber(PKVM* vm, Function* fn) {
return fiber;
}
Class* newClass(PKVM* vm, Script* scr, const char* name, uint32_t length,
Class* newClass(PKVM* vm, Module* module, const char* name, uint32_t length,
int* cls_index, int* ctor_index) {
Class* cls = ALLOCATE(vm, Class);
varInitObject(&cls->_super, vm, OBJ_CLASS);
vmPushTempRef(vm, &cls->_super); // type.
vmPushTempRef(vm, &cls->_super); // class.
uint32_t _cls_index = scriptAddConstant(vm, scr, VAR_OBJ(cls));
uint32_t _cls_index = moduleAddConstant(vm, module, VAR_OBJ(cls));
if (cls_index) *cls_index = (int)_cls_index;
pkUintBufferInit(&cls->field_names);
cls->owner = scr;
cls->name = scriptAddName(scr, vm, name, length);
cls->owner = module;
cls->name = moduleAddName(module, vm, name, length);
// FIXME:
// Make it possible to escape '@' and '$' character in formated string and
// replace below '%' with excaped '\@' (SPECIAL_NAME_CHAR) character.
String* ty_name = scr->names.data[cls->name];
String* ctor_name = stringFormat(vm, "%(Ctor:@)", ty_name);
// Since characters '@' and '$' are special in stringFormat, and they
// currently cannot be escaped (TODO), a string (char array) created
// for that character and passed as C string format.
char special[2] = { SPECIAL_NAME_CHAR, '\0' };
String* cls_name = module->names.data[cls->name];
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,
scr, false, NULL, ctor_index);
module, false, NULL, ctor_index);
vmPopTempRef(vm); // ctor_name
vmPopTempRef(vm); // type.
vmPopTempRef(vm); // class.
return cls;
}
@ -849,7 +850,7 @@ static uint32_t _hashObject(Object* obj) {
return utilHashNumber(range->from) ^ utilHashNumber(range->to);
}
case OBJ_SCRIPT:
case OBJ_MODULE:
case OBJ_FUNC:
case OBJ_FIBER:
case OBJ_CLASS:
@ -1068,12 +1069,12 @@ void freeObject(PKVM* vm, Object* self) {
case OBJ_RANGE:
break;
case OBJ_SCRIPT: {
Script* scr = (Script*)self;
pkVarBufferClear(&scr->globals, vm);
pkUintBufferClear(&scr->global_names, vm);
pkVarBufferClear(&scr->constants, vm);
pkStringBufferClear(&scr->names, vm);
case OBJ_MODULE: {
Module* module = (Module*)self;
pkVarBufferClear(&module->globals, vm);
pkUintBufferClear(&module->global_names, vm);
pkVarBufferClear(&module->constants, vm);
pkStringBufferClear(&module->names, vm);
} break;
case OBJ_FUNC: {
@ -1123,21 +1124,21 @@ void freeObject(PKVM* vm, Object* self) {
DEALLOCATE(vm, self);
}
uint32_t scriptAddConstant(PKVM* vm, Script* script, Var value) {
for (uint32_t i = 0; i < script->constants.count; i++) {
if (isValuesSame(script->constants.data[i], value)) {
uint32_t moduleAddConstant(PKVM* vm, Module* module, Var value) {
for (uint32_t i = 0; i < module->constants.count; i++) {
if (isValuesSame(module->constants.data[i], value)) {
return i;
}
}
pkVarBufferWrite(&script->constants, vm, value);
return (int)script->constants.count - 1;
pkVarBufferWrite(&module->constants, vm, value);
return (int)module->constants.count - 1;
}
uint32_t scriptAddName(Script* self, PKVM* vm, const char* name,
uint32_t moduleAddName(Module* module, PKVM* vm, const char* name,
uint32_t length) {
for (uint32_t i = 0; i < self->names.count; i++) {
String* _name = self->names.data[i];
for (uint32_t i = 0; i < module->names.count; i++) {
String* _name = module->names.data[i];
if (_name->length == length && strncmp(_name->data, name, length) == 0) {
// Name already exists in the buffer.
return i;
@ -1148,35 +1149,35 @@ uint32_t scriptAddName(Script* self, PKVM* vm, const char* name,
// return the index.
String* new_name = newStringLength(vm, name, length);
vmPushTempRef(vm, &new_name->_super);
pkStringBufferWrite(&self->names, vm, new_name);
pkStringBufferWrite(&module->names, vm, new_name);
vmPopTempRef(vm);
return self->names.count - 1;
return module->names.count - 1;
}
uint32_t scriptAddGlobal(PKVM* vm, Script* script,
uint32_t moduleAddGlobal(PKVM* vm, Module* module,
const char* name, uint32_t length,
Var value) {
// If already exists update the value.
int g_index = scriptGetGlobalIndex(script, name, length);
int g_index = moduleGetGlobalIndex(module, name, length);
if (g_index != -1) {
ASSERT(g_index < (int)script->globals.count, OOPS);
script->globals.data[g_index] = value;
ASSERT(g_index < (int)module->globals.count, OOPS);
module->globals.data[g_index] = value;
return g_index;
}
// If we're reached here that means we don't already have a variable with
// that name, create new one and set the value.
uint32_t name_ind = scriptAddName(script, vm, name, length);
pkUintBufferWrite(&script->global_names, vm, name_ind);
pkVarBufferWrite(&script->globals, vm, value);
return script->globals.count - 1;
uint32_t name_ind = moduleAddName(module, vm, name, length);
pkUintBufferWrite(&module->global_names, vm, name_ind);
pkVarBufferWrite(&module->globals, vm, value);
return module->globals.count - 1;
}
int scriptGetGlobalIndex(Script* script, const char* name, uint32_t length) {
for (uint32_t i = 0; i < script->global_names.count; i++) {
uint32_t name_index = script->global_names.data[i];
String* g_name = script->names.data[name_index];
int moduleGetGlobalIndex(Module* module, const char* name, uint32_t length) {
for (uint32_t i = 0; i < module->global_names.count; i++) {
uint32_t name_index = module->global_names.data[i];
String* g_name = module->names.data[name_index];
if (g_name->length == length && strncmp(g_name->data, name, length) == 0) {
return (int)i;
}
@ -1184,23 +1185,23 @@ int scriptGetGlobalIndex(Script* script, const char* name, uint32_t length) {
return -1;
}
void scriptSetGlobal(Script* script, int index, Var value) {
ASSERT_INDEX(index, script->globals.count);
script->globals.data[index] = value;
void moduleSetGlobal(Module* module, int index, Var value) {
ASSERT_INDEX(index, (int)module->globals.count);
module->globals.data[index] = value;
}
void scriptAddMain(PKVM* vm, Script* script) {
ASSERT(script->body == NULL, OOPS);
void moduleAddMain(PKVM* vm, Module* module) {
ASSERT(module->body == NULL, OOPS);
const char* fn_name = IMPLICIT_MAIN_NAME;
script->body = newFunction(vm, fn_name, (int)strlen(fn_name),
script, false, NULL/*TODO*/, NULL);
script->body->arity = 0;
script->initialized = false;
module->body = newFunction(vm, fn_name, (int)strlen(fn_name),
module, false, NULL/*TODO*/, NULL);
module->body->arity = 0;
module->initialized = false;
scriptAddGlobal(vm, script,
moduleAddGlobal(vm, module,
IMPLICIT_MAIN_NAME, (uint32_t)strlen(IMPLICIT_MAIN_NAME),
VAR_OBJ(script->body));
VAR_OBJ(module->body));
}
bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) {
@ -1340,7 +1341,7 @@ const char* getPkVarTypeName(PkVarType type) {
case PK_LIST: return "List";
case PK_MAP: return "Map";
case PK_RANGE: return "Range";
case PK_SCRIPT: return "Script";
case PK_MODULE: return "Module";
// TODO: since functions are not first class citizens anymore, remove it
// and add closure (maybe with the same name PK_FUNCTION).
@ -1360,7 +1361,7 @@ const char* getObjectTypeName(ObjectType type) {
case OBJ_LIST: return "List";
case OBJ_MAP: return "Map";
case OBJ_RANGE: return "Range";
case OBJ_SCRIPT: return "Script";
case OBJ_MODULE: return "Module";
case OBJ_FUNC: return "Func";
case OBJ_CLOSURE: return "Closure";
case OBJ_UPVALUE: return "Upvalue";
@ -1616,15 +1617,16 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
return;
}
case OBJ_SCRIPT: {
const Script* scr = (const Script*)obj;
case OBJ_MODULE: {
const Module* module = (const Module*)obj;
pkByteBufferAddString(buff, vm, "[Module:", 8);
if (scr->name != NULL) {
pkByteBufferAddString(buff, vm, scr->name->data,
scr->name->length);
if (module->name != NULL) {
pkByteBufferAddString(buff, vm, module->name->data,
module->name->length);
} else {
pkByteBufferWrite(buff, vm, '"');
pkByteBufferAddString(buff, vm, scr->path->data, scr->path->length);
pkByteBufferAddString(buff, vm, module->path->data,
module->path->length);
pkByteBufferWrite(buff, vm, '"');
}
pkByteBufferWrite(buff, vm, ']');
@ -1751,7 +1753,7 @@ bool toBool(Var v) {
case OBJ_LIST: return ((List*)o)->elements.count != 0;
case OBJ_MAP: return ((Map*)o)->count != 0;
case OBJ_RANGE: // [[FALLTHROUGH]]
case OBJ_SCRIPT:
case OBJ_MODULE:
case OBJ_FUNC:
case OBJ_FIBER:
case OBJ_CLASS:

View File

@ -188,7 +188,7 @@ typedef struct String String;
typedef struct List List;
typedef struct Map Map;
typedef struct Range Range;
typedef struct Script Script;
typedef struct Module Module;
typedef struct Function Function;
typedef struct Closure Closure;
typedef struct Upvalue Upvalue;
@ -214,7 +214,7 @@ typedef enum {
OBJ_LIST,
OBJ_MAP,
OBJ_RANGE,
OBJ_SCRIPT,
OBJ_MODULE,
OBJ_FUNC,
OBJ_CLOSURE,
OBJ_UPVALUE,
@ -269,15 +269,20 @@ struct Range {
double to; //< End of the range exclusive.
};
// In pocketlang, the terms Script and Module are interchangable. (Consider
// renaming it to Module to be consistance with the terms).
struct Script {
// Module in pocketlang is a collection of globals, functions, classes and top
// level statements, they can be imported in other modules generally a
// pocketlang script will compiled to a module.
struct Module {
Object _super;
// For core libraries the name and the path are same and points to the
// same String objects.
String* name; //< Module name of the script.
String* path; //< Path of the script.
// The [name] is the module name defined with either 'module' statement
// in the script or the provided name for native modules when creating.
// For core modules the name and the path are same and will points to the
// same String objects. For modules compiled from a script the path will
// be it's resolved path (could be absolute path but thats depend on the
// path resolver).
String* name;
String* path;
// The constant pool of the module, which contains literal values like
// numbers, strings, and functions which are considered constants to
@ -295,21 +300,24 @@ struct Script {
// a seperation between string literals and names in it's constant pool.
// Globals is an array of global variables of the module. All the names
// (including global variables) are stored in the names buffer of the script
// (including global variables) are stored in the names buffer of the module
// (defined bellow). The (i)th global variables names is located at index (j)
// in the names buffer where j = global_names[i].
pkVarBuffer globals;
pkUintBuffer global_names;
Function* body; //< Script body is an anonymous function.
// 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;
// When a script has globals, it's body need to be executed to initialize the
// global values, this will be false if the module isn't initialized yet and
// we need to execute the script's body whe we're importing it.
// If the [initialized] boolean is false, the body function of the module
// will be executed when it's first imported and the 'initialized' boolean
// will be set to true. If a module doesn't have globals, We can safely set
// it to true to prevent from running the above body function, if it has one.
bool initialized;
};
// Script function pointer.
// A struct contain opcodes and other information of a compiled function.
typedef struct {
pkByteBuffer opcodes; //< Buffer of opcodes.
pkUintBuffer oplines; //< Line number of opcodes for debug (1 based).
@ -319,8 +327,23 @@ typedef struct {
struct Function {
Object _super;
const char* name; //< Name in the script [owner] or C literal.
Script* owner; //< Owner script of the function.
// The module that owns this function. Since built in functions doesn't
// belongs to a module it'll be NULL for them.
Module* owner;
// FIXME:
// Because of the builtin function cannot have modules, we cannot reference
// the name of a function with a index which points to the name entry in the
// owner module's names buffer.
//
// The [name] is the name of the function which the function won't have a
// reference to that (to prevent it from garbage collected), it's either
// a C literal string or a name entry in the owner modules names buffer.
// Either way it's guranteed to be alive till the function is alive.
//
// For embedding pocketlang the user must ensure the name exists till the
// function is alive, and it's recomented to use literal C string for that.
const char* name;
// Number of argument the function expects. If the arity is -1 that means
// the function has a variadic number of parameters. When a function is
@ -337,10 +360,12 @@ struct Function {
// native functions to provide a docstring.
const char* docstring;
bool is_native; //< True if Native function.
// Function can be either native C function pointers or compiled pocket
// functions.
bool is_native;
union {
pkNativeFn native; //< Native function pointer.
Fn* fn; //< Script function pointer.
pkNativeFn native; //< Native function pointer.
Fn* fn; //< Pocket function pointer.
};
};
@ -429,8 +454,7 @@ struct Fiber {
FiberState state;
// The root function of the fiber. (For script it'll be the script's implicit
// body function).
// The root function of the fiber.
Function* func;
// The stack of the execution holding locals and temps. A heap will be
@ -462,8 +486,12 @@ struct Fiber {
struct Class {
Object _super;
Script* owner; //< The script it belongs to.
uint32_t name; //< Index of the type's name in the script's name buffer.
// The module that owns this class.
Module* owner;
// The index of the name of this class in the owner module's names
// buffer.
uint32_t name;
Function* ctor; //< The constructor function.
pkUintBuffer field_names; //< Buffer of field names.
@ -524,22 +552,13 @@ Map* newMap(PKVM* vm);
// Allocate new Range object and return Range*.
Range* newRange(PKVM* vm, double from, double to);
// Allocate new Script object and return Script*, if the argument [is_core] is
// true the script will be used as a core module and the body of the script
// would be NULL and the [name] will be used as the module name. Otherwise the
// [name] will be used as the path of the module and a main function will be
// allocated for the module.
Script* newScript(PKVM* vm, String* name, bool is_core);
// FIXME:
// We may need 2 different constructor for native and script modules.
Module* newModule(PKVM* vm, String* name, bool is_core);
// FIXME:
// This bellow function will only to be used for module functions and the
// parameter native is used for builtin functions which will be it's own
// closures (closures should have their own native function pointers, rather
// than having a function that has native pointer which is in-efficient).
//
// TODO:
// Document the bellow function once after the native function move to closure.
Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
// We may need 2 different constuctor for native and script functions.
Function* newFunction(PKVM* vm, const char* name, int length, Module* owner,
bool is_native, const char* docstring,
int* fn_index);
@ -556,7 +575,7 @@ Fiber* newFiber(PKVM* vm, Function* fn);
// Same fix has to applied as newFunction() (see above).
//
// Allocate new Class object and return Class* with name [name].
Class* newClass(PKVM* vm, Script* scr, const char* name, uint32_t length,
Class* newClass(PKVM* vm, Module* scr, const char* name, uint32_t length,
int* cls_index, int* ctor_index);
// Allocate new instance with of the base [type]. Note that if [initialize] is
@ -662,32 +681,32 @@ Var mapRemoveKey(PKVM* vm, Map* self, Var key);
// resumed anymore.
bool fiberHasError(Fiber* fiber);
// Add a constant [value] to the [script] if it doesn't already present in the
// Add a constant [value] to the [module] if it doesn't already present in the
// constant buffer and return it's index.
uint32_t scriptAddConstant(PKVM* vm, Script* script, Var value);
uint32_t moduleAddConstant(PKVM* vm, Module* module, Var value);
// Add the name (string literal) to the string buffer if not already exists and
// return it's index in the buffer.
uint32_t scriptAddName(Script* self, PKVM* vm, const char* name,
uint32_t moduleAddName(Module* module, PKVM* vm, const char* name,
uint32_t length);
// Add a global [value] to the [scrpt] and return its index.
uint32_t scriptAddGlobal(PKVM* vm, Script* script,
// Add a global [value] to the [module] and return its index.
uint32_t moduleAddGlobal(PKVM* vm, Module* module,
const char* name, uint32_t length,
Var value);
// Search for the [name] in the script's globals and return it's index.
// Search for the [name] in the module's globals and return it's index.
// If not found it'll return -1.
int scriptGetGlobalIndex(Script* script, const char* name, uint32_t length);
int moduleGetGlobalIndex(Module* module, const char* name, uint32_t length);
// Set the global value at [index] in the global buffer with the [value].
void scriptSetGlobal(Script* script, int index, Var value);
void moduleSetGlobal(Module* module, int index, Var value);
// This will allocate a new implicit main function for the script and assign to
// the script's body attribute. And the attribute initialized will be set to
// false for the new function. Note that the body of the script should be NULL
// before calling this function.
void scriptAddMain(PKVM* vm, Script* script);
// This will allocate a new implicit main function for the module and assign to
// the module's body attribute. And the attribute initialized will be set to
// false. Note that the body of the module should be NULL before calling this
// function.
void moduleAddMain(PKVM* vm, Module* module);
// Get the attribut from the instance and set it [value]. On success return
// true, if the attribute not exists it'll return false but won't set an error.

View File

@ -68,7 +68,7 @@ PKVM* pkNewVM(PkConfiguration* config) {
vm->min_heap_size = MIN_HEAP_SIZE;
vm->heap_fill_percent = HEAP_FILL_PERCENT;
vm->scripts = newMap(vm);
vm->modules = newMap(vm);
vm->core_libs = newMap(vm);
vm->builtins_count = 0;
@ -136,28 +136,28 @@ PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path,
if (path.on_done) path.on_done(vm, path);
vmPushTempRef(vm, &path_name->_super); // path_name.
// TODO: Should I clean the script if it already exists before compiling it?
// TODO: Should I clean the module if it already exists before compiling it?
// Load a new script to the vm's scripts cache.
Script* scr = vmGetScript(vm, path_name);
if (scr == NULL) {
scr = newScript(vm, path_name, false);
vmPushTempRef(vm, &scr->_super); // scr.
mapSet(vm, vm->scripts, VAR_OBJ(path_name), VAR_OBJ(scr));
vmPopTempRef(vm); // scr.
// Load a new module to the vm's modules cache.
Module* module = vmGetModule(vm, path_name);
if (module == NULL) {
module = newModule(vm, path_name, false);
vmPushTempRef(vm, &module->_super); // module.
mapSet(vm, vm->modules, VAR_OBJ(path_name), VAR_OBJ(module));
vmPopTempRef(vm); // module.
}
vmPopTempRef(vm); // path_name.
// Compile the source.
PkResult result = compile(vm, scr, source.string, options);
PkResult result = compile(vm, module, source.string, options);
if (source.on_done) source.on_done(vm, source);
if (result != PK_RESULT_SUCCESS) return result;
// Set script initialized to true before the execution ends to prevent cyclic
// Set module initialized to true before the execution ends to prevent cyclic
// inclusion cause a crash.
scr->initialized = true;
module->initialized = true;
return runFiber(vm, newFiber(vm, scr->body));
return runFiber(vm, newFiber(vm, module->body));
}
PkResult pkRunFiber(PKVM* vm, PkHandle* fiber,
@ -241,11 +241,11 @@ void vmPopTempRef(PKVM* vm) {
vm->temp_reference_count--;
}
Script* vmGetScript(PKVM* vm, String* path) {
Var scr = mapGet(vm->scripts, VAR_OBJ(path));
if (IS_UNDEF(scr)) return NULL;
ASSERT(AS_OBJ(scr)->type == OBJ_SCRIPT, OOPS);
return (Script*)AS_OBJ(scr);
Module* vmGetModule(PKVM* vm, String* path) {
Var module = mapGet(vm->modules, VAR_OBJ(path));
if (IS_UNDEF(module)) return NULL;
ASSERT(AS_OBJ(module)->type == OBJ_MODULE, OOPS);
return (Module*)AS_OBJ(module);
}
void vmCollectGarbage(PKVM* vm) {
@ -260,8 +260,8 @@ void vmCollectGarbage(PKVM* vm) {
markObject(vm, &vm->builtins[i].fn->_super);
}
// Mark the scripts cache.
markObject(vm, &vm->scripts->_super);
// Mark the modules cache.
markObject(vm, &vm->modules->_super);
// Mark temp references.
for (int i = 0; i < vm->temp_reference_count; i++) {
@ -436,23 +436,33 @@ static void* defaultRealloc(void* memory, size_t new_size, void* user_data) {
return realloc(memory, new_size);
}
// Import and return Script object as Var. If the script is imported and
// compiled here it'll set [is_new_script] to true otherwise (using the cached
// script) set to false.
static inline Var importScript(PKVM* vm, String* path_name) {
// FIXME:
// We're assuming that the module should be available at the VM's modules cache
// which is added by the compilation pahse, but we cannot rely on the
// compilation phase here as it could be a seperate system from the runtime and
// should throw a runtime error if the module is not present in the modules
// cache (or try to load).
// Example: If we may support to store the compiled script as a separate file
// (like python's ".pyc" or java's ".class" the runtime cannot ensure that the
// module it import is already cached.
//
// Import and return the Module object with the [name] (if it's a scirpt
// doesn't have a module name, the name would be it's resolved path).
static inline Var importModule(PKVM* vm, String* name) {
// Check in the core libs.
Script* scr = getCoreLib(vm, path_name);
if (scr != NULL) return VAR_OBJ(scr);
Module* module = getCoreLib(vm, name);
if (module != NULL) return VAR_OBJ(module);
// Check in the scripts cache.
Var entry = mapGet(vm->scripts, VAR_OBJ(path_name));
// Check in the modules cache.
Var entry = mapGet(vm->modules, VAR_OBJ(name));
if (!IS_UNDEF(entry)) {
ASSERT(AS_OBJ(entry)->type == OBJ_SCRIPT, OOPS);
ASSERT(AS_OBJ(entry)->type == OBJ_MODULE, OOPS);
return entry;
}
// Imported scripts were resolved at compile time.
// FIXME: Should be a runtime error.
// Imported modules were resolved at compile time.
UNREACHABLE();
return VAR_NULL;
@ -590,7 +600,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
register Var* rbp; //< Stack base pointer register.
register CallFrame* frame; //< Current call frame.
register Script* script; //< Currently executing script.
register Module* module; //< Currently executing module.
#if DEBUG
#define PUSH(value) \
@ -648,7 +658,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
frame = &vm->fiber->frames[vm->fiber->frame_count-1]; \
ip = frame->ip; \
rbp = frame->rbp; \
script = frame->fn->owner; \
module = frame->fn->owner; \
} while (false)
// Update the frame's execution variables before pushing another call frame.
@ -683,8 +693,8 @@ L_vm_main_loop:
OPCODE(PUSH_CONSTANT):
{
uint16_t index = READ_SHORT();
ASSERT_INDEX(index, script->constants.count);
PUSH(script->constants.data[index]);
ASSERT_INDEX(index, module->constants.count);
PUSH(module->constants.data[index]);
DISPATCH();
}
@ -729,10 +739,10 @@ L_vm_main_loop:
OPCODE(PUSH_INSTANCE):
{
uint8_t index = READ_SHORT();
ASSERT_INDEX(index, script->constants.count);
ASSERT(IS_OBJ_TYPE(script->constants.data[index], OBJ_CLASS), OOPS);
ASSERT_INDEX(index, module->constants.count);
ASSERT(IS_OBJ_TYPE(module->constants.data[index], OBJ_CLASS), OOPS);
Instance* inst = newInstance(vm,
(Class*)AS_OBJ(script->constants.data[index]), false);
(Class*)AS_OBJ(module->constants.data[index]), false);
PUSH(VAR_OBJ(inst));
DISPATCH();
}
@ -827,16 +837,16 @@ L_vm_main_loop:
OPCODE(PUSH_GLOBAL):
{
uint8_t index = READ_BYTE();
ASSERT_INDEX(index, script->globals.count);
PUSH(script->globals.data[index]);
ASSERT_INDEX(index, module->globals.count);
PUSH(module->globals.data[index]);
DISPATCH();
}
OPCODE(STORE_GLOBAL):
{
uint8_t index = READ_BYTE();
ASSERT_INDEX(index, script->globals.count);
script->globals.data[index] = PEEK(-1);
ASSERT_INDEX(index, module->globals.count);
module->globals.data[index] = PEEK(-1);
DISPATCH();
}
@ -855,32 +865,32 @@ L_vm_main_loop:
OPCODE(IMPORT):
{
String* name = script->names.data[READ_SHORT()];
Var scr = importScript(vm, name);
String* name = module->names.data[READ_SHORT()];
ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), OOPS);
Script* module = (Script*)AS_OBJ(scr);
PUSH(scr);
Var _imported = importModule(vm, name);
ASSERT(IS_OBJ_TYPE(_imported, OBJ_MODULE), OOPS);
PUSH(_imported);
// TODO: If the body doesn't have any statements (just the functions).
// This initialization call is un-necessary.
if (!module->initialized) {
module->initialized = true;
Module* imported = (Module*)AS_OBJ(_imported);
if (!imported->initialized) {
imported->initialized = true;
ASSERT(module->body != NULL, OOPS);
ASSERT(imported->body != NULL, OOPS);
// Note that we're setting the main function's return address to the
// module itself (for every other function we'll push a null at the rbp
// before calling them and it'll be returned without modified if the
// function doesn't returned anything). Also We can't return from the
// body of the script, so the main function will return what's at the
// body of the module, so the main function will return what's at the
// rbp without modifying it. So at the end of the main function the
// stack top would be the module itself.
Var* module_ret = vm->fiber->sp - 1;
UPDATE_FRAME(); //< Update the current frame's ip.
pushCallFrame(vm, module->body, module_ret);
pushCallFrame(vm, imported->body, module_ret);
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
}
@ -1065,7 +1075,7 @@ L_vm_main_loop:
} DISPATCH();
case OBJ_SCRIPT:
case OBJ_MODULE:
case OBJ_FUNC:
case OBJ_CLOSURE:
case OBJ_UPVALUE:
@ -1174,7 +1184,7 @@ L_vm_main_loop:
OPCODE(GET_ATTRIB):
{
Var on = PEEK(-1); // Don't pop yet, we need the reference for gc.
String* name = script->names.data[READ_SHORT()];
String* name = module->names.data[READ_SHORT()];
Var value = varGetAttrib(vm, on, name);
DROP(); // on
PUSH(value);
@ -1186,7 +1196,7 @@ L_vm_main_loop:
OPCODE(GET_ATTRIB_KEEP):
{
Var on = PEEK(-1);
String* name = script->names.data[READ_SHORT()];
String* name = module->names.data[READ_SHORT()];
PUSH(varGetAttrib(vm, on, name));
CHECK_ERROR();
DISPATCH();
@ -1196,7 +1206,7 @@ L_vm_main_loop:
{
Var value = PEEK(-1); // Don't pop yet, we need the reference for gc.
Var on = PEEK(-2); // Don't pop yet, we need the reference for gc.
String* name = script->names.data[READ_SHORT()];
String* name = module->names.data[READ_SHORT()];
varSetAttrib(vm, on, name, value);
DROP(); // value

View File

@ -46,9 +46,8 @@
(vm->fiber->error = err); \
} while (false)
// Builtin functions are stored in an array in the VM (unlike script functions
// they're member of function buffer of the script) and this struct is a single
// entry of the array.
// 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.
@ -111,14 +110,14 @@ struct PKVM {
// Current compiler reference to mark it's heap allocated objects. Note that
// The compiler isn't heap allocated. It'll be a link list of all the
// compiler we have so far. A new compiler will be created and appended when
// a new script is being imported and compiled at compiletime.
// a new module is being imported and compiled at compiletime.
Compiler* compiler;
// A cache of the compiled scripts with their path as key and the Scrpit
// A cache of the compiled modules with their path as key and the Scrpit
// object as the value.
Map* scripts;
Map* modules;
// A map of core libraries with their name as the key and the Script object
// A map of core libraries with their name as the key and the Module object
// as the value.
Map* core_libs;
@ -192,9 +191,9 @@ void vmPushTempRef(PKVM* vm, Object* obj);
// Pop the top most object from temporary reference stack.
void vmPopTempRef(PKVM* vm);
// Returns the scrpt with the resolved [path] (also the key) in the vm's script
// cache. If not found itll return NULL.
Script* vmGetScript(PKVM* vm, String* path);
// Returns the module with the resolved [path] (also the key) in the VM's
// modules cache. If not found itll return NULL.
Module* vmGetModule(PKVM* vm, String* path);
// ((Context switching - start))
// Prepare a new fiber for execution with the given arguments. That can be used