First Aid

Forth First Aid

  1. Where can I find more information about Forth programming language?
  2. Where can I use pure Forth ? Can you give more details?
  3. How can I declare a variable or define a word in the very beginning of a file?
  4. Which string types are used in ForthWin?
  5. What means does Forth provide to check if two strings are identical?
  6. What other "advanced" ways of working with strings are there?
  7. How to convert a number to a string and vice versa?
  8. What means does Forth provide for creating a simple loop?
  9. How can I "insert" a numerical variable into a text string so that I could use it, for example, in MSG: or in SEND-KEYS:?
  10. And what about a double number? Is it possible to "insert" it into a string, too?
  11. 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?
  12. How can I see the list of all Forth words in dictionary?


Where can I find more information about Forth programming language?

Here are some useful links for beginners:

https://skilldrick.github.io/easyforth/

http://galileo.phys.virginia.edu/classes/551.jvn.fall01/primer.htm

The thinking Forth Project

http://thinking-forth.sourceforge.net/

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 - http://thinking-forth.sourceforge.net/ .

Using 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 x
VARIABLE 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" TYPE

Pay 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" DROP

Special words are available to convert character string to zero-terminated string and vice-versa some of them you will find inside libraries for example ( S>ZALLOC is inside devel\~nn\lib\az.f )

  • S>ZALLOC ( a u -- az ) - takes a character string, allocates enough memory and records this string with ending zero to the allocated memory; it returns the address of the newly created string. The character count is not stored. In other words, one can record strings of any length (they may be as long as the system would allow).
  • ASCIIZ> ( az -- a u ) - converts zero-terminated string to character string by separating zero-terminated string address to an address and length in characters.
                            \ 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:
    MsgBox

3) 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 PLACE

You 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 allocation
CREATE str1 256 ALLOT
CREATE 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 allocation
CREATE str_1 256 ALLOT
CREATE str_2 256 ALLOT
CREATE str_3 256 ALLOT
CREATE 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_strings2
VARIABLE 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 identical
S" 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 found
S" str1" S" ?tr*" WC-COMPARE
\ the same as SEARCH, but not case-sensitive
S" xxxstryyu" S" STR" ISEARCH

How 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 100

Similar 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 101
101 S>D <# # # # # # #> \ produces string 00101
-101 DUP ABS S>D <# #S ROT SIGN #> \ produces string -101
101 S>D <# # 58 HOLD #S #> \ produces string 10:1

Notes:

  • for converting string to numbers USE : S>NUM ( a u -- n) which will provide always a correct result! ( sources are on devel\~nn\lib\s2num.f )
  • words <# and #> mark start and end of formatting;
  • word # converts one digit in the current numbering system;
  • word #S converts the entire number;
  • word SIGN inserts a minus sign into output string if the 3-rd number on the stack is negative;
  • word HOLD inserts into the current position any symbol whose ASCII code is currently on stack.

To get a string representation of a number in hexadecimal form, word N>H:

200 N>H \ produces string  C8

String to number:

For this purpose, use S>NUM. It is used in this way:

S" 128" S>NUM \ produces number 128

Example:

\ 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
\ NoActive
CREATE STR 256 ALLOT
VARIABLE NUMBER
Action:
    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> LOOP

Here 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 
    LOOP

To 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 5

To 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 
LOOP

If 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> UNTIL

Such 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" 
    UNTIL

Here is another type of a indefinite loop (or loop with a condition):

BEGIN <condition> WHILE <body of the loop> REPEAT

The 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 TimeMsgBox
LOOP

How can I "insert" a numerical variable into a text string so that I could use it, for example, in MSG: ?

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 variable
Action:
    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 string
MSG: "%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_size
NoActive
2VARIABLE size
Action:
    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 buffer
CREATE buf_ex 256 ALLOT
\ Action:
\ placing a string to this buffer
S" example" buf_ex PLACE
\ printing the string to console including the contents of  buf_ex
S" string containing %buf_ex COUNT% buffer" EVAL-SUBST TYPE CR
\ displaying a message with the same string
MSG: "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 MsgBox

How 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).