mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
Merge pull request #192 from ThakeeNathees/script-type-rename
Scripts renamed to Modules
This commit is contained in:
commit
3b5da9cad3
@ -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.
|
||||
|
@ -84,7 +84,7 @@ typedef enum {
|
||||
PK_LIST,
|
||||
PK_MAP,
|
||||
PK_RANGE,
|
||||
PK_SCRIPT,
|
||||
PK_MODULE,
|
||||
PK_FUNCTION,
|
||||
PK_FIBER,
|
||||
PK_CLASS,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
130
src/pk_core.c
130
src/pk_core.c
@ -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:
|
||||
|
@ -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 */
|
||||
|
@ -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");
|
||||
|
196
src/pk_value.c
196
src/pk_value.c
@ -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:
|
||||
|
123
src/pk_value.h
123
src/pk_value.h
@ -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.
|
||||
|
120
src/pk_vm.c
120
src/pk_vm.c
@ -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
|
||||
|
19
src/pk_vm.h
19
src/pk_vm.h
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user