Merge pull request #192 from ThakeeNathees/script-type-rename

Scripts renamed to Modules
This commit is contained in:
Thakee Nathees 2022-04-12 01:26:39 +05:30 committed by GitHub
commit 3b5da9cad3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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