Recently on forth-standard.org, Jim Peterson asked whether the
standards should eventually adopt a new interface ?: to make it easier
to conditionally provide a Forth implementation of word, but only if
the word does not already have a (likely faster) implementation:

https://forth-standard.org/standard/core/Colon#contribution-397

I see gforth added ?: in November 2024, but with an implementation
that merely scans for the next word that is exactly ";", which is no
better than [IF] (really [ELSE]) scanning for [THEN] - it is easily
confused by things such as:

?: 2DROP \ comment? ; or demo of the problem
  DROP DROP ;

which causes a stack underflow on gforth (because gforth has 2DROP, so
it started scanning; the scan skips \ as if it were a normal word, so
the text after \ is not a comment, and once the bare ";" in the
supposed comment ends the scan, the next word "or" is interpreted with
nothing on the stack).  Sure, you'd never write replacement code that
horrible, but the point is that when you do blind scanning, you CAN'T
recognize which words are scanning words, and thus your blind scan is
likely to parse differently than if you were letting Forth interpret
the line.

Jim's post had an interesting idea - he switched to a version that
tried to repurpose the definition as :NONAME and then rewind the
dictionary; I disagreed with his approach of using ALLOT to roll back
HERE as insufficient for rewinding other state (it may work in a
trivial implementation), and suggested that he use MARKER instead.
But his approach had a cool effect:

In gforth:

?: oops if ;
*the terminal*:1:11: error: Control structure mismatch
...
?: b if ;  ok

Note that by scanning until ; without actually compiling, you have
lost all ability to identify blatant problems in the replacement
definition.  But with Jim's approach, even if the word 'b' is already
defined, the fact that he still parsed the rest of the definition (and
then taught ; to undo the definition) means that all words between ?:
and ; are processed the same whether the word ends up being needed or
not, so that "?: b if ;" properly identifies Control structure
mismatch in the attempted replacement rather than being a no-op merely
because "b" happens to already have a definition.

But Jim also pointed out that his idea of redirecting things to a
:NONAME required special handling of IMMEDIATE; since the decision
point of whether to discard the :NONAME happens before the spot where
IMMEDIATE can normally appear.  That is, his initial proposal had a
fragile workaround so that:

?: RDROP POSTPONE R> POSTPONE DROP ; IMMEDIATE

would properly make RDROP immediate on a platform where rdrop was not
pre-defined, but would ignore the IMMEDIATE on platforms where RDROP
already exists and where you really don't want to mark the previous
definition before the ?: as immediate (since :NONAME ... ; IMMEDIATE
is ambiguous per the standard).  Inspired by that, and with help from
ruv, I proposed adding [IMMEDIATE] as a way to syntactically avoid the
problem, by allowing this instead:

?: RDROP [IMMEDIATE] POSTPONE R> POSTPONE DROP ;

so that you are once again back to the situation of only caring about
where the ; lives after the ?:

https://theforth.net/package/immediate

Would gforth be interested in adding [IMMEDIATE] as an extension word?

Then Jim went on to demo that someone might want to do:

?: -ROT ( a b c -- c a b ) -ROT -ROT ;

Did you spot the problem?  He meant to use "ROT ROT" as the
replacement; but his typo goes unchecked on a platform where -ROT is
already defined; and only causes an Undefined word exception on the
very platforms where his replacement matters.

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!

However, there's one thing I can't figure out, and that's how to silence:

*terminal*:15:4: warning: redefined a
*terminal*:13:4: warning: original location
*terminal*:15:19: warning: redefined a
*terminal*:15:4: warning: original location ok


Reply via email to