From 5c1b776047daac117df3690adad08ae098c4d815 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Thu, 13 May 2021 14:40:57 +0530 Subject: [PATCH] fixed: local variables wasn't poped from the scope --- .gitignore | 2 ++ SConstruct | 18 ++++++++--------- cli/TODO.txt | 9 +++++++-- src/compiler.c | 33 ++++++++++++++++++++++++++----- src/core.c | 28 ++++++++++++++++++--------- src/include/pocketlang.h | 2 +- src/vm.c | 42 ++++++++++++++++++++++++++-------------- test/locals.pk | 16 +++++++++++++++ 8 files changed, 110 insertions(+), 40 deletions(-) create mode 100644 test/locals.pk diff --git a/.gitignore b/.gitignore index 8952c81..dd8625f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ # Docs branch cloned into the docs/build sub directory docs/build/ +docs/static/try_now.js +docs/static/try_now.wasm build/ bin/ diff --git a/SConstruct b/SConstruct index e662132..9b1eadb 100644 --- a/SConstruct +++ b/SConstruct @@ -14,18 +14,18 @@ def CONFIGURE_ENV(env): ## PocketLang source files SOURCES = [ - Glob(root_dir + '*.c'), - Glob(root_dir + 'buffers/*.c'), + Glob(root_dir + 'src/*.c'), + Glob(root_dir + 'src/buffers/*.c'), ] - target_dir = root_dir + '../bin/' + target_dir = root_dir + 'bin/' if env['lib_shared']: ## Compile pocketlang dynamic lib. dll = env.SharedLibrary( target = target_dir + 'pocket' + env['bin_suffix'], source = SOURCES, - CPPPATH = [root_dir + 'include/'], + CPPPATH = [root_dir + 'src/include/'], CPPDEFINES = [env['CPPDEFINES'], 'PK_DLL', 'PK_COMPILE'], ) else: @@ -33,14 +33,14 @@ def CONFIGURE_ENV(env): lib = env.Library( target = target_dir + 'pocket' + env['bin_suffix'], source = SOURCES, - CPPPATH = [root_dir + 'include/'], + CPPPATH = [root_dir + 'src/include/'], ) ## Test executable test = env.Program( target = target_dir + 'pocket' + env['bin_suffix'], - source = Glob('#cli/*.c'), - CPPPATH = [root_dir + 'include/'], + source = Glob(root_dir + 'cli/*.c'), + CPPPATH = [root_dir + 'src/include/'], LIBPATH = target_dir, LIBS = 'pocket' + env['bin_suffix'], ) @@ -69,8 +69,8 @@ opts.Add(BoolVariable('lib_shared', "Compile as a shared library (only).", False ## VariantDir _build_target = ARGUMENTS.get('target', 'debug') if _build_target in ('debug', 'release'): ## Otherwise error below. - _build_target = 'build/' + _build_target + '/src/' - VariantDir(_build_target, 'src', duplicate=0) + _build_target = 'build/' + _build_target + '/' + VariantDir(_build_target, './', duplicate=0) ## Setup the Environment DefaultEnvironment(tools=[]) ## not using any tools diff --git a/cli/TODO.txt b/cli/TODO.txt index 06f4b2b..8ddc91f 100644 --- a/cli/TODO.txt +++ b/cli/TODO.txt @@ -1,6 +1,8 @@ // To implement. +[ ] Builtin functions buffer refactor. +[ ] Remove resolve path for the root module. [ ] and/or operator implementation. [ ] Runtime error trace. [ ] Make it possible to override function names. @@ -11,8 +13,8 @@ [ ] Garbage collection. [*] Mark phase. [ ] Sweep phase. -[ ] Hex, binary literals and floats like ".5". [ ] Relative file import. +[ ] Hex, binary literals and floats like ".5". [ ] Union tagging alter in var. // Add more. @@ -22,6 +24,9 @@ [ ] Complete builtin operators. [ ] Complete opcodes. [ ] Complete core libs. +[ ] Complete the docs. +[ ] More Tests. // Bugs. -[ ] Update cache on each recompilation instead of making a new cache. \ No newline at end of file +It's at pre-alpha and every thing is left to +implement, and nothing would be work as expected. \ No newline at end of file diff --git a/src/compiler.c b/src/compiler.c index f48ca30..ebe87bb 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -272,6 +272,10 @@ typedef struct sLoop { // current loop context. struct sLoop* outer_loop; + // Depth of the loop, required to pop all the locals in that loop when it + // met a break/continue statement inside. + int depth; + } Loop; typedef struct sFunc { @@ -1040,6 +1044,7 @@ static void exprName(Compiler* compiler, bool can_assign) { // This will prevent the assignment from poped out from the stack // since the assigned value itself is the local and not a temp. compiler->new_local = true; + emitStoreVariable(compiler, (index - compiler->global_count), false); } } else { // TODO: The name could be a function which hasn't been defined at this point. @@ -1372,15 +1377,25 @@ static void compilerEnterBlock(Compiler* compiler) { compiler->scope_depth++; } +// Pop all the locals at the [depth] or highter. +static void compilerPopLocals(Compiler* compiler, int depth) { + ASSERT(depth > (int)DEPTH_GLOBAL, "Cannot pop global variables."); + + int local = compiler->var_count - 1; + while (local >= 0 && compiler->variables[local].depth >= depth) { + emitOpcode(compiler, OP_POP); + compiler->var_count--; + compiler->stack_size--; + local--; + } +} + // Exits a block. static void compilerExitBlock(Compiler* compiler) { ASSERT(compiler->scope_depth > (int)DEPTH_GLOBAL, "Cannot exit toplevel."); - while (compiler->var_count > 0 && compiler->variables[ - compiler->var_count - 1].depth >= compiler->scope_depth) { - compiler->var_count--; - compiler->stack_size--; - } + // Discard all the locals at the current scope. + compilerPopLocals(compiler, compiler->scope_depth); compiler->scope_depth--; } @@ -1743,6 +1758,7 @@ static void compileWhileStatement(Compiler* compiler) { loop.start = (int)_FN->opcodes.count; loop.patch_count = 0; loop.outer_loop = compiler->loop; + loop.depth = compiler->scope_depth; compiler->loop = &loop; compileExpression(compiler); //< Condition. @@ -1797,6 +1813,7 @@ static void compileForStatement(Compiler* compiler) { loop.start = (int)_FN->opcodes.count; loop.patch_count = 0; loop.outer_loop = compiler->loop; + loop.depth = compiler->scope_depth; compiler->loop = &loop; // Compile next iteration. @@ -1834,6 +1851,9 @@ static void compileStatement(Compiler* compiler) { "Too many break statements (" STRINGIFY(MAX_BREAK_PATCH) ")." ); consumeEndStatement(parser); + // Pop all the locals at the loop's body depth. + compilerPopLocals(compiler, compiler->loop->depth + 1); + emitOpcode(compiler, OP_JUMP); int patch = emitByte(compiler, 0xffff); //< Will be patched. compiler->loop->patches[compiler->loop->patch_count++] = patch; @@ -1845,6 +1865,9 @@ static void compileStatement(Compiler* compiler) { } consumeEndStatement(parser); + // Pop all the locals at the loop's body depth. + compilerPopLocals(compiler, compiler->loop->depth + 1); + emitLoopJump(compiler); } else if (match(parser, TK_RETURN)) { diff --git a/src/core.c b/src/core.c index 4aac843..56e1096 100644 --- a/src/core.c +++ b/src/core.c @@ -167,17 +167,27 @@ FN_IS_OBJ_TYPE(Script, OBJ_SCRIPT) FN_IS_OBJ_TYPE(UserObj, OBJ_USER) void coreAssert(PKVM* vm) { + int argc = ARGC; + if (argc != 1 && argc != 2) { + vm->fiber->error = newString(vm, "Invalid argument count."); + return; + } + if (!toBool(ARG1)) { String* msg = NULL; - if (AS_OBJ(ARG2)->type != OBJ_STRING) { - msg = toString(vm, ARG2, false); - } else { - msg = (String*)AS_OBJ(ARG2); - } - vmPushTempRef(vm, &msg->_super); - vm->fiber->error = stringFormat(vm, "Assertion failed: @", msg); - vmPopTempRef(vm); + if (argc == 2) { + if (AS_OBJ(ARG2)->type != OBJ_STRING) { + msg = toString(vm, ARG2, false); + } else { + msg = (String*)AS_OBJ(ARG2); + } + vmPushTempRef(vm, &msg->_super); + vm->fiber->error = stringFormat(vm, "Assertion failed: @.", msg); + vmPopTempRef(vm); + } else { + vm->fiber->error = newString(vm, "Assertion failed."); + } } } @@ -264,7 +274,7 @@ void initializeCore(PKVM* vm) { INITALIZE_BUILTIN_FN("is_script", coreIsScript, 1); INITALIZE_BUILTIN_FN("is_userobj", coreIsUserObj, 1); - INITALIZE_BUILTIN_FN("assert", coreAssert, 2); + INITALIZE_BUILTIN_FN("assert", coreAssert, -1); INITALIZE_BUILTIN_FN("hash", coreHash, 1); INITALIZE_BUILTIN_FN("to_string", coreToString, 1); INITALIZE_BUILTIN_FN("print", corePrint, -1); diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index 7d51689..a588ccd 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -171,7 +171,7 @@ PK_PUBLIC PKInterpretResult pkInterpretSource(PKVM* vm, const char* source); PK_PUBLIC PKInterpretResult pkInterpret(PKVM* vm, const char* file); // Set a runtime error to vm. -PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* format, ...); +PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* message); // Returns the associated user data. PK_PUBLIC void* pkGetUserData(PKVM* vm); diff --git a/src/vm.c b/src/vm.c index 38281fc..0845d68 100644 --- a/src/vm.c +++ b/src/vm.c @@ -289,10 +289,8 @@ static inline void pushCallFrame(PKVM* vm, Function* fn) { frame->ip = fn->fn->opcodes.data; } -void pkSetRuntimeError(PKVM* vm, const char* format, ...) { - - vm->fiber->error = newString(vm, "TODO:"); - TODO; // Construct String and set to vm->fiber->error. +void pkSetRuntimeError(PKVM* vm, const char* message) { + vm->fiber->error = stringFormat(vm, "$", message); } void vmReportError(PKVM* vm) { @@ -434,11 +432,10 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { } \ } while (false) -// Note: '##__VA_ARGS__' is not portable but most common compilers including -// gcc, msvc, clang, tcc (c99) supports. -#define RUNTIME_ERROR(fmt, ...) \ +// [err_msg] must be of type String. +#define RUNTIME_ERROR(err_msg) \ do { \ - pkSetRuntimeError(vm, fmt, ##__VA_ARGS__); \ + vm->fiber->error = err_msg; \ vmReportError(vm); \ return PK_RESULT_RUNTIME_ERROR; \ } while (false) @@ -530,6 +527,14 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { Var key = POP(); Var on = PEEK(); + ASSERT(IS_OBJ(on) && AS_OBJ(on)->type == OBJ_MAP, OOPS); + + if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) { + RUNTIME_ERROR(stringFormat(vm, "$ type is not hashable.", varTypeName(key))); + } else { + mapSet((Map*)AS_OBJ(on), vm, key, value); + } + varsetSubscript(vm, on, key, value); CHECK_ERROR(); DISPATCH(); @@ -632,7 +637,12 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { // -1 argument means multiple number of args. if (fn->arity != -1 && fn->arity != argc) { - RUNTIME_ERROR("Expected excatly %d argument(s).", fn->arity); + String* arg_str = toString(vm, VAR_NUM(fn->arity), false); + vmPushTempRef(vm, &arg_str->_super); + String* msg = stringFormat(vm, "Expected excatly @ argument(s).", + arg_str); + vmPopTempRef(vm); // arg_str. + RUNTIME_ERROR(msg); } // Now the function will never needed in the stack and it'll @@ -644,7 +654,8 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { if (fn->is_native) { if (fn->native == NULL) { - RUNTIME_ERROR("Native function pointer of %s was NULL.", fn->name); + RUNTIME_ERROR(stringFormat(vm, + "Native function pointer of $ was NULL.", fn->name)); } fn->native(vm); // Pop function arguments except for the return value. @@ -658,7 +669,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { } } else { - RUNTIME_ERROR("Expected a function in call."); + RUNTIME_ERROR(newString(vm, "Expected a function in call.")); } DISPATCH(); } @@ -669,7 +680,10 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { Var* iterator = (vm->fiber->sp - 2); Var* container = (vm->fiber->sp - 3); uint16_t jump_offset = READ_SHORT(); - if (!varIterate(vm, *container, iterator, iter_value)) { + + bool is_done = varIterate(vm, *container, iterator, iter_value); + CHECK_ERROR(); + if (!is_done) { DROP(); //< Iter value. DROP(); //< Iterator. DROP(); //< Container. @@ -796,7 +810,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { { Var num = POP(); if (!IS_NUM(num)) { - RUNTIME_ERROR("Cannot negate a non numeric value."); + RUNTIME_ERROR(newString(vm, "Cannot negate a non numeric value.")); } PUSH(VAR_NUM(-AS_NUM(num))); DISPATCH(); @@ -941,7 +955,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { Var to = POP(); Var from = POP(); if (!IS_NUM(from) || !IS_NUM(to)) { - RUNTIME_ERROR("Range arguments must be number."); + RUNTIME_ERROR(newString(vm, "Range arguments must be number.")); } PUSH(VAR_OBJ(newRange(vm, AS_NUM(from), AS_NUM(to)))); DISPATCH(); diff --git a/test/locals.pk b/test/locals.pk new file mode 100644 index 0000000..0b1b9e7 --- /dev/null +++ b/test/locals.pk @@ -0,0 +1,16 @@ + +## Local variable test. + +seq = '' +def fib(n) + a = 0; b = 1 + for _ in 0..n + seq += to_string(a) + " " + temp = a; + a = b; + b += temp; + end +end + +fib(10) +assert(seq == '0 1 1 2 3 5 8 13 21 34 ') \ No newline at end of file