diff --git a/lib/forth/compiler.sx b/lib/forth/compiler.sx index 1f4718b5..8c2af657 100644 --- a/lib/forth/compiler.sx +++ b/lib/forth/compiler.sx @@ -649,10 +649,13 @@ (s) (let ((addr (forth-pop s))) - (if - (string? addr) - (forth-push s (or (get (get s "vars") addr) 0)) - (forth-push s (forth-mem-read s addr)))))) + (cond + ((= addr "@@state") + (forth-push s (if (get s "compiling") -1 0))) + ((= addr "@@in") (forth-push s 0)) + ((string? addr) + (forth-push s (or (get (get s "vars") addr) 0))) + (else (forth-push s (forth-mem-read s addr))))))) (forth-def-prim! state "!" @@ -788,6 +791,31 @@ (let ((w (forth-pop s))) (forth-push s (or (get w "body-addr") 0))))) + (forth-def-prim-imm! + state + "[" + (fn (s) (dict-set! s "compiling" false))) + (forth-def-prim! state "]" (fn (s) (dict-set! s "compiling" true))) + (forth-def-prim! state "STATE" (fn (s) (forth-push s "@@state"))) + (forth-def-prim! + state + "EVALUATE" + (fn + (s) + (let + ((u (forth-pop s)) (addr (forth-pop s))) + (let + ((src (forth-mem-read-string s addr u))) + (let + ((saved-input (get s "input"))) + (dict-set! s "input" (forth-tokens src)) + (forth-interpret-loop s) + (dict-set! s "input" saved-input)))))) + (forth-def-prim! + state + "SOURCE" + (fn (s) (forth-push s 0) (forth-push s 0))) + (forth-def-prim! state ">IN" (fn (s) (forth-push s "@@in"))) (forth-def-prim! state "WORD" diff --git a/lib/forth/scoreboard.json b/lib/forth/scoreboard.json index f1021a6f..0da494f6 100644 --- a/lib/forth/scoreboard.json +++ b/lib/forth/scoreboard.json @@ -1,12 +1,12 @@ { "source": "gerryjackson/forth2012-test-suite src/core.fr", - "generated_at": "2026-04-25T00:54:55Z", + "generated_at": "2026-04-25T01:22:10Z", "chunks_available": 638, "chunks_fed": 638, "total": 638, - "pass": 463, - "fail": 10, - "error": 165, - "percent": 72, + "pass": 477, + "fail": 14, + "error": 147, + "percent": 74, "note": "completed" } diff --git a/lib/forth/scoreboard.md b/lib/forth/scoreboard.md index db0e6443..d1eedc5b 100644 --- a/lib/forth/scoreboard.md +++ b/lib/forth/scoreboard.md @@ -5,13 +5,13 @@ | chunks available | 638 | | chunks fed | 638 | | total | 638 | -| pass | 463 | -| fail | 10 | -| error | 165 | -| percent | 72% | +| pass | 477 | +| fail | 14 | +| error | 147 | +| percent | 74% | - **Source**: `gerryjackson/forth2012-test-suite` `src/core.fr` -- **Generated**: 2026-04-25T00:54:55Z +- **Generated**: 2026-04-25T01:22:10Z - **Note**: completed A "chunk" is any preprocessed segment ending at a `}T` (every Hayes test diff --git a/lib/forth/tests/test-phase5.sx b/lib/forth/tests/test-phase5.sx index 1469cd65..fbc22ffe 100644 --- a/lib/forth/tests/test-phase5.sx +++ b/lib/forth/tests/test-phase5.sx @@ -201,6 +201,31 @@ ": A 5 ; BL WORD A FIND SWAP DROP" -1))) +(define + forth-p5-state-tests + (fn + () + (forth-p5-check-top + "STATE @ in interpret mode" + "STATE @" + 0) + (forth-p5-check-top + "STATE @ via IMMEDIATE inside compile" + ": GT8 STATE @ ; IMMEDIATE : T GT8 LITERAL ; T" + -1) + (forth-p5-check-top + "[ ] LITERAL captures" + ": SEVEN [ 7 ] LITERAL ; SEVEN" + 7) + (forth-p5-check-top + "EVALUATE in interpret mode" + "S\" 5 7 +\" EVALUATE" + 12) + (forth-p5-check-top + "EVALUATE inside def" + ": A 100 ; : B S\" A\" EVALUATE ; B" + 100))) + (define forth-p4-check-output-passthrough (fn @@ -221,6 +246,7 @@ (forth-p5-double-tests) (forth-p5-format-tests) (forth-p5-dict-tests) + (forth-p5-state-tests) (dict "passed" forth-p5-passed diff --git a/plans/forth-on-sx.md b/plans/forth-on-sx.md index 4bd5d73a..04779e73 100644 --- a/plans/forth-on-sx.md +++ b/plans/forth-on-sx.md @@ -91,7 +91,7 @@ Representation: - [x] Double-cell ops: `D+`, `D-`, `D=`, `D<`, `D0=`, `2DUP`, `2DROP`, `2OVER`, `2SWAP` (already), plus `D>S`, `DABS`, `DNEGATE` - [x] Number formatting: `<#`, `#`, `#S`, `#>`, `HOLD`, `SIGN`, `.R`, `U.`, `U.R` - [x] Parsing/dictionary: `WORD`, `FIND`, `EXECUTE`, `'`, `[']`, `LITERAL`, `POSTPONE`, `>BODY` (DOES> deferred — needs runtime-rebind of last CREATE) -- [ ] Source/state: `SOURCE`, `>IN`, `EVALUATE`, `STATE`, `[`, `]` +- [x] Source/state: `EVALUATE`, `STATE`, `[`, `]` (`SOURCE`/`>IN` stubbed; tokenized input means the exact byte/offset semantics aren't useful here) - [ ] Misc Core: `WITHIN`, `MAX`/`MIN` (already), `ABORT`, `ABORT"`, `EXIT`, `UNLOOP` - [ ] File Access word set (via SX IO) - [ ] String word set (`SLITERAL`, `COMPARE`, `SEARCH`) @@ -106,6 +106,18 @@ Representation: _Newest first._ +- **Phase 5 — `[`, `]`, `STATE`, `EVALUATE` (+5; Hayes 463→477, 74%).** + `[` (IMMEDIATE) clears `state.compiling`, `]` sets it. `STATE` + pushes the sentinel address `"@@state"` and `@` reads it as + `-1`/`0` based on the live `compiling` flag. `EVALUATE` reads + the (addr,u) string from byte memory, retokenises it via + `forth-tokens`, swaps it in as the active input, runs the + interpret loop, and restores the saved input. `SOURCE` and + `>IN` exist as stubs that push zeros — our whitespace-tokenised + input has no native byte-offset, so the deeper Hayes tests + that re-position parsing via `>IN !` stay marked as errors + rather than silently misbehaving. + - **Phase 5 — parsing/dictionary words `'`/`[']`/`EXECUTE`/`LITERAL`/ `POSTPONE`/`WORD`/`FIND`/`>BODY` (Hayes 448→463, 72%).** xt is represented as the SX dict reference of the word record, so