Forth First Aid
Where can I find more information about Forth programming language?
Here are some useful links for beginners:
Don't forget to check onine versions of Starting Forth, a great book by Leo Brodie - http://home.iae.nl/users/mhx/sf.html and Thinking Forth by Leo Brodie - http://thinking-forth.sourceforge.net/ .
There also exists a Stephen Pelc book Programming Forth - http://www.mpeltd.demon.co.uk/books.htm .
There is a newsgroup, COMP.LANG.FORTH, which is dedicated to Forth language.
Using e Forth creating your first words.... Can you give more details?
Forth can be used immediately after creating any word.
Example:
 : x1 .... ;: x2 .... ;: x3 .... ;Action:    x1 x2 x3  How can I declare a variable or define a word outside of a particular task, e.g. in the very beginning of a file?
You can use any previously defined Forth words or expressions in your constructs., if they are already on the dictionary. This type of construct can be located in any place of a file including its beginning.
Example:
 VARIABLE xVARIABLE y: z .... x @ .... y ! ;  Action: z   ( stack effect  x value y value etc..) Which string types are used in Forth?
1) Character string (au-string):
The strings of this type are most often used strings in Forth. Such a string is specified by a cell pair ( a u -- ) representing its starting address and length in characters. Use a S" xxx" syntax to create character string. For example, after you enter in console
S" my first string" and press <ENTER>, two values will be placed on the stack - the address of the first character of the newly created string and the number of its characters.
Most of words, that are working with strings are expecting for character string as their argument. For example, you can use the word TYPE ( a u -- ) to print character string to the console:
S" my first string" TYPEPay attention to the fact, that the length of a strings created using S" xxx" syntax can not exceeds 255 characters.
2) Zero-terminated string (az-string):
These strings are used when calling API functions and when the string length can exceed 255 characters: the length of zero-terminated strings is not limited.
Use Z" xxx" or S" xxx" DROP syntax to create zero-terminated string:
Z" null-terminated string"S" null-terminated string" DROPSpecial words are available to convert character string to zero-terminated string and vice-versa:
                            \ converting character string to a zero-terminated string:    S" test string" S>ZALLOC                           \ separating string address to an address and length in characters:    ASCIIZ>                             \ displaying the string in a message:    MsgBox3) Counted string (ac-string):
Counted string is a sequence of bytes, that represents the string contents. The first byte in this sequence holds the string length in characters (binary
representation of the number of data characters). This byte is called length character. Counted string in memory is identified by the address of its length character. This string type was widely used in early Forth implementations. Currently there are not too many words, that are expecting for counted strings as their arguments. The length of counted strings can not exceeds 255 characters.
Use C" xxx" syntax to create counted string. In addition, counted string can be created by using the word PLACE
C" counter string"S" your string" buffer_name PLACEYou can convert counted string to a character string using the word COUNT ( a -- a u ), that separates counted string address to an address and length in characters.
     \ creating counted string:    С" test string"    \ converting counted string to a character string    \ and printing it to console:    COUNT TYPE CR  CREATE my_strbuffer 256 ALLOT\ Action:    \ creating counted string:    S" test string" my_strbuffer PLACE    \ printing buffer address to console:    my_strbuffer . CR    \ separating address to an address and length in characters    \ and printing character string to console:    my_strbuffer COUNT TYPE CR    \ displaying character string in a message:    MSG: "%my_strbuffer COUNT%"  What means does Forth provide to check if two strings are identical?
You can use word COMPARE which returns 0 if compared objects are identical.
Example:
S" first string" S" second string" COMPARE 0=IF MSG: "Strings are the same"ELSE MSG: "Strings are different"THEN What other "advanced" ways of working with strings are there?
This is a very difficult question to answer, there are many possible answers. To make the answer as precise as possible, let me use you some examples.
Example 1:
\ forth_strings\ creating 2 strings up to  255 characters each \ with static memory allocationCREATE str1 256 ALLOTCREATE str2 256 ALLOT\ Action:    \ Word PLACE puts a string into specified location    S" This is a first string" str1 PLACE    \ Displaying a message with the first string. Using word COUNT      \  to convert  an address into address with string length.    MSG: "%str1 COUNT%"    \ +PLACE adds another string to the specified string    S" , and this is an addition to it..." str1 +PLACE    MSG: "%str1 COUNT%"    \ removing 5 leftmost characters from str1     \ and placing the result to str2    str1 COUNT 5 /STRING str2 PLACE    MSG: "%str2 COUNT%"    \ removing 5 rightmost characters from str1     \ and placing the result to str2    str1 COUNT 5 - 0 MAX str2 PLACE    MSG: "%str2 COUNT%"    \ placing the first 10 characters from str1 to str2    str1 COUNT 10 MIN str2 PLACE    MSG: "%str2 COUNT%"    \ placing to str2 the last 10 characters from str1    str1 COUNT DUP 10 - 0 MAX /STRING str2 PLACE    MSG: "%str2 COUNT%"    \ placing 10 characters from  str1 to str2,    \ starting with the 11-th character    str1 COUNT 10 /STRING 10 MIN str2 PLACE    MSG: "%str2 COUNT%"\Example 2:
\ forth_strings1\ creating 4 strings up to  255 characters each \ with static memory allocationCREATE str_1 256 ALLOTCREATE str_2 256 ALLOTCREATE str_3 256 ALLOTCREATE str_4 256 ALLOT\ A new word,  StringReplace str2 str1 str3 str4,\ Searches  str_1 for substrings matching  str_3, \ and if it finds anything, it replaces all found matches by \ на str_4. The result is placed to str_2.\ If no matches are found, it places \ the entire str_1 to str_2, without any modifications\ The example is not uneversal in that it was written to process \ a particular phrase.: StringReplace ( -- )  str_2 0!  str_1 COUNT  BEGIN OVER SWAP str_3 COUNT SEARCH WHILE  >R SWAP 2DUP - str_2 +PLACE  str_4 COUNT str_2 +PLACE  R> str_3 C@ /STRING  REPEAT  str_2 +PLACE  DROP;\ Another new word: StringReplace str str str str\ It can function in the same way   \ that the preceding one, but it is universal \ because it uses local variables, i.e..\ it doesn't have to use  str_1, str_2  etc.: StringReplace2 { a2 a1 u1 a3 u3 a4 u4 \ rest -- a2 u2 }  a2 0!   a1 u1   BEGIN OVER SWAP a3 u3 SEARCH WHILE   TO rest SWAP 2DUP - a2 +PLACE   a4 u4 a2 +PLACE   rest u3 /STRING   REPEAT   a2 +PLACE   DROP  ;\ Action:    \ placing a long phrase into str_1    S" This string is not a very good sample. " str_1 PLACE    S" But not just a sample." str_1 +PLACE    \ defining a search phrase    S" not" str_3 PLACE    \ defining a replacement phrase    S" " str_4 PLACE    StringReplace str_2    MSG: "%str_2 COUNT%"    str_2 S" test test test" S" es" S" ouris" StringReplace2    MSG: "%str_2 COUNT%"\ )#Example 3:
\  forth_strings2VARIABLE position\ New word. It is used to search a string for matches \ with another string and (if any found) to return the position\  of the first character of this  match.\ Position numbering starts with 1. If the substring is not found, \ it returns 0. Of course, it is also possible to start the position count from 0\ and to return -1 if nothing is found: StringGetPos { a1 u1 a2 u2 -- pos}a1 u1 a2 u2 SEARCH IF DROP a1 - 1+ ELSE 2DROP 0 THEN ;\ Action:    \ comparing the strings and writing the result     \ into variable position    S" 123456789" S" 567" StringGetPos position !    \ displaying the result (should be 5)    MSG: "Position: %position @%"\There are many other words that are used to work with strings. For example:
\ returns 0 if the strings are identicalS" str1" S" str2" COMPARE\ returns 0 if the strings are identical (not case-sensitive)S" str1" S" Str2" ICOMPARE\ returns TRUE if a match is foundS" str1" S" ?tr*" WC-COMPARE\ the same as SEARCH, but not case-sensitiveS" xxxstryyu" S" STR" ISEARCHHow to convert a number to a string and vice versa?
Number to string:
Create the s word N2S. Here is its definition:
:  N2S  DUP >R ABS 0 <# #S R> SIGN #> ;  \ N2S (u--addr u) It is used in this way:
100 N2S \ produces string 100Similar results can be obtained with the help of construct S>D <# #S #>, and in this case one can specify how many characters should be converted, add necessary characters to the string, and convert characters with a sign:
101 S>D <# #S #> \ produces string 101101 S>D <# # # # # # #> \ produces string 00101-101 DUP ABS S>D <# #S ROT SIGN #> \ produces string -101101 S>D <# # 58 HOLD #S #> \ produces string 10:1Notes:
To get a string representation of a number in hexadecimal form, word N>H:
200 N>H \ produces string  C8String to number:
For this purpose, use S>NUM. It is used in this way:
S" 128" S>NUM \ produces number 128Example:
\ test_int_to_string\ picking  IP addresses one by one, in order,  starting with 128.128.128.1\ and ending at 128.128.128.10\ NoActiveCREATE STR 256 ALLOTVARIABLE NUMBERAction:    BEGIN    S" 128.128.128." STR PLACE    NUMBER @ 1 + NUMBER !    NUMBER @ N>S STR +PLACE    TMSG: "%STR COUNT%" 1    \ instead of just displaying a resulting  IP address,     \ you could, for example, "feed" it to HOST-EXIST:     NUMBER @ 10 =    UNTIL\Loops in Forth >: creating a simple loop
If you know in advance how many times the loop should be repeated, then best way to do it would be to use DO ... LOOP construct (definite loop):
10 0 DO <body of the loop> LOOPHere 10 is the limit and 0 is the index (a number which is incremented by 1 with each iteration). To access the index from the body of the loop, use word "I" (please note that this is an uppercase letter "i", not a slash or vertical bar)—it puts the current value of the index into data stack .
The loop will be executed over and over until the index becomes equal to the limit. Therefore, the above loop will be executed 10 times.
Example:
\ Action:    3 0 DO        \  message  will be displayed 3 times        TMSG: "Inside the loop" 1         \ numbering starts with 0, index equals 1        I 1 =         \ displaying another message        IF TMSG: "Second loop" 1 THEN     LOOPTo cause the index to be incremented by any other number then 1, use word +LOOP:
50 0 DO <body of the loop> 5 +LOOP \ incrementing by 5To force an exit from a definite loop, use word LEAVE:
\ Action:5 0 DO    \ message will be displayed four times    TMSG: "Inside the loop" 1     \ exiting the loop after the fourth iteration    I 3 = IF LEAVE THEN LOOPIf the number of iterations is not known, you can use the BEGIN ... UNTIL construct (indefinite loop, which is repeated until a specified condition is met). It is used in the following way:
BEGIN <body of the loop> <condition> UNTILSuch a loop will be executed at least one time and then its iterations will go on until a certain condition becomes true:
Action:    BEGIN         \ repeating this message        TMSG: "Warning! xxx.txt not exist!" 3         \ until the following file appears        FILE-EXIST: "c:\xxx.txt"     UNTILHere is another type of a indefinite loop (or loop with a condition):
BEGIN <condition> WHILE <body of the loop> REPEATThe loop is repeated until the condition is true.
And here is an example showing you how you can display a value of index (I) without creating an additional variable:
3 0 DO    I S>D <# 0 HOLD #S S" I=" HOLDS #> 1 TimeMsgBoxLOOPHow can I "insert" a numerical variable into a text string so that I could use it, for example, in MSG: or in SEND-KEYS:?
Here's how:
%VARIABLE_NAME @%Now this variable's value can be displayed in a message (MSG, TMSG) and/or printed with SEND-KEYS etc.
Example:
VARIABLE MyVariable \ creating a variableAction:    13 MyVariable ! \ setting the value of  MyVariable  to 13    MSG: "MyVariable = %MyVariable @%"    START-APP: "notepad"    PAUSE: 1000    BEGIN \ starting a loop        SEND-KEYS: "%MyVariable @% {DELAY 300}"        MyVariable @ 1 - MyVariable !         MyVariable @ 0 =    UNTIL \ ending  the loop    SEND-KEYS: "{ENTER}That's the end, folks!"Please keep in mind that you can also insert into a text string any Forth word which return a string ( -- addr u) or a 32-bit integer ( -- n). In order to do that, you just have to surround the word by percent signs.
Example:
\ the number of milliseconds since system startup, \ is inserted into a stringMSG: "%GetTickCount%"And what about a double number? Is it possible to "insert" it into a string, too?
Sure. Here is an example of this:
\ test_dir_sizeNoActive2VARIABLE sizeAction:    RECURSIVE DIR-SIZE: "c:\cpp" size 2!    MSG: "c:\cpp size = %size 2@ <# #S #>%"\Handling double numbers is discussed
Did I get it right that within a string (between quotation marks) one can place a pair of percent signs ("%%") and insert any sequence of Forth words between them?
Yes, you got it right. "%...%" construct is dynamically processed, that is to say, when the prefix string is parsed, the sequence between "% % " signs is analyzed separately and word EVALUATE is applied to this substring. Please note that if you use postfix notation you will have to use word EVAL-SUBST in order to "unfold" contents of "%...%" construct.
Example:
\ creating a bufferCREATE buf_ex 256 ALLOT\ Action:\ placing a string to this bufferS" example" buf_ex PLACE\ printing the string to console including the contents of  buf_exS" string containing %buf_ex COUNT% buffer" EVAL-SUBST TYPE CR\ displaying a message with the same stringMSG: "string containing %buf_ex COUNT% buffer"To your information: if a Forth word or a variable returns a string, you can skip the %...% altogether when using postfix notation:
\ "prefix" notation:MSG: "%USERNAME%"\ postfix notation:USERNAME MsgBox\ of course, you can do it this way too  (but less efficiently):S" %USERNAME%" EVAL-SUBST MsgBoxHow can I see the list of all Forth words in the dictionary?
That is very simple: open FORTHwin console, enter command WORDS and hit <ENTER>. But be prepared to see a list of about >1000 words! :)
\ Action:    WORDS (The console must be closed).
Hmmmm... And how can I find the word I need (I only can recall a part of it)?
It is just for this occasion you have the word WORDS-LIKE in plugin tools.spf.
How can I use value of a variable (or current value on the stack) as a parameter of some word?
There are special words to use numbers and strings from the stack as a parameters:%n esPICK% and %n esPICKS%.
Besides that, each prefix-type word (ХХХ:) has a corresponding postfix word (either ХХХ, or some other name). And it is these postfix words that should be used when the current value on the stack is used as an argument.
Examples:
BEEP: 1000 500 -> var_name @ var_name1 @ BEEPWIN-SHOW: "xxx" -> S" xxx" WIN-SHOWWIN-HIDE: "xxx" -> S" xxx" WIN-HIDE \ ...FILE-APPEND: "file" "xxx" -> S" xxx" S" file" FAPPENDFILE-WRITE: "file" "xxx" -> S" xxx" S" file" FWRITE FILE-COPY: "file1" "file2" -> S" file1" S" file2" FCOPYPrefix words of "ХХХ:" type were introduced expressly in order to save users the trouble of puzzling out how to use the reverse Polish notation.
How to write data to a file or read from one?
You can easily write text data to a file using features provided by nnCron. Read the Help sections discussing such words as LOG:, FILE-WRITE:, FILE-APPEND:...
As to reading an entire text file, the simplest way to do this is as follows:
S" file-name" FILEor even
FILE: filenameThe first method is convenient when you have to get the file's name form a variable, and the second one is for use between percent signs (% %), so it takes as its parameter (file name) the sequence of characters to the end of the line.
\ first method: 2nd-file S" %JT%\test.txt" EVAL-SUBST FILE ;Action:    FILE-APPEND: "%JT%\new.txt" "%2nd-file%"\ second methodMSG: "%FILE: test.txt%"Example:
#( test_read_from_fileNoActive\ reading 64 characters from file  test.txt  \ and placing them to array  file_contents. \ displaying a message with the content of array.CREATE file_contents 256 ALLOTAction:    PAD 64 S" test.txt" FREAD file_contents PLACE    MSG: "%file_contents COUNT%")#To read information from a file line by line, we'll need to use ForthWin word READ-LINE. Here is its description:
READ-LINE ( c-addr u1 fileid -- u2 flag ior ): read the next line from the file specified in fileid into memory address c-addr. No more then u1 characters will be read. String buffer c-addr must have length of minimum u1+2 characters (including end of line characters). If the action is successful, flag will be "true" and ior will equal zero. If end of line is reached before u1 characters has been read, then u2 is a number of actually read characters without "end of line" characters. If u1=u2, end of line has been reached already.
#( test_read_by_lineNoActive\ Reading  file test.txt line by line (in a loop) \ into array  list-contents\ and displaying each line in a message box.\ Lines in file should not be longer then 255 characters.VARIABLE list-fileCREATE list-contents 258 ALLOTAction:    S" test.txt" R/O OPEN-FILE-SHARED THROW list-file !    \ writing the string into the second postion of array list-content    BEGIN list-contents 1+ 255 list-file @ READ-LINE THROW WHILE    \ Number of read characters remains on the stack..    \ We will save it in the first position of array    \ in order to get a string with a character count    list-contents C!    MSG: "%list-contents COUNT%"    REPEAT    DROP    list-file @ CLOSE-FILE DROP)##( test_read_by_line1NoActive\ Reading  file test.txt line by line (in a loop) \ into variable list-contents\ and displaying each line in a message box.USER-VALUE list-fileUSER-VALUE list-contents\ maximum length of line  (number of characters)10240 CONSTANT max-line-sizeAction:    \ Allocating sufficient space in memory    \ and recording its address into list-contents        max-line-size ALLOCATE THROW TO list-contents    S" test.txt" R/O OPEN-FILE-SHARED THROW TO list-file    BEGIN list-contents max-line-size 2- list-file READ-LINE THROW WHILE    \ The number or read characters remains on the stack.    \ Adding it to address of allocated memory    \ and adding 0 to get a zero-terminated string    list-contents + 0 SWAP C!    MSG: "%list-contents ASCIIZ>%"    REPEAT    DROP    list-file CLOSE-FILE DROP    list-contents FREE DROP)#How can I handle lists?
A list is a special type of data structure where each element (node) contains a reference to the next element in the list which unites the data and simplifies navigation of the list. Such a list is often called "linked list". The last element of a list does not contain a reference and returns a zero if you attempt to get the address of the next element. This permits us to easily learn if the list is empty or not. To declare and name a list use word VARIABLE.
Here are the words for working with lists:
To illustrate this, let's create an elementary list, add some nodes to it, print their values to console, purge the list and make sure that it is really empty:
#( test_listNoActive \ creating a listVARIABLE MY_LIST\ defining a word that will print \ contents of a node to console: show_values NodeValue . CR ;Action:    \ adding nodes and their values to the list    1 MY_LIST AppendNode    2 MY_LIST AppendNode    3 MY_LIST AppendNode    \ printing to console addresses or the first,    \ second and third nodes    MY_LIST @ . CR    MY_LIST @ @ . CR    MY_LIST @ @ @ . CR    \ now printing to console     \ the values stored in the list nodes    ['] show_values MY_LIST DoList    \ printing the same values "manually"    MY_LIST @ NodeValue . CR    MY_LIST @ @ NodeValue . CR    MY_LIST @ @ @ NodeValue . CR    \ purging the list    MY_LIST FreeList    \ making sure that the list is empty:    MY_LIST @    IF S" List is not empty" TYPE CR    ELSE S" List is empty" TYPE CR    THEN)#Word ['] compiles the address of the next word in the definition as a literal, i.e. instead of the actual word we substitute the address where its definition is stored.
Another "real-life" example:
#( task1NoActiveVARIABLE f-list: f-action NodeValue ASCIIZ> MsgBox ;: f-free NodeValue FREE DROP ;Action:    ['] faction f-list DoList    ['] f-free f-list DoList    f-list FreeList)##( task2NoActiveAction:    f-list OFF    FOR-FILES: x:\xxx\*.*        IS-DIR? 0=        IF FOUND-FULLPATH S>ZALLOC f-list AddNode THEN    ;FOR-FILES    f-list @ IF task1 RUN THEN)#