interpret: Remove state return argument

This commit is contained in:
Remko Tronçon 2022-05-30 21:15:22 +02:00
parent eec7bf9be8
commit afffcbbfb3
4 changed files with 104 additions and 55 deletions

View file

@ -10,7 +10,13 @@
#define CORE_TABLE_EXPORT_INDEX 0 #define CORE_TABLE_EXPORT_INDEX 0
#define CORE_MEMORY_EXPORT_INDEX 1 #define CORE_MEMORY_EXPORT_INDEX 1
#define CORE_INTERPRET_EXPORT_INDEX 6 #define CORE_ERROR_EXPORT_INDEX 6
#define CORE_INTERPRET_EXPORT_INDEX 7
#define ERR_UNKNOWN 0x1
#define ERR_QUIT 0x2
#define ERR_ABORT 0x3
#define ERR_EOI 0x4
wasm_memory_t *memory; wasm_memory_t *memory;
wasm_table_t *table; wasm_table_t *table;
@ -85,12 +91,8 @@ wasm_trap_t *load_cb(const wasm_val_vec_t *args, wasm_val_vec_t *results) {
wasm_trap_t *trap = NULL; wasm_trap_t *trap = NULL;
wasm_instance_t *instance = wasm_instance_new(store, module, &imports, &trap); wasm_instance_t *instance = wasm_instance_new(store, module, &imports, &trap);
if (!instance) { if (!instance) {
printf("> Error instantiating loaded module!\n"); assert(trap != NULL);
if (trap) { return trap;
print_trap(trap);
wasm_trap_delete(trap);
}
return trap_from_string("error instantiating module");
} }
return NULL; return NULL;
} }
@ -108,8 +110,6 @@ wasm_trap_t *call_cb(void *env, const wasm_val_vec_t *args, wasm_val_vec_t *resu
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv_main[]) { int main(int argc, char *argv_main[]) {
int ret = -1;
wasm_engine_t *engine = wasm_engine_new(); wasm_engine_t *engine = wasm_engine_new();
store = wasm_store_new(engine); store = wasm_store_new(engine);
wasm_byte_vec_t core = {.data = (wasm_byte_t *)waforth_core, .size = sizeof(waforth_core)}; wasm_byte_vec_t core = {.data = (wasm_byte_t *)waforth_core, .size = sizeof(waforth_core)};
@ -156,7 +156,7 @@ int main(int argc, char *argv_main[]) {
wasm_extern_vec_t exports; wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports); wasm_instance_exports(instance, &exports);
if (exports.size == 0) { if (exports.size == 0) {
printf("error accessing export\n"); printf("error accessing exports\n");
return -1; return -1;
} }
@ -178,27 +178,43 @@ int main(int argc, char *argv_main[]) {
return -1; return -1;
} }
const wasm_func_t *error_fn = wasm_extern_as_func(exports.data[CORE_ERROR_EXPORT_INDEX]);
if (error_fn == NULL) {
printf("error accessing `error` export\n");
return -1;
}
printf("WAForth (" VERSION ")\n"); printf("WAForth (" VERSION ")\n");
wasm_val_t as[1] = {WASM_I32_VAL(0)}; wasm_val_t interpret_as[1] = {WASM_I32_VAL(0)};
wasm_val_vec_t args = WASM_ARRAY_VEC(as); wasm_val_vec_t interpret_args = WASM_ARRAY_VEC(interpret_as);
wasm_val_t vs[] = {WASM_INIT_VAL}; wasm_val_vec_t interpret_results = WASM_EMPTY_VEC;
wasm_val_vec_t results_vec = WASM_ARRAY_VEC(vs);
while (true) { wasm_val_vec_t err_args = WASM_EMPTY_VEC;
trap = wasm_func_call(interpret_fn, &args, &results_vec); wasm_val_t err_results_vs[] = {WASM_INIT_VAL};
if (trap == NULL) { wasm_val_vec_t err_results = WASM_ARRAY_VEC(err_results_vs);
// No trap means the input buffer was no longer filled when asked.
// This means the program should exit. for (int stopped = 0; !stopped;) {
trap = wasm_func_call(interpret_fn, &interpret_args, &interpret_results);
wasm_trap_t *etrap = wasm_func_call(error_fn, &err_args, &err_results);
assert(etrap == NULL);
switch (err_results.data[0].of.i32) {
case ERR_QUIT:
case ERR_ABORT:
assert(trap != NULL);
wasm_trap_delete(trap);
break; break;
case ERR_EOI:
assert(trap == NULL);
stopped = true;
break;
case ERR_UNKNOWN:
assert(trap != NULL);
print_trap(trap);
wasm_trap_delete(trap);
break;
default:
assert(false);
} }
wasm_name_t message;
wasm_trap_message(trap, &message);
// `unreachable` is called when we want to reset the call stack, and start the interpreter
// loop again (i.e. when QUIT is called)
if (strstr(message.data, "wasm `unreachable` instruction executed") == NULL) {
printf("error: %s\n", message.data);
}
wasm_name_delete(&message);
wasm_trap_delete(trap);
} }
wasm_extern_vec_delete(&exports); wasm_extern_vec_delete(&exports);
@ -211,5 +227,5 @@ int main(int argc, char *argv_main[]) {
wasm_module_delete(module); wasm_module_delete(module);
wasm_store_delete(store); wasm_store_delete(store);
wasm_engine_delete(engine); wasm_engine_delete(engine);
return ret; return 0;
} }

View file

@ -708,8 +708,10 @@
;; 6.1.0670 ABORT ;; 6.1.0670 ABORT
(func $ABORT (param $tos i32) (result i32) (func $ABORT (param $tos i32) (result i32)
(call $QUIT (i32.const 0x10000 (; = STACK_BASE ;)))) (global.set $error (i32.const 0x3 (; = ERR_ABORT ;)))
(call $quit (i32.const 0x10000 (; = STACK_BASE ;))))
(data (i32.const 0x202d0) "\c4\02\02\00" "\05" "ABORT " "\3b\00\00\00") (data (i32.const 0x202d0) "\c4\02\02\00" "\05" "ABORT " "\3b\00\00\00")
(elem (i32.const 0x3b) $ABORT)
;; 6.1.0680 ABORT" ;; 6.1.0680 ABORT"
(func $ABORTq (param $tos i32) (result i32) (func $ABORTq (param $tos i32) (result i32)
@ -1059,7 +1061,7 @@
(global.set $inputBufferSize (i32.load (i32.sub (local.get $tos) (i32.const 4)))) (global.set $inputBufferSize (i32.load (i32.sub (local.get $tos) (i32.const 4))))
(i32.store (i32.const 0x20294 (; = body(>IN) ;)) (i32.const 0)) (i32.store (i32.const 0x20294 (; = body(>IN) ;)) (i32.const 0))
(drop (call $interpret (local.get $bbtos))) (call $interpret (local.get $bbtos))
;; Restore input state ;; Restore input state
(global.set $sourceID (local.get $prevSourceID)) (global.set $sourceID (local.get $prevSourceID))
@ -1393,11 +1395,8 @@
;; 6.1.2050 ;; 6.1.2050
(func $QUIT (param $tos i32) (result i32) (func $QUIT (param $tos i32) (result i32)
(global.set $tos (local.get $tos)) (global.set $error (i32.const 0x2 (; = ERR_QUIT ;)))
(global.set $tors (i32.const 0x2000 (; = RETURN_STACK_BASE ;))) (call $quit (local.get $tos)))
(global.set $sourceID (i32.const 0))
(i32.store (i32.const 0x207bc (; = body(STATE) ;)) (i32.const 0))
(unreachable))
(data (i32.const 0x206ac) "\98\06\02\00" "\04" "QUIT " "\7b\00\00\00") (data (i32.const 0x206ac) "\98\06\02\00" "\04" "QUIT " "\7b\00\00\00")
(elem (i32.const 0x7b) $QUIT) (elem (i32.const 0x7b) $QUIT)
@ -1851,7 +1850,7 @@
;; Interprets the string in the input, until the end of string is reached. ;; Interprets the string in the input, until the end of string is reached.
;; Returns 0 if processed, 1 if still compiling, or traps if a word ;; Returns 0 if processed, 1 if still compiling, or traps if a word
;; was not found. ;; was not found.
(func $interpret (param $tos i32) (result i32) (result i32) (func $interpret (param $tos i32) (result i32)
(local $FINDResult i32) (local $FINDResult i32)
(local $FINDToken i32) (local $FINDToken i32)
(local $error i32) (local $error i32)
@ -1897,8 +1896,7 @@
;; We're compiling a non-immediate ;; We're compiling a non-immediate
(local.set $tos (call $compileCall (local.get $tos) (local.get $FINDToken))))))) (local.set $tos (call $compileCall (local.get $tos) (local.get $FINDToken)))))))
(br $loop))) (br $loop)))
(local.get $tos) (local.get $tos))
(i32.load (i32.const 0x207bc (; = body(STATE) ;))))
;; Returns (number, unparsed length) ;; Returns (number, unparsed length)
(func $readNumber (param $addr i32) (param $len i32) (result i32 i32) (func $readNumber (param $addr i32) (param $len i32) (result i32 i32)
@ -1962,6 +1960,13 @@
(i64.mul (local.get $sign) (local.get $value)) (i64.mul (local.get $sign) (local.get $value))
(local.get $p) (local.get $p)
(i32.sub (local.get $end) (local.get $p))) (i32.sub (local.get $end) (local.get $p)))
(func $quit (param $tos i32) (result i32)
(global.set $tos (local.get $tos))
(global.set $tors (i32.const 0x2000 (; = RETURN_STACK_BASE ;)))
(global.set $sourceID (i32.const 0))
(i32.store (i32.const 0x207bc (; = body(STATE) ;)) (i32.const 0))
(unreachable))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interpreter state ;; Interpreter state
@ -1988,6 +1993,17 @@
;; Pictured output pointer ;; Pictured output pointer
(global $po (mut i32) (i32.const -1)) (global $po (mut i32) (i32.const -1))
;; Error code
;;
;; This can only be inspected after an interpret() call (using error()).
;;
;; Values:
;; ERR_UNKNOWN := 0x1 (Unknown error, e.g. exception during one of the `shell` functions)
;; ERR_QUIT := 0x2 (QUIT called)
;; ERR_ABORT := 0x3 (ABORT or ABORT" called)
;; ERR_EOI := 0x4 (No more user input received)
(global $error (mut i32) (i32.const 0x0))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compiler functions ;; Compiler functions
;; ;;
@ -2734,17 +2750,20 @@
(func (export "here") (result i32) (func (export "here") (result i32)
(global.get $here)) (global.get $here))
(func (export "error") (result i32)
(global.get $error))
(func (export "interpret") (param $silent i32) (result i32) (func (export "interpret") (param $silent i32)
(local $result i32) (local $state i32)
(local $tos i32) (local $tos i32)
(local.tee $tos (global.get $tos)) (local.tee $tos (global.get $tos))
(global.set $error (i32.const 0x1 (; = ERR_UNKNOWN ;)))
(block $endLoop (param i32) (result i32) (block $endLoop (param i32) (result i32)
(loop $loop (param i32) (result i32) (loop $loop (param i32) (result i32)
(call $REFILL) (call $REFILL)
(br_if $endLoop (i32.eqz (call $pop))) (br_if $endLoop (i32.eqz (call $pop)))
(local.set $result (call $interpret)) (local.set $tos (call $interpret))
(local.set $tos)
;; Check for stack underflow ;; Check for stack underflow
(if (i32.lt_s (local.get $tos) (i32.const 0x10000 (; = STACK_BASE ;))) (if (i32.lt_s (local.get $tos) (i32.const 0x10000 (; = STACK_BASE ;)))
@ -2753,7 +2772,7 @@
;; Show prompt ;; Show prompt
(if (i32.eqz (local.get $silent)) (if (i32.eqz (local.get $silent))
(then (then
(if (i32.ge_s (local.get $result) (i32.const 0)) (if (i32.ge_s (i32.load (i32.const 0x207bc (; = body(STATE) ;))) (i32.const 0))
(then (then
;; Write ok ;; Write ok
(call $shell_emit (i32.const 111)) (call $shell_emit (i32.const 111))
@ -2769,7 +2788,7 @@
(local.get $tos) (local.get $tos)
(br $loop))) (br $loop)))
(global.set $tos) (global.set $tos)
(local.get $result)) (global.set $error (i32.const 0x4 (; = ERR_EOI ;))))
(func (export "push") (param $v i32) (func (export "push") (param $v i32)
(global.set $tos (call $push (global.get $tos) (local.get $v)))) (global.set $tos (call $push (global.get $tos) (local.get $v))))

View file

@ -93,15 +93,15 @@ function loadTests() {
} }
function run(s, expectErrors = false) { function run(s, expectErrors = false) {
const r = forth.interpret(s, true); forth.interpret(s, true);
const r = forth.core.exports.error();
if (expectErrors) { if (expectErrors) {
expect(r).to.be.undefined; expect(r).to.not.eql(4);
} else { } else {
expect(r).to.not.be.an( expect(r).to.eql(
"undefined", 4,
"Error running: " + s + "; Output: " + output "Error " + r + " running: " + s + "; Output: " + output
); );
expect(r).to.not.be.below(0);
} }
} }
@ -197,18 +197,21 @@ function loadTests() {
it("should interpret a positive number", () => { it("should interpret a positive number", () => {
forth.read("123"); forth.read("123");
expect(core.interpret()).to.eql(0); core.interpret();
expect(core.error()).to.eql(4);
expect(stackValues()[0]).to.eql(123); expect(stackValues()[0]).to.eql(123);
}); });
it("should interpret a negative number", () => { it("should interpret a negative number", () => {
forth.read("-123"); forth.read("-123");
expect(core.interpret()).to.eql(0); core.interpret();
expect(core.error()).to.eql(4);
expect(stackValues()[0]).to.eql(-123); expect(stackValues()[0]).to.eql(-123);
}); });
it("should interpret a hex", () => { it("should interpret a hex", () => {
forth.read("16 BASE ! DF"); forth.read("16 BASE ! DF");
expect(core.interpret()).to.eql(0); core.interpret();
expect(core.error()).to.eql(4);
expect(stackValues()[0]).to.eql(223); expect(stackValues()[0]).to.eql(223);
}); });
it("should not interpret hex in decimal mode", () => { it("should not interpret hex in decimal mode", () => {

View file

@ -36,6 +36,13 @@ function saveString(s: string, memory: WebAssembly.Memory, addr: number) {
} }
} }
enum ErrorCode {
Unknown = 0x1, // Unknown error
Quit = 0x2, // QUIT was called
Abort = 0x3, // ABORT or ABORT" was called
EOI = 0x4, // No more input
}
/** /**
* JavaScript shell around the WAForth WebAssembly module. * JavaScript shell around the WAForth WebAssembly module.
* *
@ -235,8 +242,12 @@ class WAForth {
return (this.core!.exports.interpret as any)(silent); return (this.core!.exports.interpret as any)(silent);
} catch (e) { } catch (e) {
// Exceptions thrown from the core means QUIT or ABORT is called, or an error // Exceptions thrown from the core means QUIT or ABORT is called, or an error
// has occurred. Assume what has been done has been done, and ignore here. // has occurred.
if ((this.core!.exports.error as any)() === ErrorCode.Unknown) {
console.error(e);
}
} }
return (this.core!.exports.error as any)() as ErrorCode;
} }
/** /**