Merge pull request #16 from ThakeeNathees/local-variable-bug

fixed: local variables wasn't popped from the scope
This commit is contained in:
Thakee Nathees 2021-05-13 14:42:25 +05:30 committed by GitHub
commit b83820bd4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 110 additions and 40 deletions

2
.gitignore vendored
View File

@ -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/

View File

@ -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

View File

@ -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.
It's at pre-alpha and every thing is left to
implement, and nothing would be work as expected.

View File

@ -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)) {

View File

@ -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);

View File

@ -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);

View File

@ -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();

16
test/locals.pk Normal file
View File

@ -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 ')