mirror of
https://github.com/remko/waforth
synced 2024-12-26 09:59:09 +01:00
interpret: Remove state return argument
This commit is contained in:
parent
eec7bf9be8
commit
afffcbbfb3
4 changed files with 104 additions and 55 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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))))
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue