Digest #307 2025-08-10

Contributions

[398] 2025-08-09 15:54:10 JimPeterson wrote:

referenceImplementation - Possible Reference Implementation

I suggest the following as a reference implementation:

: _cmp ( x1 x2 -- -1|0|1 ) - DUP IF 0< 1 OR THEN ;

: COMPARE ( addr1 u1 addr2 u2 -- -1|0|1 )
  ROT 2DUP SWAP _cmp >R
  MIN ?DUP IF
    0 DO
      COUNT >R SWAP COUNT R> _cmp ?DUP IF
        NIP NIP UNLOOP R> DROP EXIT
      THEN
      SWAP
    LOOP
  THEN
  2DROP
  R>
;

Replies

[r1485] 2025-08-08 14:00:22 JimPeterson replies:

comment - `?:`?

I suppose the concept is somewhat niche, in the sense that it addresses a concern for Forth coders who wish to write code that is not only portable across multiple (or really all Core-supporting) implementations, but also ekes out every ounce of efficiency possible. I imagine cross-implementation coding is likely currently achieved with stanzas like:

platform-is-X [IF]

\ ... X-specific definitions

[THEN]

platform-is-Y [IF]

\ ... Y-specific definitions

[THEN]

where the specific optimizations and coverages are switched out based on known platform optimizations, whereas the ?: concept is more focused on individual words needed, and would therefore adapt even to implementations unknown to the author and to improvements when some of the words are added to new versions of existing implementations. Moreover, ?: is certainly not sufficient for words that work together, like TO/VALUE or DEFER/IS/etc. Such words would still require [IF]/[THEN] stanzas.

I definitely prefer the Core-only implementation (although :NONAME is Core-ext and VALUE/TO would need to be changed to VARIABLE), as my main focus is tiny implementations used for custom hardware, which may not have a full suite of extensions, and for which every bit of memory and performance matters. While I understand that MARKER is likely a much more comprehensive manner of rewinding a definition, I'm not really proposing this particular implementation as much as I am the concept of a word like ?:. The implementation I present is really more a proof-of-concept demonstration of behavior, and each Forth system would have whatever it considers most appropriate. That being said, besides rewinding HERE, I have no idea what other lasting effects a rewound :NONAME definition would have on the system (except as pertains to IMMEDIATE and other IMMEDIATE-like words).

This Core-only focus, however, implies that the ?: would only be of use if it was found in Core, itself. Otherwise, a stanza of the form [UNDEFINED] ?: [IF] ... [THEN] would be necessary, and not only that but, since [UNDEFINED], etc. are not even in Core, it may become challenging to include an appropriate header that covered all possiblities...


\ poor man's Core-only [IF]/[THEN] (avoid using ')' in clauses):
: ) ;
: IF( 0= IF POSTPONE ( THEN ;

\ Core-only... can't rely on [UNDEFINED]:
BL WORD ?: FIND NIP 0= IF(

    \ make sure `IMMEDIATE` is ok
    \ to call even after a `:NONAME`
    CREATE IMMEDIATE-OK 0 ,
    : : : -1 IMMEDIATE-OK ! ;
    : :NONAME :NONAME 0 IMMEDIATE-OK ! ; \ from Core-ext!
    : IMMEDIATE IMMEDIATE-OK @ IF IMMEDIATE THEN ;

    CREATE SKIP-DEF 0 ,

    : ;
        POSTPONE ;
        
        SKIP-DEF @ IF
            DROP HERE - ALLOT
            0 SKIP-DEF !
        THEN
        
    ; IMMEDIATE

    \ usage:  `?: -ROT ROT ROT ;`, for instance, will
    \ define `-ROT` only if it's not already defined.
    : ?:
        >IN @
        BL WORD FIND NIP
        IF
            \ skip definition:
            DROP
            HERE
            -1 SKIP-DEF !
            :NONAME \ from Core-ext!
        ELSE
            \ back up and define
            >IN ! :
        THEN
    ;

)

[r1486] 2025-08-08 17:51:25 EricBlake replies:

comment - `?:`?

    \ make sure `IMMEDIATE` is ok
    \ to call even after a `:NONAME`
    CREATE IMMEDIATE-OK 0 ,
    : : : -1 IMMEDIATE-OK ! ;
    : :NONAME :NONAME 0 IMMEDIATE-OK ! ; \ from Core-ext!
    : IMMEDIATE IMMEDIATE-OK @ IF IMMEDIATE THEN ;

As written, this does not pass the testsuite. For a simple example:

:NONAME ; DROP
1 CONSTANT one IMMEDIATE

fails to make one immediate, because it was defined with CONSTANT, yet the most recent :NONAME has not been countermanded by :. One way to fix it is wrapping ALL the defining words (CONSTANT, VARIABLE, VALUE, BUFFER:, CREATE, 2VARIABLE, ...), but that doesn't scale (how do you portably learn which defining words a system natively supports, since some like 2VARIABLE are optional, and since the user can create more defining words, as you have just done with ?:). So I tried a different approach: https://github.com/ForthHub/discussion/discussions/197 By making IMMEDIATE immediate, you can write:

: name IMMEDIATE body... ;

as syntax sugar for the standard : name body... ; IMMEDIATE, but more importantly, if you then use:

?: RDROP IMMEDIATE POSTPONE R> POSTPONE DROP ;

you either get a definition for RDROP that is immediate, or you get a :NONAME whose body is compiled to perform the effects of immediate at whatever future time(s) the xt is executed, in addition to its other semantics. You really don't want an RDROP that has the side effect of marking random words immediate; but on the other hand, your only use of :NONAME is for something you plan to throw away by rewinding HERE, so it will never be executed, and thus you never have an opportunity to see that subtle change in semantics between : and :NONAME for a leading immediate IMMEDIATE. Plus, once you switch to that style, you no longer have to implement IMMEDIATE-OK, and thus you manage to correctly support IMMEDIATE after all named defining words, without having to wrap every one of them.


[r1487] 2025-08-08 18:15:40 AntonErtl replies:

proposal - minimalistic core API for recognizers

Another point that I missed up to now and that others may have missed, too, is the order of recognizers in when using recognizer-sequence:: As currently specified, you use this word as follows

' rec-float ` rec-num ` rec-nt 3 recognizer-sequence: tradforth

And tradforth will first invoke rec-nt, then rec-num, and then rec-float. I find this order counterintuitive, but an argument for it is that it agrees with the order in get-order and set-order. In any case, I think we should discuss this.


[r1488] 2025-08-08 18:37:48 EricBlake replies:

proposal - Pronounciations

It would be helpful to use the proper spelling "Pronunciation" rather than "Pronounciation".


[r1489] 2025-08-09 15:30:12 JimPeterson replies:

referenceImplementation - Possible Reference Implementation

I'm not sure your version works (assuming 4TH means 3 PICK), as towards the end of the string, COMPARE may be accessing characters at caddr1+u1 and beyond. I think if you changed the DUP to 2 PICK OVER <= it may work better. I think this also points to a missing test(s) of this sort:

T{ : s8 S" xyz"      ; -> }T
T{ s1 2 - s8 SEARCH -> s1 2 - \<FALSE\>  }T

which tries to ensure that the code does not access characters that it shouldn't.

The corrected version is:

: SEARCH  ( caddr1 u1 caddr2 u2 -- caddr3 u3 flag )
   2OVER                  \ retain a copy of 1st string 
   BEGIN 
     2 PICK OVER <=
   WHILE 
     2OVER 3 PICK OVER COMPARE 
   WHILE 
     1 /STRING  
   REPEAT
     2NIP 2NIP TRUE EXIT  \ string found 
   THEN
   2DROP 2DROP FALSE      \ string not found 
;

[r1490] 2025-08-09 15:46:15 JimPeterson replies:

referenceImplementation - Possible Reference Implementation

... apparently, <= is not standard, so maybe > 0= instead?


[r1491] 2025-08-09 16:13:21 JimPeterson replies:

comment - `?:`?

This is interesting, and my original Forth implementation had IMMEDIATE as an immediate word (mostly because I didn't know Forth well enough... coincidentally, my first introduction being via JonesForth, as well). Unfortunately, I have the following:


create _to 0 ,
: TO 1 _to ! ; immediate
: VALUE
  create ,
  immediate
  does>
  STATE @ if
    _to @ if
      postpone literal
      postpone !
    else
      postpone literal
      postpone @
    then
  else
    _to @ if ! else @ then
  then
  0 _to !
;

where VALUE creates a word and then makes the created word immediate. I think that's one of the main capabilities intended by IMMEDIATE not being immediate, is that you can define words that define other words which themselves are immediate. Of course, you could always use POSTPONE in such situations, but unfortunately the die is cast.

Of course, again, all of this is just a rough draft at how it could work. If ?: was included, an implementation could just simply ignore an IMMEDIATE that occurs after a :NONAME.


[r1492] 2025-08-09 17:17:51 JimPeterson replies:

comment - `?:`?

hmm... I see you actually handle the DOES> case in your implementation. Still, all of this could be avoided if the standard simply said "Using IMMEDIATE after a :NONAME definition is a no-op", or something.