feat(v4.0.0): every error is raised and ends the line (D-18)

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>
This commit is contained in:
rajames
2026-10-04 20:13:23 -04:00
co-authored by Claude Opus 5.5
parent 644bfc0a25
commit dc7e37ba5c
12 changed files with 181 additions and 38 deletions
+12 -10
View File
@@ -35,6 +35,7 @@
\ (C)+5 what a loop's end lays after its test
\ (C)+6 (C)+7 a branch and a tag a control word is holding
\ (C)+8 whether a data word's entry was made
\ (C)+9 the error code a line is being abandoned with
\ (CFP) word address of the control-flow stack's pointer
\ CFBASE CFEND word addresses of its first cell and of the cell past its last
@@ -47,11 +48,15 @@ macro ROT push SWAP pop SWAP endmacro
\ NODE-ERROR. A definition that was under way stays hidden. It returns to
\ whatever called INTERPRET, which is QUIT (quit.v4): QUIT sees NODE-ERROR
\ and prints ERROR.
: (ABANDON)
\ (ABANDON-AS) is the same with an error code (core.v4) for the prompt to
\ print the message of.
: (ABANDON-AS) ( code -- )
(C)+9 a! !
0 STATE a! ! (CG-RESET)
CFBASE (CFP) a! !
SPAN a! @ >IN a! !
NODE-ERROR b! -1 !b ;
(C)+9 a! @ NODE-ERROR b! !b ;
: (ABANDON) -1 jump (ABANDON-AS)
\ ---- the control-flow stack ----------------------------------------------------
@@ -59,7 +64,7 @@ macro ROT push SWAP pop SWAP endmacro
: >CF
(CFP) a! @ CFEND xor if FULL
drop (CFP) a! @ dup 1 + ! a! ! ;
FULL: drop drop jump (ABANDON)
FULL: drop drop 8 jump (ABANDON-AS) \ control structures too deep
\ ( -- x ) pop; 0 from an empty stack, which no opening word leaves
: CF>
@@ -153,10 +158,7 @@ header INTERPRET
drop
STATE a! @ if KEEP drop (LIT,) jump L
KEEP: drop jump L
BAD: drop drop \ "UNKNOWN WORD: '"
$4E4B4E55 (EMIT4) $204E574F (EMIT4) $44524F57 (EMIT4) $27203A (EMIT4)
(.WORD) 39 EMIT CR
jump (ABANDON)
BAD: drop drop (UNKNOWN) drop jump (ABANDON)
EOL: drop drop ;
\ ---- state ---------------------------------------------------------------------
@@ -206,13 +208,13 @@ header COMPILE immediate compile-only
: COMPILE
32 WORD (LOOKUP) if MISSING
(LIT,) &(COMPILE,) jump (CALL,)
MISSING: drop jump (ABANDON)
MISSING: drop (UNKNOWN) drop jump (ABANDON)
\ [COMPILE] xxx FORTH-79: compile xxx even though it is immediate
header [COMPILE] immediate compile-only
: BRACKET-COMPILE
32 WORD (LOOKUP) if MISSING jump (COMPILE,)
MISSING: drop jump (ABANDON)
MISSING: drop (UNKNOWN) drop jump (ABANDON)
\ ---- data words ---------------------------------------------------------------------
\ A data word's code is one call to its run-time routine; the call leaves the
@@ -267,7 +269,7 @@ header DOES> immediate compile-only
\ ( tag wanted -- ) if they differ, abandon the line and leave the control
\ word that called this as well: its return address is dropped, so the
\ abandoning returns to the interpreter and nothing more is laid down.
: (PAIR) xor if OK drop pop drop jump (ABANDON) OK: drop ;
: (PAIR) xor if OK drop pop drop 7 jump (ABANDON-AS) OK: drop ;
\ ( ref -- ) the branch at ref lands here
: (HERE!) (FLUSH) HERE SWAP jump (RESOLVE)
+11 -2
View File
@@ -6,6 +6,15 @@
\
\ Constants the loader supplies: N-1 (cell bits - 1), NODE-ERROR, CONSOLE-TX,
\ CONSOLE-RX, CONSOLE-STATUS, BASE.
\
\ ERRORS (D-18). A word that finds an error takes its arguments off the stack
\ and stores a code in NODE-ERROR. On a node with a prompt that store is a
\ trap: the word goes no further, and the prompt prints the message for the
\ code and ERROR (quit.v4, (RAISED)). The codes:
\ 1 Negative count 2 Not a number 3 Number too long
\ 4 Not a character 5 Dictionary full 6 Name missing
\ 7 Control structure mismatch 8 Control structures too deep
\ -1 the word has printed its own message
macro SWAP over push push drop pop pop endmacro
macro - push inv pop + inv endmacro \ x y -- x-y
@@ -72,7 +81,7 @@ header C!
\ ---- section 5.9: strings ------------------------------------------------
header CMOVE
: CMOVE ( src dst u -- )
-if OK drop drop drop NODE-ERROR b! -1 !b ;
-if OK drop drop drop NODE-ERROR b! 1 !b ;
OK: if DONE push
over C@ over C! 1 + push 1 + pop pop -1 + jump OK
DONE: drop drop drop ;
@@ -93,7 +102,7 @@ header COUNT
: COUNT ( baddr -- baddr+1 c ) dup C@ push 1 + pop ;
header TYPE
: TYPE ( baddr u -- )
-if OK drop drop NODE-ERROR b! -1 !b ;
-if OK drop drop NODE-ERROR b! 1 !b ;
OK: if DONE over C@ EMIT push 1 + pop -1 + jump OK
DONE: drop drop ;
+11 -4
View File
@@ -41,7 +41,7 @@ macro OR over inv and xor endmacro
\ they are storing in A while the space is taken: nothing of theirs is then
\ on the data stack under (DP+), which itself needs three cells (D-2).
: (DERR) ( -- 0 ) NODE-ERROR b! -1 !b 0 ;
: (DERR) ( -- 0 ) NODE-ERROR b! 5 !b 0 ; \ dictionary full
\ ( n -- flag ) move DP by n bytes if it stays inside the dictionary space
\ and say -1; otherwise leave it, set NODE-ERROR and say 0. The two
@@ -66,7 +66,7 @@ header ,
a!
DP b! @b 3 + -4 and \ the next whole cell, as a byte address
dup inv DLIMIT + -3 + -if ROOM \ DLIMIT - (it + 4)
drop drop NODE-ERROR b! -1 !b ;
drop drop NODE-ERROR b! 5 !b ;
ROOM: drop dup 4 + !b \ DP is past it
4/ b! a !b ;
header C,
@@ -148,7 +148,7 @@ header HIDDEN
0 , (D) a! @ , LATEST ,
HERE (LATEST) a! ! ;
FULL: drop ; \ (DP+) has set NODE-ERROR
EMPTY: drop NODE-ERROR b! -1 !b ;
EMPTY: drop NODE-ERROR b! 6 !b ; \ name missing
\ ---- looking a name up -----------------------------------------------------
@@ -186,5 +186,12 @@ header FIND
\ ( -- addr ) FORTH-79: the parameter field address of the next word in the
\ input stream. Not found is an error: 0, and NODE-ERROR is set. This is '
\ when interpreting; compile.v4 adds what ' does when compiling.
\ ( -- 0 ) say that the word WORD has just left is not known, as v3 does:
\ UNKNOWN WORD: 'xxx'
: (UNKNOWN)
$4E4B4E55 (EMIT4) $204E574F (EMIT4) $44524F57 (EMIT4) $27203A (EMIT4)
WBUF COUNT TYPE 39 EMIT CR
NODE-ERROR b! -1 !b 0 ;
: (') 32 WORD (LOOKUP) if MISSING jump PFA
MISSING: drop jump (DERR)
MISSING: drop jump (UNKNOWN)
+1 -1
View File
@@ -183,7 +183,7 @@ header CONVERT
header NUMBER
: NUMBER
(NUMBER?) if BAD drop ;
BAD: drop NODE-ERROR b! -1 !b ;
BAD: drop NODE-ERROR b! 2 !b ;
\ ---- comments (5.15) -------------------------------------------------------
\ PAREN skips to the closing parenthesis; BACKSLASH skips the rest of the line.
+3 -3
View File
@@ -43,9 +43,9 @@ header <#
: <# ( -- ) HEND HLD b! !b ;
header HOLD
: HOLD ( c -- )
dup -256 and if OKC drop jump ERR
OKC: drop HLD b! @b -HFLOOR + -if ROOM drop
ERR: drop NODE-ERROR b! -1 !b ;
dup -256 and if OKC drop drop NODE-ERROR b! 4 !b ; \ not a character
OKC: drop HLD b! @b -HFLOOR + -if ROOM
drop drop NODE-ERROR b! 3 !b ; \ the buffer is full
ROOM: drop HLD b! @b -1 + dup !b jump C!
header SIGN
: SIGN ( n -- ) -if L1 drop 45 jump HOLD L1: drop ;
+21 -2
View File
@@ -87,11 +87,30 @@ header ABORT
$75746552 (EMIT4) $73206E72 (EMIT4) $6B636174 (EMIT4) $646E7520 (EMIT4) $6C667265 (EMIT4) $776F (EMIT4) CR
2 jump (REPL)
\ The table the loader gives the node: five words, one for each kind of
\ A word has stored an error code in NODE-ERROR (D-18; the codes are listed
\ in core.v4). Print its message -- a negative code means the word printed
\ its own -- and end the line with ERROR. The return stack has been
\ emptied; the data stack is as the word left it.
: (RAISED)
NODE-ERROR b! @b
-if POS drop 2 jump (REPL)
POS: -1 + if M1 -1 + if M2 -1 + if M3 -1 + if M4
-1 + if M5 -1 + if M6 -1 + if M7 -1 + if M8
drop 2 jump (REPL)
M1: drop $6167654E (EMIT4) $65766974 (EMIT4) $756F6320 (EMIT4) $746E (EMIT4) CR 2 jump (REPL)
M2: drop $20746F4E (EMIT4) $756E2061 (EMIT4) $7265626D (EMIT4) CR 2 jump (REPL)
M3: drop $626D754E (EMIT4) $74207265 (EMIT4) $6C206F6F (EMIT4) $676E6F (EMIT4) CR 2 jump (REPL)
M4: drop $20746F4E (EMIT4) $68632061 (EMIT4) $63617261 (EMIT4) $726574 (EMIT4) CR 2 jump (REPL)
M5: drop $74636944 (EMIT4) $616E6F69 (EMIT4) $66207972 (EMIT4) $6C6C75 (EMIT4) CR 2 jump (REPL)
M6: drop $656D614E (EMIT4) $73696D20 (EMIT4) $676E6973 (EMIT4) CR 2 jump (REPL)
M7: drop $746E6F43 (EMIT4) $206C6F72 (EMIT4) $75727473 (EMIT4) $72757463 (EMIT4) $696D2065 (EMIT4) $74616D73 (EMIT4) $6863 (EMIT4) CR 2 jump (REPL)
M8: drop $746E6F43 (EMIT4) $206C6F72 (EMIT4) $75727473 (EMIT4) $72757463 (EMIT4) $74207365 (EMIT4) $64206F6F (EMIT4) $706565 (EMIT4) CR 2 jump (REPL)
\ The table the loader gives the node: six words, one for each kind of
\ fault in the node's order, each a jump. A jump fills its word, so they
\ are one after another.
: (FAULTS)
jump (FAULT) jump (D-OVER) jump (D-UNDER) jump (R-OVER) jump (R-UNDER)
jump (FAULT) jump (D-OVER) jump (D-UNDER) jump (R-OVER) jump (R-UNDER) jump (RAISED)
\ ---- text in the source ---------------------------------------------------------
\ ." and ABORT" take the text up to the next " , which may be none at all