diff --git a/src/waforth.wat b/src/waforth.wat index ff1d935..e427be6 100644 --- a/src/waforth.wat +++ b/src/waforth.wat @@ -2758,29 +2758,34 @@ (local $result i32) (local $tos i32) (local.tee $tos (global.get $tos)) - (call $REFILL) - (drop (call $pop)) - (local.set $result (call $interpret)) - (global.set $tos) + (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) - ;; Check for stack underflow - (if (i32.lt_s (global.get $tos) (i32.const 0x10000 (; = STACK_BASE ;))) - (drop (call $fail (global.get $tos) (i32.const 0x200B2 (; stack empty ;))))) - - (if (i32.ge_s (local.get $result) (i32.const 0)) - (then - ;; Write ok - (call $shell_emit (i32.const 111)) - (call $shell_emit (i32.const 107))) - (else - ;; Write error - (call $shell_emit (i32.const 101)) - (call $shell_emit (i32.const 114)) - (call $shell_emit (i32.const 114)) - (call $shell_emit (i32.const 111)) - (call $shell_emit (i32.const 114)))) - (call $shell_emit (i32.const 10)) - (local.get $result)) + ;; Check for stack underflow + (if (i32.lt_s (local.get $tos) (i32.const 0x10000 (; = STACK_BASE ;))) + (drop (call $fail (local.get $tos) (i32.const 0x200B2 (; stack empty ;))))) + + (if (i32.ge_s (local.get $result) (i32.const 0)) + (then + ;; Write ok + (call $shell_emit (i32.const 111)) + (call $shell_emit (i32.const 107))) + (else + ;; Write error + (call $shell_emit (i32.const 101)) + (call $shell_emit (i32.const 114)) + (call $shell_emit (i32.const 114)) + (call $shell_emit (i32.const 111)) + (call $shell_emit (i32.const 114)))) + (call $shell_emit (i32.const 10)) + (local.get $tos) + (br $loop))) + (global.set $tos) + (local.get $result)) (func (export "push") (param $v i32) (global.set $tos (call $push (global.get $tos) (local.get $v)))) diff --git a/src/web/tests/suite.js b/src/web/tests/suite.js index 45f4018..4df2bc0 100644 --- a/src/web/tests/suite.js +++ b/src/web/tests/suite.js @@ -222,6 +222,15 @@ function loadTests() { expect(() => core.interpret()).to.throw(); expect(output.trim()).to.eql("undefined word: 23FOO"); }); + + it("should interpret a long string", () => { + const p = ["1"]; + for (let i = 0; i < 1000; i++) { + p.push(`${1} +`); + } + forth.interpret(p.join("\n")); + expect(stackValues()).to.eql([1001]); + }); }); describe("DUP", () => { diff --git a/src/web/waforth.ts b/src/web/waforth.ts index 90f41f6..b412950 100644 --- a/src/web/waforth.ts +++ b/src/web/waforth.ts @@ -46,7 +46,7 @@ function saveString(s: string, memory: WebAssembly.Memory, addr: number) { * */ class WAForth { core?: WebAssembly.Instance; - #buffer?: number[]; + #buffer?: string; #fns: Record void>; /** @@ -96,7 +96,7 @@ class WAForth { async load() { let table: WebAssembly.Table; let memory: WebAssembly.Memory; - this.#buffer = []; + this.#buffer = ""; const instance = await WebAssembly.instantiate(wasmModule, { shell: { @@ -111,17 +111,22 @@ class WAForth { }, read: (addr: number, length: number): number => { - let data = new Uint8Array( - (this.core!.exports.memory as WebAssembly.Memory).buffer, - addr, - length - ); - let n = 0; - while (this.#buffer!.length > 0 && n < length) { - data[n] = this.#buffer!.shift()!; - n += 1; + let input: string; + if (this.#buffer!.length <= length) { + input = this.#buffer!; + this.#buffer = ""; + } else { + const i = this.#buffer!.lastIndexOf("\n", length - 1); + input = this.#buffer!.substring(0, i + 1); + this.#buffer = this.#buffer!.substring(i + 1); } - return n; + // console.log("read: %s (%d remaining)", input, this.#buffer!.length); + saveString( + input, + this.core!.exports.memory as WebAssembly.Memory, + addr + ); + return input.length; }, key: () => { @@ -215,16 +220,16 @@ class WAForth { * Read data `s` into the input buffer without interpreting it. */ read(s: string) { - const data = new TextEncoder().encode(s); - for (let i = 0, len = data.length; i < len; ++i) { - this.#buffer!.push(data[i]); - } + this.#buffer = this.#buffer + s; } /** * Read data `s` into the input buffer, and interpret. */ interpret(s: string) { + if (!s.endsWith("\n")) { + s = s + "\n"; + } this.read(s); try { return (this.core!.exports.interpret as any)();