ENGINE.md 3b, the node's side of ruling A (a word's code is the node's, its
accounts the kernel's).
- dict.v4, system.v4: (WORD-DEFINED) ( xt -- ) is run when an entry is
made, (WORD-FORGOTTEN) ( w -- ) when FORGET or COLD removes entries; with
0 there no one is told, as on the hosted product
- test_host_quit.c: a kernel that keeps the list of words and is checked to
hold exactly the node's dictionary after definitions, a vocabulary, an
abandoned definition, FORGET, a refused FORGET and COLD; KERNEL-WORD
called from the prompt and from a definition
Fixed, found while writing that test: since the capsules moved from build
time to boot time (294e6946), what COLD returns to and FORGET protects was
still the nucleus alone, so COLD lost U*, U/MOD and BYE and FORGET U* was
allowed. The boot now seals the system when it has loaded it
(v4_image_seal), and hosted-check checks COLD, the capsule word after it,
the refused FORGET and BYE.
Verified: make -C v4 test passes at both widths (1283 checks in
test_host_quit.c); hosted-check passes on three ISAs; clean qemu with
STARFORTH_V4=1 on amd64, aarch64 and riscv64 passes POST, and COLD, U*
after it, FORGET U* (refused), an unserved kernel word and BYE typed at
each prompt are answered correctly (logs/20261005-193045, -193307, -193636).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- VOCABULARY DEFINITIONS CONTEXT CURRENT FORTH, and v3's ORDER. A name is
looked up in the CONTEXT vocabulary and then in FORTH; a new entry goes
into the CURRENT vocabulary; : makes the CURRENT vocabulary CONTEXT;
FORTH is immediate.
- WORDS lists the CONTEXT vocabulary. FORGET takes what was defined later
out of every vocabulary, and a vocabulary that goes gives way to FORTH.
- v3's vocabularies separate nothing: a word defined in one is found from
every other, and one redefined in a vocabulary replaces FORTH's for good.
- tests/test_host_quit.c: isolation, chaining to FORTH, two vocabularies
with the same names, FORGET across them; and DUMP is now checked to put
BASE back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ruled 2026-10-04: guard all errors. The errors that set NODE-ERROR and
let the line run on now stop it at once, with a message.
- A store of a non-zero code to NODE-ERROR is a trap, a sixth kind of
fault: nothing after it executes, the return stack is emptied, and the
data stack is left as the word left it. Not attached, NODE-ERROR is
plain memory, as the tests below the prompt use it.
- The capsule's words store a code where they stored -1, and the prompt's
(RAISED) prints its message: Negative count, Not a number, Number too
long, Not a character, Dictionary full, Name missing, Control structure
mismatch, Control structures too deep.
- ' and COMPILE and [COMPILE] of a word that is not there say
UNKNOWN WORD: 'xxx', as the interpreter does.
- tests: the trap in test_exec.c; every message from the prompt, with the
rest of the line not run and the stack kept, in test_host_quit.c.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured, compiling a DO loop left one return entry spare under
INTERPRET and a defining word called from inside another word
overflowed the nine-entry return stack (D-2); the prompt loop will take
one more.
C@ and C! shift without a loop. The code generator shifts each opcode
into the word being built, so nothing is placed by a counted shift, and
the address masks come from a table. The dictionary search, the
in-liner, the end of a loop and the making of a data word are each one
word reached by a jump.
Every kind of line now leaves at least three return entries spare while
it compiles; a word run from the interpreter has six; and CREATE ...
DOES> works from the prompt, from a word, and from a word that calls
that.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WORD, the dictionary words and the code generator run underneath
whatever the user has on the stacks, which are ten and nine deep (D-2).
Measured, they used five to seven data cells of their own, leaving a
line about three. Each now keeps what it works on in its file's
scratch cells and has at most three cells on the data stack; , calls
nothing; and the longest chains of calls are shorter.
The public words of core.v4, input.v4 and dict.v4 get dictionary
headers. NUMBER is split so the interpreter can have a flag instead of
NODE-ERROR. The host-node tests share one memory map, host_map.h.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second layer of the compiler capsule, v4/capsule/dict.v4: HERE ALIGN
ALLOT , C, 2, PAD LATEST; an entry layout reached entirely from the xt,
with >LINK LFA LINK> >NAME NFA NAME> CFA PFA >BODY TRAVERSE SMUDGE
HIDDEN on it; and FIND and ' .
FIND is FORTH-79's and v3's. ' is FORTH-79's: the parameter field
address, which for a code word is what FIND gives. ALLOT counts cells
(D-1). 31 characters of a name are significant.
Executed at both cell widths against a list of 300 entries kept in C.
WORD no longer holds its length on the return stack.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>