fixed: iterator (internal) variables popped twise.

printing stack trace implemented
This commit is contained in:
Thakee Nathees 2021-05-14 16:14:57 +05:30
parent 9c77ff5a6c
commit c3041c74a9
15 changed files with 116 additions and 109 deletions

View File

@ -10,7 +10,13 @@
void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line, void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line,
const char* message) { const char* message) {
fprintf(stderr, "Error: %s\n\tat %s:%i\n", message, file, line); if (type == PK_ERROR_COMPILE) {
fprintf(stderr, "Error: %s\n\tat %s:%i\n", message, file, line);
} else if (type == PK_ERROR_RUNTIME) {
fprintf(stderr, "Error: %s\n", message);
} else if (type == PK_ERROR_STACKTRACE) {
fprintf(stderr, " [%s:%i] %s()\n", file, line, message);
}
} }
void writeFunction(PKVM* vm, const char* text) { void writeFunction(PKVM* vm, const char* text) {

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) 2021 Thakee Nathees
* Licensed under: MIT License
*/
const _initial_snippet = `\
# A recursive fibonacci function.
def fib(n)
if n < 2 then return n end
return fib(n-1) + fib(n-2)
end
# Print all fibonacci from 0 to 5 exclusive.
for i in 0..5
print(fib(i))
end
`
var highlight_fn = function(editor) {
// highlight.js does not trim old tags,
// let's do it by this hack.
editor.textContent = editor.textContent;
editor.innerHTML = Prism.highlight(editor.textContent, Prism.languages.ruby, 'ruby');
}
var runSource;
window.onload = function() { // called after index.html is loaded -> Module is defined.
runSource = Module.cwrap('runSource', 'number', ['string']);
document.getElementById("run-button").onclick = function() {
document.getElementById('output').innerText = '';
const source = document.querySelector('.editor').textContent;
runSource(source);
}
let editor = document.querySelector('.editor')
editor.textContent = _initial_snippet;
highlight_fn(editor);
}

View File

@ -10,9 +10,19 @@
mergeInto(LibraryManager.library, { mergeInto(LibraryManager.library, {
/** js_func_name : function() {...} */ /** js_func_name : function() {...} */
js_errorPrint : function(message, line) { js_errorPrint : function(type, line, message) {
var err_text = ''
const msg = AsciiToString(message);
if (type == 0 /*PK_ERROR_COMPILE*/) {
err_text = `[Error at:${line}] ${msg}`;
} else if (type == 1 /*PK_ERROR_RUNTIME*/) {
err_text = `Error: ${msg}`;
} else if (type == 2 /*PK_ERROR_STACKTRACE*/) {
err_text = ` [at:${line}] ${msg}`;
}
var out = document.getElementById("output"); var out = document.getElementById("output");
out.innerText += `[Error at:${line}]: ${AsciiToString(message)} \n`; out.innerText += err_text + '\n';
}, },
js_writeFunction : function(message) { js_writeFunction : function(message) {

View File

@ -10,13 +10,15 @@
#include <emscripten.h> #include <emscripten.h>
#include "pocketlang.h" #include "pocketlang.h"
extern void js_errorPrint(const char* message, int line); extern void js_errorPrint(int type, int line, const char* message);
extern void js_writeFunction(const char* message); extern void js_writeFunction(const char* message);
extern const char* js_loadScript(); extern const char* js_loadScript();
void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line, void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line,
const char* message) { const char* message) {
js_errorPrint(message, line); // No need to pass file (since there is only script that'll ever run on the
// browser.
js_errorPrint((int)type, line, message);
} }
void writeFunction(PKVM* vm, const char* text) { void writeFunction(PKVM* vm, const char* text) {

View File

@ -345,6 +345,8 @@ static void reportError(Parser* parser, const char* file, int line,
int length = vsprintf(message, fmt, args); int length = vsprintf(message, fmt, args);
ASSERT(length < ERROR_MESSAGE_SIZE, "Error message buffer should not exceed " ASSERT(length < ERROR_MESSAGE_SIZE, "Error message buffer should not exceed "
"the buffer"); "the buffer");
if (vm->config.error_fn == NULL) return;
vm->config.error_fn(vm, PK_ERROR_COMPILE, file, line, message); vm->config.error_fn(vm, PK_ERROR_COMPILE, file, line, message);
} }

View File

@ -206,6 +206,10 @@ void coreToString(PKVM* vm) {
} }
void corePrint(PKVM* vm) { void corePrint(PKVM* vm) {
// If the host appliaction donesn't provide any write function, discard the
// output.
if (vm->config.write_fn == NULL) return;
String* str; //< Will be cleaned by garbage collector; String* str; //< Will be cleaned by garbage collector;
for (int i = 1; i <= ARGC; i++) { for (int i = 1; i <= ARGC; i++) {

View File

@ -259,18 +259,18 @@ void dumpInstructions(PKVM* vm, Function* func) {
} }
void reportStackTrace(PKVM* vm) { void reportStackTrace(PKVM* vm) {
if (vm->config.error_fn == NULL) return;
Fiber* fiber = vm->fiber; Fiber* fiber = vm->fiber;
Script* script = fiber->func->owner;
//vm->config.error_fn(vm, PK_ERROR_RUNTIME, NULL, -1, fiber->error ) vm->config.error_fn(vm, PK_ERROR_RUNTIME, NULL, -1, fiber->error->data);
//
//// TODO: I'm not confident about this approach.
//if (script->path != NULL) { // User script.
//
//
//} else { // "std" script.
//
//}
for (int i = fiber->frame_count - 1; i >= 0; i--) {
CallFrame* frame = &fiber->frames[i];
Function* fn = frame->fn;
ASSERT(!fn->is_native, OOPS);
int line = fn->fn->oplines.data[frame->ip - fn->fn->opcodes.data - 1];
vm->config.error_fn(vm, PK_ERROR_STACKTRACE, fn->owner->name->data, line, fn->name);
}
} }

View File

@ -83,7 +83,7 @@ typedef void (*pkNativeFn)(PKVM* vm);
typedef enum { typedef enum {
// Compile time errors (syntax errors, unresolved fn, etc). // Compile time errors (syntax errors, unresolved fn, etc).
PK_ERROR_COMPILE, PK_ERROR_COMPILE = 0,
// Runtime error message. // Runtime error message.
PK_ERROR_RUNTIME, PK_ERROR_RUNTIME,

View File

@ -189,6 +189,7 @@ static void blackenObject(Object* obj, PKVM* vm) {
} }
vm->bytes_allocated += sizeof(CallFrame) * fiber->frame_capacity; vm->bytes_allocated += sizeof(CallFrame) * fiber->frame_capacity;
grayObject(&fiber->caller->_super, vm);
grayObject(&fiber->error->_super, vm); grayObject(&fiber->error->_super, vm);
} break; } break;
@ -286,7 +287,7 @@ Script* newScript(PKVM* vm, String* name) {
stringBufferInit(&script->names); stringBufferInit(&script->names);
vmPushTempRef(vm, &script->_super); vmPushTempRef(vm, &script->_super);
const char* fn_name = "@(ScriptLevel)"; const char* fn_name = "$(SourceBody)";
script->body = newFunction(vm, fn_name, (int)strlen(fn_name), script, false); script->body = newFunction(vm, fn_name, (int)strlen(fn_name), script, false);
vmPopTempRef(vm); vmPopTempRef(vm);

View File

@ -336,6 +336,9 @@ struct Fiber {
// Number of frame entry in frames. // Number of frame entry in frames.
int frame_count; int frame_count;
// Caller of this fiber if it has one, NULL otherwise.
Fiber* caller;
// Runtime error initially NULL, heap allocated. // Runtime error initially NULL, heap allocated.
String* error; String* error;
}; };

View File

@ -230,8 +230,9 @@ static Var importScript(PKVM* vm, String* name, bool is_core,
*is_new_script = true; *is_new_script = true;
pkStringResult result = { false, NULL, NULL }; pkStringResult result = { false, NULL, NULL };
if (vm->config.load_script_fn != NULL) if (vm->config.load_script_fn != NULL) {
result = vm->config.load_script_fn(vm, name->data); result = vm->config.load_script_fn(vm, name->data);
}
if (!result.success) { if (!result.success) {
vmPopTempRef(vm); // name vmPopTempRef(vm); // name
@ -290,12 +291,15 @@ static inline void pushCallFrame(PKVM* vm, Function* fn) {
} }
void pkSetRuntimeError(PKVM* vm, const char* message) { void pkSetRuntimeError(PKVM* vm, const char* message) {
vm->fiber->error = stringFormat(vm, "$", message); vm->fiber->error = newString(vm, message);
} }
void vmReportError(PKVM* vm) { void vmReportError(PKVM* vm) {
ASSERT(HAS_ERROR(), "runtimeError() should be called after an error."); ASSERT(HAS_ERROR(), "runtimeError() should be called after an error.");
TODO; // TODO: create debug.h
// TODO: pass the error to the caller of the fiber.
reportStackTrace(vm);
} }
// FIXME: temp. // FIXME: temp.
@ -327,16 +331,20 @@ PKInterpretResult pkInterpret(PKVM* vm, const char* file) {
vmPushTempRef(vm, &name->_super); vmPushTempRef(vm, &name->_super);
if (!resolveScriptPath(vm, &name)) { if (!resolveScriptPath(vm, &name)) {
vm->config.error_fn(vm, PK_ERROR_COMPILE, NULL, -1, if (vm->config.error_fn != NULL) {
stringFormat(vm, "Failed to resolve path '$'.", file)->data); vm->config.error_fn(vm, PK_ERROR_COMPILE, NULL, -1,
stringFormat(vm, "Failed to resolve path '$'.", file)->data);
}
return PK_RESULT_COMPILE_ERROR; return PK_RESULT_COMPILE_ERROR;
} }
// Load the script source. // Load the script source.
pkStringResult res = vm->config.load_script_fn(vm, name->data); pkStringResult res = vm->config.load_script_fn(vm, name->data);
if (!res.success) { if (!res.success) {
vm->config.error_fn(vm, PK_ERROR_COMPILE, NULL, -1, if (vm->config.error_fn != NULL) {
stringFormat(vm, "Failed to load script '@'.", name)->data); vm->config.error_fn(vm, PK_ERROR_COMPILE, NULL, -1,
stringFormat(vm, "Failed to load script '@'.", name)->data);
}
return PK_RESULT_COMPILE_ERROR; return PK_RESULT_COMPILE_ERROR;
} }
@ -390,7 +398,10 @@ void _debugRuntime(PKVM* vm) {
PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
register uint8_t* ip; //< Current instruction pointer. // Reference to the instruction pointer in the call frame.
register uint8_t** ip;
#define IP (*ip) // Convinent macro to the instruction pointer.
register Var* rbp; //< Stack base pointer register. register Var* rbp; //< Stack base pointer register.
register CallFrame* frame; //< Current call frame. register CallFrame* frame; //< Current call frame.
register Script* script; //< Currently executing script. register Script* script; //< Currently executing script.
@ -420,8 +431,8 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
#define POP() (*(--vm->fiber->sp)) #define POP() (*(--vm->fiber->sp))
#define DROP() (--vm->fiber->sp) #define DROP() (--vm->fiber->sp)
#define PEEK() (*(vm->fiber->sp - 1)) #define PEEK() (*(vm->fiber->sp - 1))
#define READ_BYTE() (*ip++) #define READ_BYTE() (*IP++)
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1])) #define READ_SHORT() (IP+=2, (uint16_t)((IP[-2] << 8) | IP[-1]))
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR. // Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
#define CHECK_ERROR() \ #define CHECK_ERROR() \
@ -440,16 +451,12 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
return PK_RESULT_RUNTIME_ERROR; \ return PK_RESULT_RUNTIME_ERROR; \
} while (false) } while (false)
// Store the current frame to vm's call frame before pushing a new frame. // Load the last call frame to vm's execution variables to resume/run the
// Frames rbp will set once it created and will never change. // function.
#define STORE_FRAME() frame->ip = ip
// Update the call frame and ip once vm's call frame pushed or popped.
// fuction call, return or done running imported script.
#define LOAD_FRAME() \ #define LOAD_FRAME() \
do { \ do { \
frame = &vm->fiber->frames[vm->fiber->frame_count-1]; \ frame = &vm->fiber->frames[vm->fiber->frame_count-1]; \
ip = frame->ip; \ ip = &(frame->ip); \
rbp = frame->rbp; \ rbp = frame->rbp; \
script = frame->fn->owner; \ script = frame->fn->owner; \
} while (false) } while (false)
@ -663,9 +670,8 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
CHECK_ERROR(); CHECK_ERROR();
} else { } else {
STORE_FRAME();
pushCallFrame(vm, fn); pushCallFrame(vm, fn);
LOAD_FRAME(); LOAD_FRAME(); //< Load the top frame to vm's execution variables.
} }
} else { } else {
@ -681,13 +687,10 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var* container = (vm->fiber->sp - 3); Var* container = (vm->fiber->sp - 3);
uint16_t jump_offset = READ_SHORT(); uint16_t jump_offset = READ_SHORT();
bool is_done = varIterate(vm, *container, iterator, iter_value); bool iterated = varIterate(vm, *container, iterator, iter_value);
CHECK_ERROR(); CHECK_ERROR();
if (!is_done) { if (!iterated) {
DROP(); //< Iter value. IP += jump_offset;
DROP(); //< Iterator.
DROP(); //< Container.
ip += jump_offset;
} }
DISPATCH(); DISPATCH();
} }
@ -695,14 +698,14 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
OPCODE(JUMP): OPCODE(JUMP):
{ {
uint16_t offset = READ_SHORT(); uint16_t offset = READ_SHORT();
ip += offset; IP += offset;
DISPATCH(); DISPATCH();
} }
OPCODE(LOOP): OPCODE(LOOP):
{ {
uint16_t offset = READ_SHORT(); uint16_t offset = READ_SHORT();
ip -= offset; IP -= offset;
DISPATCH(); DISPATCH();
} }
@ -712,7 +715,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var cond = POP(); Var cond = POP();
uint16_t offset = READ_SHORT(); uint16_t offset = READ_SHORT();
if (toBool(cond)) { if (toBool(cond)) {
ip += offset; IP += offset;
} }
DISPATCH(); DISPATCH();
} }
@ -722,14 +725,18 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var cond = POP(); Var cond = POP();
uint16_t offset = READ_SHORT(); uint16_t offset = READ_SHORT();
if (!toBool(cond)) { if (!toBool(cond)) {
ip += offset; IP += offset;
} }
DISPATCH(); DISPATCH();
} }
OPCODE(RETURN): OPCODE(RETURN):
{ {
// TODO: handle caller fiber.
Var ret = POP(); Var ret = POP();
// Pop the last frame.
vm->fiber->frame_count--; vm->fiber->frame_count--;
// If no more call frames. We're done. // If no more call frames. We're done.
@ -754,6 +761,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var on = POP(); Var on = POP();
String* name = script->names.data[READ_SHORT()]; String* name = script->names.data[READ_SHORT()];
PUSH(varGetAttrib(vm, on, name)); PUSH(varGetAttrib(vm, on, name));
CHECK_ERROR();
DISPATCH(); DISPATCH();
} }
@ -762,6 +770,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var on = PEEK(); Var on = PEEK();
String* name = script->names.data[READ_SHORT()]; String* name = script->names.data[READ_SHORT()];
PUSH(varGetAttrib(vm, on, name)); PUSH(varGetAttrib(vm, on, name));
CHECK_ERROR();
DISPATCH(); DISPATCH();
} }
@ -772,6 +781,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
String* name = script->names.data[READ_SHORT()]; String* name = script->names.data[READ_SHORT()];
varSetAttrib(vm, on, name, value); varSetAttrib(vm, on, name, value);
PUSH(value); PUSH(value);
CHECK_ERROR();
DISPATCH(); DISPATCH();
} }
@ -864,7 +874,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
OPCODE(MOD): OPCODE(MOD):
{ {
Var r = POP(), l = POP(); Var r = POP(), l = POP();
PUSH(varModulo(vm, r, l)); PUSH(varModulo(vm, l, r));
CHECK_ERROR(); CHECK_ERROR();
DISPATCH(); DISPATCH();
} }

17
test/examples/fib.pk Normal file
View File

@ -0,0 +1,17 @@
## Fib test.
res = ''
def fib(n)
a = 0; b = 1
for _ in 0..n
res += to_string(a) + " "
temp = a;
a = b;
b += temp;
end
end
fib(10)
assert(res == '0 1 1 2 3 5 8 13 21 34 ')

View File

@ -1,3 +1,8 @@
## Prime numbers.
res = ''
def is_prime(n) def is_prime(n)
if n < 2 then return false end if n < 2 then return false end
for i in 2..n for i in 2..n
@ -6,11 +11,13 @@ def is_prime(n)
return true return true
end end
def print_all_primes(n) def get_all_primes(n)
for i in 0..n for i in 0..n
if is_prime(i) then print(i) end if is_prime(i)
res += to_string(i) + ' '
end
end end
end end
get_all_primes(20)
print_all_primes(10) assert(res == '2 3 5 7 11 13 17 19 ')

View File

@ -1,16 +0,0 @@
## 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 ')

View File

@ -4,8 +4,8 @@ set files=( ^
lang\basics.pk ^ lang\basics.pk ^
lang\import.pk ^ lang\import.pk ^
lang\if.pk ^ lang\if.pk ^
lang\locals.pk ^
^ ^
examples\fib.pk ^
examples\prime.pk ^ examples\prime.pk ^
) )