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

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, {
/** 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");
out.innerText += `[Error at:${line}]: ${AsciiToString(message)} \n`;
out.innerText += err_text + '\n';
},
js_writeFunction : function(message) {

View File

@ -10,13 +10,15 @@
#include <emscripten.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 const char* js_loadScript();
void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line,
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) {

View File

@ -345,6 +345,8 @@ static void reportError(Parser* parser, const char* file, int line,
int length = vsprintf(message, fmt, args);
ASSERT(length < ERROR_MESSAGE_SIZE, "Error message buffer should not exceed "
"the buffer");
if (vm->config.error_fn == NULL) return;
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) {
// 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;
for (int i = 1; i <= ARGC; i++) {

View File

@ -259,18 +259,18 @@ void dumpInstructions(PKVM* vm, Function* func) {
}
void reportStackTrace(PKVM* vm) {
Fiber* fiber = vm->fiber;
Script* script = fiber->func->owner;
if (vm->config.error_fn == NULL) return;
//vm->config.error_fn(vm, PK_ERROR_RUNTIME, NULL, -1, fiber->error )
//
//// TODO: I'm not confident about this approach.
//if (script->path != NULL) { // User script.
//
//
//} else { // "std" script.
//
//}
Fiber* fiber = vm->fiber;
vm->config.error_fn(vm, PK_ERROR_RUNTIME, NULL, -1, fiber->error->data);
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 {
// Compile time errors (syntax errors, unresolved fn, etc).
PK_ERROR_COMPILE,
PK_ERROR_COMPILE = 0,
// Runtime error message.
PK_ERROR_RUNTIME,

View File

@ -189,6 +189,7 @@ static void blackenObject(Object* obj, PKVM* vm) {
}
vm->bytes_allocated += sizeof(CallFrame) * fiber->frame_capacity;
grayObject(&fiber->caller->_super, vm);
grayObject(&fiber->error->_super, vm);
} break;
@ -286,7 +287,7 @@ Script* newScript(PKVM* vm, String* name) {
stringBufferInit(&script->names);
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);
vmPopTempRef(vm);

View File

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

View File

@ -230,8 +230,9 @@ static Var importScript(PKVM* vm, String* name, bool is_core,
*is_new_script = true;
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);
}
if (!result.success) {
vmPopTempRef(vm); // name
@ -290,12 +291,15 @@ static inline void pushCallFrame(PKVM* vm, Function* fn) {
}
void pkSetRuntimeError(PKVM* vm, const char* message) {
vm->fiber->error = stringFormat(vm, "$", message);
vm->fiber->error = newString(vm, message);
}
void vmReportError(PKVM* vm) {
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.
@ -327,16 +331,20 @@ PKInterpretResult pkInterpret(PKVM* vm, const char* file) {
vmPushTempRef(vm, &name->_super);
if (!resolveScriptPath(vm, &name)) {
vm->config.error_fn(vm, PK_ERROR_COMPILE, NULL, -1,
stringFormat(vm, "Failed to resolve path '$'.", file)->data);
if (vm->config.error_fn != NULL) {
vm->config.error_fn(vm, PK_ERROR_COMPILE, NULL, -1,
stringFormat(vm, "Failed to resolve path '$'.", file)->data);
}
return PK_RESULT_COMPILE_ERROR;
}
// Load the script source.
pkStringResult res = vm->config.load_script_fn(vm, name->data);
if (!res.success) {
vm->config.error_fn(vm, PK_ERROR_COMPILE, NULL, -1,
stringFormat(vm, "Failed to load script '@'.", name)->data);
if (vm->config.error_fn != NULL) {
vm->config.error_fn(vm, PK_ERROR_COMPILE, NULL, -1,
stringFormat(vm, "Failed to load script '@'.", name)->data);
}
return PK_RESULT_COMPILE_ERROR;
}
@ -390,7 +398,10 @@ void _debugRuntime(PKVM* vm) {
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 CallFrame* frame; //< Current call frame.
register Script* script; //< Currently executing script.
@ -420,8 +431,8 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
#define POP() (*(--vm->fiber->sp))
#define DROP() (--vm->fiber->sp)
#define PEEK() (*(vm->fiber->sp - 1))
#define READ_BYTE() (*ip++)
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1]))
#define READ_BYTE() (*IP++)
#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.
#define CHECK_ERROR() \
@ -440,16 +451,12 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
return PK_RESULT_RUNTIME_ERROR; \
} while (false)
// Store the current frame to vm's call frame before pushing a new frame.
// Frames rbp will set once it created and will never change.
#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.
// Load the last call frame to vm's execution variables to resume/run the
// function.
#define LOAD_FRAME() \
do { \
frame = &vm->fiber->frames[vm->fiber->frame_count-1]; \
ip = frame->ip; \
ip = &(frame->ip); \
rbp = frame->rbp; \
script = frame->fn->owner; \
} while (false)
@ -663,9 +670,8 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
CHECK_ERROR();
} else {
STORE_FRAME();
pushCallFrame(vm, fn);
LOAD_FRAME();
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
}
} else {
@ -681,13 +687,10 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var* container = (vm->fiber->sp - 3);
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();
if (!is_done) {
DROP(); //< Iter value.
DROP(); //< Iterator.
DROP(); //< Container.
ip += jump_offset;
if (!iterated) {
IP += jump_offset;
}
DISPATCH();
}
@ -695,14 +698,14 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
OPCODE(JUMP):
{
uint16_t offset = READ_SHORT();
ip += offset;
IP += offset;
DISPATCH();
}
OPCODE(LOOP):
{
uint16_t offset = READ_SHORT();
ip -= offset;
IP -= offset;
DISPATCH();
}
@ -712,7 +715,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var cond = POP();
uint16_t offset = READ_SHORT();
if (toBool(cond)) {
ip += offset;
IP += offset;
}
DISPATCH();
}
@ -722,14 +725,18 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var cond = POP();
uint16_t offset = READ_SHORT();
if (!toBool(cond)) {
ip += offset;
IP += offset;
}
DISPATCH();
}
OPCODE(RETURN):
{
// TODO: handle caller fiber.
Var ret = POP();
// Pop the last frame.
vm->fiber->frame_count--;
// If no more call frames. We're done.
@ -754,6 +761,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var on = POP();
String* name = script->names.data[READ_SHORT()];
PUSH(varGetAttrib(vm, on, name));
CHECK_ERROR();
DISPATCH();
}
@ -762,6 +770,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Var on = PEEK();
String* name = script->names.data[READ_SHORT()];
PUSH(varGetAttrib(vm, on, name));
CHECK_ERROR();
DISPATCH();
}
@ -772,6 +781,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
String* name = script->names.data[READ_SHORT()];
varSetAttrib(vm, on, name, value);
PUSH(value);
CHECK_ERROR();
DISPATCH();
}
@ -864,7 +874,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
OPCODE(MOD):
{
Var r = POP(), l = POP();
PUSH(varModulo(vm, r, l));
PUSH(varModulo(vm, l, r));
CHECK_ERROR();
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)
if n < 2 then return false end
for i in 2..n
@ -6,11 +11,13 @@ def is_prime(n)
return true
end
def print_all_primes(n)
def get_all_primes(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
print_all_primes(10)
get_all_primes(20)
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\import.pk ^
lang\if.pk ^
lang\locals.pk ^
^
examples\fib.pk ^
examples\prime.pk ^
)