mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
Merge pull request #16 from ThakeeNathees/local-variable-bug
fixed: local variables wasn't popped from the scope
This commit is contained in:
commit
b83820bd4c
2
.gitignore
vendored
2
.gitignore
vendored
@ -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/
|
||||
|
18
SConstruct
18
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
|
||||
|
@ -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.
|
@ -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)) {
|
||||
|
28
src/core.c
28
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);
|
||||
|
@ -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);
|
||||
|
42
src/vm.c
42
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();
|
||||
|
16
test/locals.pk
Normal file
16
test/locals.pk
Normal 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 ')
|
Loading…
Reference in New Issue
Block a user