mirror of
https://github.com/remko/waforth
synced 2024-12-27 09:59:29 +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_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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -1963,6 +1961,13 @@
|
||||||
(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
|
||||||
;;
|
;;
|
||||||
|
@ -2735,16 +2751,19 @@
|
||||||
(func (export "here") (result i32)
|
(func (export "here") (result i32)
|
||||||
(global.get $here))
|
(global.get $here))
|
||||||
|
|
||||||
(func (export "interpret") (param $silent i32) (result i32)
|
(func (export "error") (result i32)
|
||||||
(local $result i32)
|
(global.get $error))
|
||||||
|
|
||||||
|
(func (export "interpret") (param $silent 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))))
|
||||||
|
|
|
@ -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", () => {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue