mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
fixed: iterator (internal) variables popped twise.
printing stack trace implemented
This commit is contained in:
parent
9c77ff5a6c
commit
c3041c74a9
@ -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) {
|
||||
|
39
docs/static/try_now.js
vendored
39
docs/static/try_now.js
vendored
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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++) {
|
||||
|
24
src/debug.c
24
src/debug.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
70
src/vm.c
70
src/vm.c
@ -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
17
test/examples/fib.pk
Normal 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 ')
|
@ -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 ')
|
@ -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 ')
|
@ -4,8 +4,8 @@ set files=( ^
|
||||
lang\basics.pk ^
|
||||
lang\import.pk ^
|
||||
lang\if.pk ^
|
||||
lang\locals.pk ^
|
||||
^
|
||||
examples\fib.pk ^
|
||||
examples\prime.pk ^
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user