On Wed, Aug 13, 2025 at 07:56:17AM -0500, Eric Blake wrote:
> So, observe what happens when I do this in gforth:
>
> : nop ;
> defer cleanup
> ' nop ' cleanup defer!
> : unknown-word -13 throw ;
> : ?:
> >in @ >r ['] parse-name perform find-name 0=
> if r> >in ! : exit then
> noname marker latestxt ['] cleanup defer! \ install marker
> r@ >in ! : postpone unknown-word postpone ; immediate \ hide existing word
> r> >in ! : \ proceed to parse as normal, ; will clean up
> ;
> : ;
> postpone ;
> cleanup \ undo ?: if needed, otherwise a nop
> ['] nop ['] cleanup defer! \ restore nop cleanup
> ; immediate
>
> Now I can do:
>
> ?: a ." in a, v1" ;
> ?: a ." in a, v2" ;
> a
> => in a, v1
>
> and more usefully, if I typo:
>
> ?: -rot -rot -rot ;
> *the terminal*:17:9: error: Undefined word
> ?: -rot >>>-rot<<< -rot ?;
>
> the Forth compiler tells me about my problem!
One slight improvement. If there is an error before ;, then the
temporary disabled version of the macro will remain in force (use "see
-rot" to see what I mean). Better might be to have ?: use catch and
kick off a nested interpreter loop that is guaranteed to end via a
throw (whether a premature throw because the colon definition is
buggy, or an intended throw during the cleanup used by ;), so that the
marker is always invoked whether or not ; was reached.
: nop ;
defer cleanup
' nop ' cleanup defer!
: unknown-word -13 throw ;
: ?:
>in @ >r ['] parse-name perform find-name 0=
if r> >in ! : exit then
noname marker latestxt \ create marker
\ set cleanup to throw a witness value if reached by ;
[: ['] cleanup throw ;] ['] cleanup defer!
r@ >in ! : postpone unknown-word postpone ; immediate \ hide existing word
r> >in ! >r ( r: markerxt ) \ prepare for throwaway parse
\ NOTE: the quotation below should probably refill as needed...
[: : interpret ;] catch \ run nested interpreter on throwaway word
r> execute \ invoke the marker
['] nop ['] cleanup defer! \ reset cleanup
dup ['] cleanup <> and throw \ rethrow anything except success
;
: ; postpone ; cleanup ; immediate
Fixing this to auto-refill (so that ?: to ; can extend across lines,
even when entered interactively) is an exercise for the reader; my
point here was adding in the nested interpreter to guarantee cleanup
whether or not the ; is reached.
With that amended definition:
?: -rot -rot -rot ;
*terminal*:1:4: warning: redefined -rot
prim:1670:1: warning: original location
*the terminal*:1:9: error: Undefined word
?: -rot >>>-rot<<< -rot ;
: a ." in a v1" ; ok
?: a ." in a v2" ;
*terminal*:3:4: warning: redefined a
*terminal*:2:3: warning: original location
*terminal*:3:18: warning: redefined a
*terminal*:3:4: warning: original location ok
a in a v1 ok
and "see -rot" is still fine.
HOWEVER, I think I hit a gforth bug:
?: a if ;
*terminal*:6:4: warning: redefined a
*terminal*:2:3: warning: original location
*the terminal*:6:9: error: Control structure mismatch
?: a if >>>;<<<
: c ;
*the terminal*:7:1: error: Cannot allocate memory
>>>:<<< c ;
Backtrace:
kernel/stringk.fs:75:24: 0 $7F703F61B248 throw
kernel/stringk.fs:90:28: 1 $7F703F61B488 $!len
2 $7F703F5FE068
kernel/stringk.fs:136:14: 3 $7F703F61B9D8 $+!len
kernel/stringk.fs:146:28: 4 $7F703F61BB08 $room
kernel/stringk.fs:148:25: 5 $7F703F6123A0 $bit
kernel/comp.fs:840:9: 6 $7F703F615F88 flush-code
kernel/comp.fs:843:5: 7 $7F703F616018 :start
"Control structure mismatch" occurring while a colon definition is
incomplete, followed by calling a word defined by marker, appears to
hose state so that it is no longer possible to define any new words.
I can't figure out why that fails, while "Undefined word" passed,
other than to guess that the control stack is not being entirely
cleaned up as part of the throw/catch handshake.
So in addition to my earlier worry about how to silence redefinition
warnings, I'm now wondering why gforth can't safely use a marker after
certain parse errors.