imported scripts were initialized (#158)

This commit is contained in:
Thakee Nathees 2021-07-02 21:32:30 +05:30 committed by GitHub
parent 3ef213b170
commit 89f8b77bb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 108 additions and 62 deletions

View File

@ -4,23 +4,13 @@ Add '.title' attribute to string
// To implement.
- refactor the build.bat script.
- no __file__ for core modules.
- def f(x)
f(x) ## not tco, unless return f(x)
end
if a function's last statement is a call, we can perform tco.
- make assert as a keyword (like python) and disable it on release build.
- implement 'lang.getMaxCallDepth()' (default=1000 like python) and
setMaxCallDepth(val) to change stack size at runtime.
- change or add => to_string() to value.as_string
and add as_repr, as_bool.
- Make bool and num are incompatible (also it increase performance a bit).
1 + true - make it not allowed.
- literal function recursive call.
fn = func(a, b)
@ -30,7 +20,6 @@ Add '.title' attribute to string
- Implement utf8 support.
- Implement gdb like debugger (add color print for readability).
- Initialize imported scripts (require fiber based vm).
- Complete all the TODO; macros.
- implement MAX_ARGC checks (would cause a buffer overflow if not)
when compiling and calling a function (also in fibers).

View File

@ -77,7 +77,7 @@ typedef struct PkHandle PkHandle;
typedef void* PkVar;
// Type enum of the pocketlang variables, this can be used to get the type
// from a Var* in the method pkGetVarType().
// from a PkVar in the method pkGetVarType().
typedef enum {
PK_NULL,
PK_BOOL,
@ -110,9 +110,6 @@ typedef enum {
typedef enum {
PK_RESULT_SUCCESS = 0, // Successfully finished the execution.
// This will be set to `true` if we're running REPL mode and reached an EOF
// unexpectedly,
// Unexpected EOF while compiling the source. This is another compile time
// error that will ONLY be returned if we're compiling with the REPL mode set
// in the compile options. We need this specific error to indicate the host
@ -216,7 +213,7 @@ PK_PUBLIC PkConfiguration pkNewConfiguration(void);
// application.
PK_PUBLIC PkCompileOptions pkNewCompilerOptions(void);
// Allocate, initialize and returns a new VM
// Allocate, initialize and returns a new VM.
PK_PUBLIC PKVM* pkNewVM(PkConfiguration* config);
// Clean the VM and dispose all the resources allocated by the VM.
@ -332,14 +329,6 @@ struct PkCompileOptions {
// Compile debug version of the source.
bool debug;
// TODO: don't use FILE* pointer or any of <stdio.h> functions here.
// instead add a stream option to vm.config.write_fn callback.
//
// Dump the compiled opcodes to the given [dump_stream] FILE* could be stdio,
// stderr, or a file pointer.
//bool dump_opcodes;
//FILE* dump_stream;
// Set to true if compiling in REPL mode, This will print repr version of
// each evaluated non-null values. Note that if [repl_mode] is true the
// [expression] should also be true otherwise it's incompatible, (will fail
@ -443,9 +432,11 @@ PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn);
// callback.
PK_PUBLIC PkHandle* pkNewInstNative(PKVM* vm, void* data, uint32_t id);
// TODO: The functions below will push the primitive values on the stack
// and return it's pointer as a PkVar. It's useful to convert your primitive
// values as pocketlang variables.
// TODO: Create a primitive (non garbage collected) variable buffer (or a
// fixed size array) to store them and make the handle points to the variable
// in that buffer, this will prevent us from invoking an allocation call for
// each time we want to pass a primitive type.
//PK_PUBLIC PkVar pkPushNull(PKVM* vm);
//PK_PUBLIC PkVar pkPushBool(PKVM* vm, bool value);
//PK_PUBLIC PkVar pkPushNumber(PKVM* vm, double value);

View File

@ -2215,7 +2215,7 @@ static Script* importFile(Compiler* compiler, const char* path) {
}
// Make a new script and to compile it.
Script* scr = newScript(vm, path_name);
Script* 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.

View File

@ -757,8 +757,7 @@ static Script* newModuleInternal(PKVM* vm, const char* name) {
"A module named '$' already exists", name)->data);
}
Script* scr = newScript(vm, _name);
scr->module = _name;
Script* scr = newScript(vm, _name, true);
vmPopTempRef(vm); // _name
// Add the script to core_libs.

View File

@ -108,9 +108,9 @@ OPCODE(PUSH_BUILTIN_FN, 1, 1)
// Pop the stack top.
OPCODE(POP, 0, -1)
// Pop the path from the stack, import the module at the path and push the
// script in the script. If the script is imported for the first time (not
// cached) the script's body will be executed.
// Push the pre-compiled module at the index (from opcode) on the stack, and
// initialize the module (ie. run the main function) if it's not initialized
// already.
// params: 2 byte name index.
OPCODE(IMPORT, 2, 1)

View File

@ -367,13 +367,23 @@ Range* newRange(PKVM* vm, double from, double to) {
return range;
}
Script* newScript(PKVM* vm, String* path) {
Script* newScript(PKVM* vm, String* name, bool is_core) {
Script* script = ALLOCATE(vm, Script);
varInitObject(&script->_super, vm, OBJ_SCRIPT);
script->path = path;
ASSERT(name != NULL && name->length > 0, OOPS);
script->path = name;
script->module = NULL;
script->initialized = false;
script->body = NULL;
// Core modules has its name as the module name, and since they don't have a
// main function, they doesn't need an initialization.
if (is_core) {
script->module = name;
script->initialized = true;
}
pkVarBufferInit(&script->globals);
pkUintBufferInit(&script->global_names);
@ -382,20 +392,26 @@ Script* newScript(PKVM* vm, String* path) {
pkClassBufferInit(&script->classes);
pkStringBufferInit(&script->names);
vmPushTempRef(vm, &script->_super);
const char* fn_name = PK_IMPLICIT_MAIN_NAME;
script->body = newFunction(vm, fn_name, (int)strlen(fn_name),
script, false, NULL/*TODO*/);
script->body->arity = 0; // TODO: should it be 1 (ARGV)?.
// 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);
const char* fn_name = PK_IMPLICIT_MAIN_NAME;
script->body = newFunction(vm, fn_name, (int)strlen(fn_name),
script, false, NULL/*TODO*/);
script->body->arity = 0;
// 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] != '$') {
scriptAddGlobal(vm, script, "__file__", 8, VAR_OBJ(script->path));
// 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] != '$') {
scriptAddGlobal(vm, script, "__file__", 8, VAR_OBJ(script->path));
}
// TODO: Add ARGV as a global.
vmPopTempRef(vm); // script.
}
vmPopTempRef(vm); // script.
return script;
}

View File

@ -437,8 +437,12 @@ 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*.
Script* newScript(PKVM* vm, String* path);
// 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);
// Allocate new Function object and return Function*. Parameter [name] should
// be the name in the Script's nametable. If the [owner] is NULL the function

View File

@ -143,7 +143,7 @@ PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path,
// Load a new script to the vm's scripts cache.
Script* scr = vmGetScript(vm, path_name);
if (scr == NULL) {
scr = newScript(vm, path_name);
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.
@ -548,7 +548,6 @@ static inline void pushCallFrame(PKVM* vm, const Function* fn, Var* rbp) {
if (vm->fiber->stack_size <= needed) growStack(vm, needed);
CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++;
*rbp = VAR_NULL;
frame->rbp = rbp;
frame->fn = fn;
frame->ip = fn->fn->opcodes.data;
@ -703,7 +702,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
#define DISPATCH() goto L_vm_main_loop
// Trigger a break point here, if we're trying to debug the call stack.
#if DEBUG_DUMP_CALL_STACK
#if DEBUG_DUMP_CALL_STACK
DEBUG_BREAK();
#endif
@ -908,13 +907,32 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
String* name = script->names.data[READ_SHORT()];
Var scr = importScript(vm, name);
// TODO: implement fiber based execution.
//ASSERT(IS_OBJ_TYPE(script, OBJ_SCRIPT), OOPS);
//Script* scr = (Script*)AS_OBJ(script);
//if (!scr->initialized) vmRunScript(vm, scr);
ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), OOPS);
Script* module = (Script*)AS_OBJ(scr);
PUSH(scr);
CHECK_ERROR();
// TODO: If the body doesn't have any statements (just the functions).
// This initialization call is un-necessary.
if (!module->initialized) {
module->initialized = true;
ASSERT(module->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
// 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);
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
}
DISPATCH();
}
@ -953,6 +971,10 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
RUNTIME_ERROR(msg);
}
// Next call frame starts here. (including return value).
call_fiber->ret = callable;
*(call_fiber->ret) = VAR_NULL; //< Set the return value to null.
if (fn->is_native) {
if (fn->native == NULL) {
@ -963,10 +985,6 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
// Update the current frame's ip.
UPDATE_FRAME();
// Next call frame starts here. (including return value).
call_fiber->ret = callable;
*(call_fiber->ret) = VAR_NULL; //< Set the return value to null.
fn->native(vm); //< Call the native function.
// Calling yield() will change vm->fiber to it's caller fiber, which

View File

@ -35,6 +35,15 @@ assert(all_f3() == 'f3')
import 'import/all_import.pk' as all_import
assert(all_import.all_f1 == all_f1)
## Test if the imported globals were initialized
import 'import/globals.pk'
assert(g_import != null)
assert(g_import.g_var_1 == 3)
assert(g_import.g_var_2 == g_import.get_a_value())
import 'import/globals2.pk'
assert(g_val_1 == 100)
assert(g_val_2 == get_a_value())
# If we got here, that means all test were passed.
print('All TESTS PASSED')

View File

@ -0,0 +1,9 @@
module g_import
g_var_1 = 1 + 2
g_var_2 = get_a_value()
def get_a_value()
return "foobar"
end

View File

@ -0,0 +1,11 @@
g_val_1 = 0
for i in 0..100
g_val_1 += 1
end
g_val_2 = get_a_value()
def get_a_value()
return "baz"
end