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_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_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_instance_t *instance = wasm_instance_new(store, module, &imports, &trap);
if (!instance) {
printf("> Error instantiating loaded module!\n");
if (trap) {
print_trap(trap);
wasm_trap_delete(trap);
}
return trap_from_string("error instantiating module");
assert(trap != NULL);
return trap;
}
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 ret = -1;
wasm_engine_t *engine = wasm_engine_new();
store = wasm_store_new(engine);
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_instance_exports(instance, &exports);
if (exports.size == 0) {
printf("error accessing export\n");
printf("error accessing exports\n");
return -1;
}
@ -178,27 +178,43 @@ int main(int argc, char *argv_main[]) {
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");
wasm_val_t as[1] = {WASM_I32_VAL(0)};
wasm_val_vec_t args = WASM_ARRAY_VEC(as);
wasm_val_t vs[] = {WASM_INIT_VAL};
wasm_val_vec_t results_vec = WASM_ARRAY_VEC(vs);
while (true) {
trap = wasm_func_call(interpret_fn, &args, &results_vec);
if (trap == NULL) {
// No trap means the input buffer was no longer filled when asked.
// This means the program should exit.
wasm_val_t interpret_as[1] = {WASM_I32_VAL(0)};
wasm_val_vec_t interpret_args = WASM_ARRAY_VEC(interpret_as);
wasm_val_vec_t interpret_results = WASM_EMPTY_VEC;
wasm_val_vec_t err_args = WASM_EMPTY_VEC;
wasm_val_t err_results_vs[] = {WASM_INIT_VAL};
wasm_val_vec_t err_results = WASM_ARRAY_VEC(err_results_vs);
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;
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);
@ -211,5 +227,5 @@ int main(int argc, char *argv_main[]) {
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return ret;
return 0;
}

View file

@ -708,8 +708,10 @@
;; 6.1.0670 ABORT
(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")
(elem (i32.const 0x3b) $ABORT)
;; 6.1.0680 ABORT"
(func $ABORTq (param $tos i32) (result i32)
@ -1059,7 +1061,7 @@
(global.set $inputBufferSize (i32.load (i32.sub (local.get $tos) (i32.const 4))))
(i32.store (i32.const 0x20294 (; = body(>IN) ;)) (i32.const 0))
(drop (call $interpret (local.get $bbtos)))
(call $interpret (local.get $bbtos))
;; Restore input state
(global.set $sourceID (local.get $prevSourceID))
@ -1393,11 +1395,8 @@
;; 6.1.2050
(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))
(global.set $error (i32.const 0x2 (; = ERR_QUIT ;)))
(call $quit (local.get $tos)))
(data (i32.const 0x206ac) "\98\06\02\00" "\04" "QUIT " "\7b\00\00\00")
(elem (i32.const 0x7b) $QUIT)
@ -1851,7 +1850,7 @@
;; 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
;; was not found.
(func $interpret (param $tos i32) (result i32) (result i32)
(func $interpret (param $tos i32) (result i32)
(local $FINDResult i32)
(local $FINDToken i32)
(local $error i32)
@ -1897,8 +1896,7 @@
;; We're compiling a non-immediate
(local.set $tos (call $compileCall (local.get $tos) (local.get $FINDToken)))))))
(br $loop)))
(local.get $tos)
(i32.load (i32.const 0x207bc (; = body(STATE) ;))))
(local.get $tos))
;; Returns (number, unparsed length)
(func $readNumber (param $addr i32) (param $len i32) (result i32 i32)
@ -1962,6 +1960,13 @@
(i64.mul (local.get $sign) (local.get $value))
(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
@ -1988,6 +1993,17 @@
;; Pictured output pointer
(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
;;
@ -2734,17 +2750,20 @@
(func (export "here") (result i32)
(global.get $here))
(func (export "error") (result i32)
(global.get $error))
(func (export "interpret") (param $silent i32) (result i32)
(local $result i32)
(func (export "interpret") (param $silent i32)
(local $state i32)
(local $tos i32)
(local.tee $tos (global.get $tos))
(global.set $error (i32.const 0x1 (; = ERR_UNKNOWN ;)))
(block $endLoop (param i32) (result i32)
(loop $loop (param i32) (result i32)
(call $REFILL)
(br_if $endLoop (i32.eqz (call $pop)))
(local.set $result (call $interpret))
(local.set $tos)
(local.set $tos (call $interpret))
;; Check for stack underflow
(if (i32.lt_s (local.get $tos) (i32.const 0x10000 (; = STACK_BASE ;)))
@ -2753,7 +2772,7 @@
;; Show prompt
(if (i32.eqz (local.get $silent))
(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
;; Write ok
(call $shell_emit (i32.const 111))
@ -2769,7 +2788,7 @@
(local.get $tos)
(br $loop)))
(global.set $tos)
(local.get $result))
(global.set $error (i32.const 0x4 (; = ERR_EOI ;))))
(func (export "push") (param $v i32)
(global.set $tos (call $push (global.get $tos) (local.get $v))))

View file

@ -93,15 +93,15 @@ function loadTests() {
}
function run(s, expectErrors = false) {
const r = forth.interpret(s, true);
forth.interpret(s, true);
const r = forth.core.exports.error();
if (expectErrors) {
expect(r).to.be.undefined;
expect(r).to.not.eql(4);
} else {
expect(r).to.not.be.an(
"undefined",
"Error running: " + s + "; Output: " + output
expect(r).to.eql(
4,
"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", () => {
forth.read("123");
expect(core.interpret()).to.eql(0);
core.interpret();
expect(core.error()).to.eql(4);
expect(stackValues()[0]).to.eql(123);
});
it("should interpret a negative number", () => {
forth.read("-123");
expect(core.interpret()).to.eql(0);
core.interpret();
expect(core.error()).to.eql(4);
expect(stackValues()[0]).to.eql(-123);
});
it("should interpret a hex", () => {
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);
});
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.
*
@ -235,8 +242,12 @@ class WAForth {
return (this.core!.exports.interpret as any)(silent);
} catch (e) {
// 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;
}
/**