Jump to content
IGNORED

TF, camel, FB Forth fun


GDMike

Recommended Posts

On 8/26/2024 at 11:59 PM, TheBF said:

Not many folks are going to care about this but I am kind of jazzed about it so here it is. :)

 

While learning more about implementing lists in Forth kind of like LISP I realized that I was always thinking of "lists" as data.

One of the cool things about LISP is that DATA can be interpreted like CODE.

 

Forth allows that kind of thing as well by passing strings to the word EVALUATE.

S" 34 9 + ."  EVALUATE

However if we were to just make lists of Forth words and interpret them it would be 10X slower than compiled Forth.

 

It occurred to me that the real answer is to make lists of "compiled" code and that way you could do anything that you could compile.

But how? 

 

ANS Forth added the new word :NONAME into the language.  :NONAME works like ":"  but instead of associating the "execution token" (an address) 

of a defined word with a text name, it just gives you that magic address and you can do what you want with it. 

 

Example 

: HI   CR ." This is a normal Forth definition" ;  

:NONAME  CR ." This definition has "no name" so we will record the execution token"  ;  CONSTANT HI-TOKEN  

 

To run the word HI we just use the name and it runs itself. 

To run the token held in HI-TOKEN  we need to pass it to the word EXECUTE like this:

HI-TOKEN EXECUTE 

 

The benefit of using the tokens rather that strings is that you can still have lists of data with these token lists. 

How?  Because every Forth word begins with a pointer to a piece of code that tells the data how to behave. I call these routines "executors"

 

So a CONSTANT has a few instructions that say: Get the number you are holding and put it on the data stack. 

A VARIABLE's executor has code for:  Take the address where you are keeping your contents and put it on the data stack 

Similarly as string literal started with S"   has an executor that says put your address and string length on the data stack. 

 

In other words if we "execute" the code for these data types they can't help but do the right thing. 

 

So I wrote up a little experiment to play with the idea.

This code gives us {   } which counts how many things are on the data stack between the braces and returns that number 

 

TOKENS:  gives us a way to take all those tokens on the data stack, compile them into memory, like an array of tokens, and give them name

 

When you run that word with an index number it reaches into that array of tokens and PERFORMs the one at the index.

(PERFORM is just like EXECUTE but it needs a memory address, it fetches the token and does EXECUTE all in one ALC word)

 

(I took the liberty of renaming :NONAME to :: to make the code look cleaner) 

 

VARIABLE STK  \ can't use CSP because it is used by the colon compiler 

: {  ( -- )   SP@ STK ! ;
: }  ( -- n)  STK @ SP@ - 2/ 1- ;

: TOKENS:  ( xt1 ... xtn  n -- )
   CREATE    
        DUP ,            \ compile the number of items into this word 
        0 ?DO  ,  LOOP   \ compile all the XTs on the data stack 

   DOES> ( ndx -- [output depends on the code] )  
        DUP CELL+ >R    \ save copy of xt base address
        @               \ get the # of strings 
        OVER U< ABORT" TOKEN: index out of range" 
        CELLS R> +      \ index into the array of XTs
        PERFORM         \ fetch the XT and EXECUTE it 
;          

: :: :NONAME ( -- XT ) ; \ renamed for brevity

 

Now for example we could make lookup array for ELIZA like this:

\ Since these are colon definitions, they will return the address
\ of the string when TOKEN does the PERFORM function 
   {  :: S" I AM" ;
      :: S" I HAVE" ;
      :: S" I'VE" ;   
      :: S" I'M" ;    
      :: S" I WILL" ; 
      :: S" I'D" ;    
      :: S" I'LL" ;   
      :: S" MINE" ;   
      :: S" ARE" ;    
      :: S" WERE" ;   
      :: S" YOU" ;    
      :: S" ME" ;     
      :: S" YOUR" ;   
      :: S" IS" ;     
      :: S" MY" ;     
      :: S" I" ;     
   }  TOKENS: ]PHRASES  

 

8 ]PHRASE TYPE   will show us MINE 

 

But that's not the magic part. 

If we wanted strings that typed themselves out when invoked we just use  ."   the Forth equivalent of  PRINT  rather than S"  

And... we can use any code at all in the array.

{ ::  CR ." This 1st string will print itself" ;
  ::  CR ." This 2nd string will also print itself" ; 
  ::  10 0 DO  CR ." And this string has a built in loop !!"  LOOP  ;

} TOKENS: ]MAGICSTRINGS 

 

We just made a string array with embedded code.

 

I can die happy. :)

 

OK. Here it is for fbForth 3.0:

0 VARIABLE STK  \ can't use CSP because it is used by the colon compiler 

: PERFORM   ( addr -- )   @ EXECUTE  ;    \ fetch cfa and execute it

: {  ( -- )   SP@ STK ! ;           \ address of TOS to STK
: }  ( -- n)  STK @ SP@ - 2/ 1- ;   \ 

: TOKENS:  ( xt1 ... xtn  n -- )
   <BUILDS
        DUP ,           \ compile the number of items into this word 
        0 DO  ,  LOOP   \ compile all the XTs on the data stack 

   DOES> ( ndx -- [output depends on the code] )      
        DUP 2+ >R       \ save copy of xt base address
        @               \ get the # of strings        
        1- OVER U< ABORT" TOKEN: index out of range"  
        CELLS R> +      \ index into the array of XTs 
        PERFORM         \ fetch the XT and EXECUTE it 
;          

HEX
: :NONAME  ( -- cfa )
   ?EXEC             \ must be executing
   ALIGN             \ ensure dictionary pointer on even address
   HERE              \ cfa of nameless word body
   !CSP              \ store current stack level
   ]                 \ start compiling
   8334 ,            \ compile DOCOL address to cfa
;  IMMEDIATE

DECIMAL
: :: [COMPILE] :NONAME ; IMMEDIATE

: ;NONAME  ( --- )
   ?CSP              \ same stack level as at beginning of definition
   COMPILE ;S        \ compile SEMIS at end of current nameless word
   [COMPILE] [       \ start executing
;  IMMEDIATE

: ;; [COMPILE] ;NONAME ; IMMEDIATE

{ ::  CR ." This 1st string will print itself" ;;
  ::  CR ." This 2nd string will also print itself" ;; 
  ::  10 0 DO  CR ." And this string has a built in loop !!"  LOOP ;;
} TOKENS: ]MAGICSTRINGS

 

TOKENS: had a bug testing ndx. The array is zero based, so ndx needed to be decremented before testing. Though not a bug, the above TOKENS: code also stores the array upside down. The following code corrects the order:

: TOKENS:  ( xt1 ... xtn  n -- )
   <BUILDS
      DUP ,             \ compile the number of items into this word 
      CELLS DUP ALLOT   \ reserve space for all XTs
      \ Compile all the XTs on the data stack from end to beginning
      2+ 2 DO     
         HERE I - !     \ store next one back
      2 +LOOP   

   DOES> ( ndx -- [output depends on the code] )      
        DUP 2+ >R       \ save copy of xt base address
        @               \ get the # of strings        
        1- OVER U< ABORT" TOKEN: index out of range"  
        CELLS R> +      \ index into the array of XTs 
        PERFORM         \ fetch the XT and EXECUTE it 
;

 

Also, note the definitions for :NONAME, ::, ;NONAME, and ;;.

 

I should also note that S" gives the address of a counted string in fbForth 3.0, which means that typing a specific string from ]PHRASES would require COUNT before TYPE in @TheBF’s quoted code.

8 ]PHRASES COUNT TYPE

 

...lee

Edited by Lee Stewart
Addendum
  • Like 2
Link to comment
Share on other sites

Excellent work by you. 

It's pretty cool to see that across all those years of Forth systems from Figs to Camels there were just a few kinks to straighten out to make this work. 

Although you have to have a pretty deep knowledge of both to do the translation. 

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...