+TheBF Posted August 28 Share Posted August 28 Aha. I do not enough about the inner workings of FigForth clearly. I will peek at your source code to get better handle on TOGGLE and SMUDGE. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 28 Share Posted August 28 (edited) 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 August 28 by Lee Stewart Addendum 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 28 Share Posted August 28 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. 1 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.