feat(v4.0.0): the defining and compiling words

The fourth layer of the compiler capsule.  compile.v4: INTERPRET's
loop, STATE [ ] : ; EXIT IMMEDIATE LITERAL COMPILE [COMPILE] ' EXECUTE,
CREATE VARIABLE CONSTANT DOES>, and IF ELSE THEN BEGIN UNTIL AGAIN
WHILE REPEAT DO ?DO LOOP +LOOP on a control-flow stack in memory.
forth.v4: the first words of the vocabulary, the in-line ones.

FORTH source now goes in and running code comes out.  49 programmes
were run through the v3 binary and through this; every result agrees,
bar COMPILE, which v3 cannot run.  The code laid down for each control
structure is word for word the expansion DECOMPOSITION.md gives.

A line may have six values on the stack while it is interpreted, and
words called from the interpreter may nest eight deep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-04 10:42:23 -04:00
co-authored by Claude Opus 5.5
parent bbd1b4047f
commit dac7ffac92
4 changed files with 850 additions and 5 deletions
+32 -5
View File
@@ -682,10 +682,10 @@ The storage service belongs to the Artemis role, now a device node.
| Word | Fate | | Word | Fate |
| --- | --- | | --- | --- |
| `FIND` | CC | `( -- xt \| 0 )`, FORTH-79 and v3 alike: the compilation address of the next word in the input stream, or 0 if it is not in the dictionary. `32 WORD` then a search from the newest entry back, skipping hidden ones; names are case sensitive, as v3, and 31 characters are significant (FORTH-79). Executed on the golden model's host node (2026-10-04) against a list kept in C, 300 entries. Leaves its caller 7 data cells and 5 return entries. | | `FIND` | CC | `( -- xt \| 0 )`, FORTH-79 and v3 alike: the compilation address of the next word in the input stream, or 0 if it is not in the dictionary. `32 WORD` then a search from the newest entry back, skipping hidden ones; names are case sensitive, as v3, and 31 characters are significant (FORTH-79). Executed on the golden model's host node (2026-10-04) against a list kept in C, 300 entries. Leaves its caller 7 data cells and 5 return entries. |
| `'` | CC | `( -- addr )`, FORTH-79: the parameter field address of the next word in the input stream; not found is an error (0, and `NODE-ERROR` set). v3's `'` returned what `FIND` returns. For a word that is code the two are the same address; for a data word the parameter field is the cell after its code, which is what FORTH-79's `n ' NAME !` needs. Its compiling behaviour comes with the compiler words. Executed on the golden model's host node (2026-10-04). | | `'` | CC | `( -- addr )`, FORTH-79: the parameter field address of the next word in the input stream; not found is an error (0, and `NODE-ERROR` set). v3's `'` returned what `FIND` returns. For a word that is code the two are the same address; for a data word the parameter field is the cell after its code, which is what FORTH-79's `n ' NAME !` needs. It is immediate: inside a definition the address is compiled as a literal (FORTH-79), where v3's `'` parsed when the definition ran. Executed on the golden model's host node (2026-10-04). |
| `>LINK` `LFA` `LINK>` `>NAME` `NFA` `NAME>` `CFA` `PFA` `>BODY` `TRAVERSE` | CC | The fields of an entry, all reached from its xt, as in v3 where the xt was the entry: `>LINK`/`LFA` the link's address, `LINK>` the older entry's xt, `>NAME`/`NFA` the byte address of the counted name, `NAME>` back to the xt, `CFA` the xt itself, `PFA`/`>BODY` the parameter field, `TRAVERSE` from a name's count byte to past its last character (for `n > 0`; otherwise unchanged, as v3). Executed on the golden model's host node (2026-10-04). | | `>LINK` `LFA` `LINK>` `>NAME` `NFA` `NAME>` `CFA` `PFA` `>BODY` `TRAVERSE` | CC | The fields of an entry, all reached from its xt, as in v3 where the xt was the entry: `>LINK`/`LFA` the link's address, `LINK>` the older entry's xt, `>NAME`/`NFA` the byte address of the counted name, `NAME>` back to the xt, `CFA` the xt itself, `PFA`/`>BODY` the parameter field, `TRAVERSE` from a name's count byte to past its last character (for `n > 0`; otherwise unchanged, as v3). Executed on the golden model's host node (2026-10-04). |
| `SMUDGE` `HIDDEN` | CC | `SMUDGE` toggles the hidden flag of the newest entry; `HIDDEN` sets it. v3 refuses both outside compilation; that check comes with the compiler words. Executed on the golden model's host node (2026-10-04). | | `SMUDGE` `HIDDEN` | CC | `SMUDGE` toggles the hidden flag of the newest entry; `HIDDEN` sets it. v3 refuses both outside compilation; that check comes with the compiler words. Executed on the golden model's host node (2026-10-04). |
| `INTERPRET` | CC | | | `INTERPRET` | CC | FORTH-79; source in `v4/capsule/compile.v4`. Takes words from the input stream until it is exhausted: a word in the dictionary is executed, or, when compiling and not immediate, compiled; anything else must be a number in the current `BASE` (a single cell, with an optional leading minus), left on the stack or compiled as a literal. What is neither, or a compile-only word met while interpreting, abandons the line: compiling stops, the definition under way stays hidden, the rest of the input is skipped and `NODE-ERROR` is set (the message and the stack reset come with `QUIT`). Executed on the golden model's host node (2026-10-04). |
An entry, as `v4/capsule/dict.v4` builds it. A word's execution address (xt) is the address of its An entry, as `v4/capsule/dict.v4` builds it. A word's execution address (xt) is the address of its
code, and everything else is found from it: code, and everything else is found from it:
@@ -713,7 +713,7 @@ it, `xt + 1`; for any other word the parameter field is the code itself.
| Word | Fate | Notes | | Word | Fate | Notes |
| --- | --- | --- | | --- | --- | --- |
| `(` `\` | CC | `(` is `41 WORD drop`; `\` is `SPAN @ >IN !`. In `v4/capsule/input.v4` under the names `PAREN` and `BACKSLASH`. Executed on the golden model's host node (2026-10-04). | | `(` `\` | CC | `(` is `41 WORD drop`; `\` is `SPAN @ >IN !`. In `v4/capsule/input.v4` under the names `PAREN` and `BACKSLASH`. Executed on the golden model's host node (2026-10-04). |
| `EXECUTE` | CAP | `push ;` (tail-jumps to the xt; the xt returns to `EXECUTE`'s caller) | | `EXECUTE` | CAP | `push ;` (tail-jumps to the xt; the xt returns to `EXECUTE`'s caller). Executed on the golden model's host node (2026-10-04). |
| `NOP` | OP | `nop` | | `NOP` | OP | `nop` |
| `QUIT` `ABORT` `ABORT"` `(ABORT")` `COLD` `WARM` | CC | | | `QUIT` `ABORT` `ABORT"` `(ABORT")` `COLD` `WARM` | CC | |
| `BYE` `REBOOT` | HERA | | | `BYE` `REBOOT` | HERA | |
@@ -732,10 +732,35 @@ it, `xt + 1`; for any other word the parameter field is the code itself.
| Word | Fate | Notes | | Word | Fate | Notes |
| --- | --- | --- | | --- | --- | --- |
| `:` `;` `CREATE` `VARIABLE` `CONSTANT` `DOES>` `IMMEDIATE` `STATE` `[` `]` `LITERAL` `COMPILE` `[COMPILE]` `FORGET` `FENCE` | CC | | | `:` `;` `EXIT` `IMMEDIATE` `STATE` `[` `]` `LITERAL` `COMPILE` `[COMPILE]` | CC | Source in `v4/capsule/compile.v4`, all to FORTH-79. `:` makes an entry and starts compiling; the word cannot be found until `;`, so a redefinition can use the old word. `;` lays the return, reveals the word and stops compiling. `LITERAL` compiles the number on the stack when compiling. `COMPILE xxx` lays code that compiles `xxx` when the word it is in runs; `[COMPILE] xxx` compiles `xxx` though it is immediate. Executed on the golden model's host node (2026-10-04) against results recorded from the v3 binary; v3's `COMPILE` does not work (`: C1 COMPILE DUP ; IMMEDIATE : U3 C1 + ;` gives `DUP: Stack underflow`), reported, not fixed. |
| `CREATE` `VARIABLE` `CONSTANT` `DOES>` | CC | FORTH-79. A data word's code is one instruction word, a call to its run-time routine, and its parameter field is the cell after it: `(DOVAR)` is `pop ;` (the call's return address is the parameter field's address) and `(DOCON)` is `pop a! @ ;`. `VARIABLE` allots one cell, set to 0. `DOES>` lays a call to `(DOES)` and then `pop`: `(DOES)`, run by the defining word, rewrites the newest entry's code into a call to the code after it and returns from the defining word; that code's `pop` fetches the parameter field address. Executed on the golden model's host node (2026-10-04) against results recorded from the v3 binary. |
| `FORGET` `FENCE` | CC | |
| `LIT` | OP | `@p` | | `LIT` | OP | `@p` |
| `does_rt` | RET | Internal helper; `DOES>` is implemented by the compiler capsule. | | `does_rt` | RET | Internal helper; `DOES>` is implemented by the compiler capsule. |
**How a word is compiled.** v4 code is native: a colon definition is instruction words, and
compiling a word into one lays down a call to it. Two flags on an entry change that. An **in-line**
word's code is a straight run of opcodes and literals ending in `;`, and that run is copied in place
of a call; this is fate IN (§0), and everything that touches the return stack must be in line, since a
call would bury what it works on. A **compile-only** word may not be executed by the interpreter.
`v4/capsule/forth.v4` holds the first words of the vocabulary flagged this way (`DUP`, `+`, `>R`, `I`,
`LEAVE`, `@`, the in-line variables and so on).
**The short stacks (D-2) and the compiler.** The interpreter and compiler run on the same ten-cell and
nine-entry stacks as the user's words, with the user's values beneath them. Two consequences are
built into the capsule:
- Every word of the capsule keeps what it works on in memory and has at most three cells of its own on
the data stack. Measured on the golden model: a line may have **6 values on the stack** while the
interpreter reads its next word, and defining and running a word leaves 4 cells under it untouched.
- Words called from the interpreter may nest **8 deep**; a `DO` loop takes two return entries, so two
nested loops leave four. `*` is therefore not `UM* drop` on the host node but N steps of `+*` with
only the count on the return stack; it gives the same low cell (what `+*` loses at the top of `T`
takes N shifts to reach the bit that moves into `A`, and the loop ends first).
A programme that v3 runs with more than that on its stacks does not run here; that is D-2, not the
compiler.
**The code generator** (`v4/capsule/codegen.v4`) is what these words are built on. It packs opcodes, **The code generator** (`v4/capsule/codegen.v4`) is what these words are built on. It packs opcodes,
literals and branches into instruction words at `HERE`, by the rules of §1.2 and §2: slots filled left literals and branches into instruction words at `HERE`, by the rules of §1.2 and §2: slots filled left
to right, unused slots `nop`, a literal's value in the cell after its word, `;` `ex` and any branch to right, unused slots `nop`, a literal's value in the cell after its word, `;` `ex` and any branch
@@ -761,7 +786,9 @@ lays down was then run.
| Word | Fate | v4 definition / notes | | Word | Fate | v4 definition / notes |
| --- | --- | --- | | --- | --- | --- |
| `IF` `ELSE` `THEN` `BEGIN` `UNTIL` `AGAIN` `WHILE` `REPEAT` `DO` `?DO` `LOOP` `+LOOP` `LEAVE` `CASE` `OF` `ENDOF` `ENDCASE` | CC | Compile-time structure words. Expansions per §2 and below. | | `IF` `ELSE` `THEN` `BEGIN` `UNTIL` `AGAIN` `WHILE` `REPEAT` `DO` `?DO` `LOOP` `+LOOP` | CC | Compile-time structure words, immediate and compile-only; source in `v4/capsule/compile.v4`. Expansions per §2 and below. What an opening word leaves for the word that closes it goes on a **control-flow stack in memory** (32 cells), not on the data stack, with a number saying which word left it; a closing word that finds the wrong number, or nothing, abandons the line. A loop that tests at its end (`UNTIL`) must drop the flag on both ways out, so `BEGIN` lays `jump L0 Ld: drop L0:` and `UNTIL` lays `if Ld drop`. Executed on the golden model's host node (2026-10-04): the code laid down for each is word for word what the text assembler makes of the expansion given here, and the programmes run give what the v3 binary gave. |
| `LEAVE` `I` `J` `UNLOOP` | CC | In-line words (below), compile-only. |
| `CASE` `OF` `ENDOF` `ENDCASE` | CC | |
| `EXIT` | OP | `;` | | `EXIT` | OP | `;` |
| `(BRANCH)` | OP | `jump` | | `(BRANCH)` | OP | `jump` |
| `(0BRANCH)` | IN | `if L … drop` (§2) — executed on the golden model (2026-10-03) as `IF 1 ELSE 2 THEN`, including results recorded from the v3 binary. | | `(0BRANCH)` | IN | `if L … drop` (§2) — executed on the golden model (2026-10-03) as `IF 1 ELSE 2 THEN`, including results recorded from the v3 binary. |
File diff suppressed because it is too large Load Diff
+72
View File
@@ -0,0 +1,72 @@
\ forth.v4 -- the first words of the FORTH vocabulary: the ones that are
\ opcodes or short runs of them, and the variables that are in-line addresses.
\
\ Each is the definition DECOMPOSITION.md gives (sections 4, 5.1 - 5.5, 5.18).
\ A word flagged inline is copied into a definition in place of a call; the
\ ones that touch the return stack are compile-only as well. Rests on core.v4
\ and compile.v4's loader constants.
\ ---- stack ----
header DUP inline : DUP dup ;
header DROP inline : DROP drop ;
header OVER inline : OVER over ;
header SWAP inline : SWAP' SWAP ;
header ROT inline : ROT' ROT ;
header -ROT inline : -ROT SWAP push SWAP pop ;
header ?DUP : ?DUP if Z dup ; Z: ;
header 2DUP inline : 2DUP over over ;
header 2DROP inline : 2DROP drop drop ;
\ ---- return stack: in line, and only inside a definition ----
header >R inline compile-only : >R push ;
header R> inline compile-only : R> pop ;
header R@ inline compile-only : R@ pop dup push ;
header I inline compile-only : I pop dup push ;
header J inline compile-only : J pop pop pop dup push a! push push a ;
header LEAVE inline compile-only : LEAVE pop pop drop dup push push ;
header UNLOOP inline compile-only : UNLOOP pop drop pop drop ;
\ ---- memory ----
header @ inline : FETCH a! @ ;
header ! inline : STORE a! ! ;
header +! inline : +! a! @ + ! ;
header CELLS inline : CELLS ; \ an address unit is a cell (D-1)
\ ---- arithmetic ----
header + inline : PLUS + ;
header - inline : MINUS - ;
\ The low cell of the product, by N steps of +* with nothing but the count on
\ the return stack. UM* drop gives the same cell but needs two more return
\ entries, which a word inside two DO loops does not have (D-2). The low
\ cell is exact: what +* loses at the top of T (D-3) takes N shifts to reach
\ the bit that moves into A, and the loop ends first.
header * : STAR a! 0 N-1 FOR +* UNEXT drop drop a ;
header NEGATE inline : NEGATE' NEGATE ;
header 1+ inline : 1+ 1 + ;
header 1- inline : 1- -1 + ;
header 2+ inline : 2+ 2 + ;
header 2- inline : 2- -2 + ;
header 2* inline : TWO* 2* ;
header 2/ inline : TWO/ 2/ ;
\ ---- logic and comparison ----
header AND inline : AND and ;
header XOR inline : XOR xor ;
header OR inline : OR' OR ;
header 0= : 0= if L1 drop 0 ; L1: drop -1 ;
header 0< : 0< -if L1 drop -1 ; L1: drop 0 ;
header NOT : NOT jump 0=
header = : EQUALS xor jump 0=
header < : LESS
over over xor -if SAME drop drop jump 0<
SAME: drop - jump 0<
header > : GREATER SWAP jump LESS
\ ---- in-line addresses and constants ----
header BL inline : BL' BL ;
header TIB inline : TIB' TIB ;
header >IN inline : >IN' >IN ;
header SPAN inline : SPAN' SPAN ;
header BASE inline : BASE' BASE ;
header PAD inline : PAD' PAD ;
header STATE inline : STATE' STATE ;
File diff suppressed because it is too large Load Diff