Using kForth

2.7 Acting on Conditions

Nearly all computer programs, except for the simplest, will check to see if a specified condition is either true or false, and carry out different instructions based on the result. We have already seen how a DO ... LOOP works in Forth. In this special case, the word LOOP adds 1 to the loop counter and then checks whether or not the condition that the loop counter is equal to the ending count of the loop is true or false. Often, we will want to instruct the computer to check conditions that are not related to loops and then execute one sequence of words if the condition is true, or another sequence of words if the condition is false. Let's see how we can do this in Forth.

To start with, let's look at how to test a condition and how the result of the test is represented. As an example, our condition to be tested is whether or not the variable X is greater than 2. In Forth, such a test would be written as

X @ 2 >

We fetch the value of X onto the stack, next place the integer 2 on the stack, and then use the word > to check whether or not the number buried one cell deep into the stack is greater than the number on the top of the stack. The stack diagram for > is

n1 n2 -- b

Therefore, > removes both numbers from the stack and leaves a boolean flag, written as "b" in the stack diagram above. The flag "b" is itself another number, but it is a number that is always either 0 or -1. The value of the flag represents one of two states: true, corresponding to the value -1 and false, corresponding to the value 0. As a convenience, Forth provides two predefined constants TRUE and FALSE. Try the following.



Now we have learned that the result of a test is a flag value, either true or false, placed on top of the stack. Although our example used the word >, other words in Forth can test for equality of two numbers, a less than condition, and perform several other comparisons.

A flag on top of the stack is used by the word IF to cause the computer to jump to different locations inside the executing word, based on the flag's value. This process is called conditional branching and all programming languages provide a way to do this. The word IF is part of a control structure made up of the words IF ... ELSE ... THEN, where ... represents some arbitrary word sequences. Many other programming languages have a structure similar to this, but in Forth its use is slightly different. The word IF assumes the conditional test has already been performed and that there is a flag on top of the stack. Let's illustrate the use of the IF ... ELSE ... THEN structure with an example. Suppose we want to write a word that prints whether a number given to it is "even" or "odd". We could define this word as follows

: parity  ( n -- | print whether a number is even or odd )
    2 MOD 0=
      ." even"
      ." odd"
    THEN ;

In our definition of the word parity, the conditional test is given by the line

2 MOD 0=

The word MOD performs a division, except that it returns the remainder instead of the quotient. An "even" number divided by 2 has a zero remainder, so we check to see if the value returned by MOD, on top of the stack, is equal to zero. The word 0= returns a true flag when the number on top of the stack is zero, a false flag otherwise. When IF examines this flag, if it finds the flag to be true, execution jumps to the word following IF. On the other hand, if the flag is false, execution branches to the word following ELSE. To see how it works, try typing a number followed by the word parity, e.g.

4 parity

Here are a few other points to note about the IF ... ELSE ... THEN structure:

2.8 The Return Stack

Because Forth words often use values on the stack for input data, it may become difficult to keep the items ordered exactly as needed during the calculation, especially when there are several input values required. While Forth provides several stack manipulation words such as DUP SWAP ROT, etc., sometimes the most convenient operation is to temporarily remove an item from the top of the stack, then place it back on the stack when needed. Of course we may use variables for this purpose, but Forth provides a simpler way to accomplish this by providing another stack, called the return stack. An item on the stack can be temporarily moved onto the return stack by using the word >R. The item may be moved back from the return stack to the ordinary data stack with the word R>.

The following example also illustrates the use of the return stack.

: this_date ( -- day month year )
     time&date >r >r >r 2drop drop r> r> r> ;

The word this_date returns today's date on the stack with the year on top. It does this by calling kForth's built-in word, TIME&DATE, which has the following stack diagram:

time&date ( -- secs mins hours day month year )

We want our word this_date to only return the day, month, and year, so we must remove secs, mins, and hours left on the stack by TIME&DATE. However, day, month, and year are on top and the three numbers we want to drop (secs, mins, and hours) are buried underneath. Using >R three times, we remove the year, month, and day from the stack, in that order. These numbers are moved onto the return stack. Now we use 2DROP and DROP to remove hours, mins, and secs from the stack. Finally, we use the word R> three times to move day, month, and year from the return stack back to the data stack.

A word of caution to the novice Forth user: the return stack must be used with the following restrictions because Forth itself places items on the return stack at the beginning of executing a word and also when executing DO loops:

Example 2: Calculating Age

In this example, we will make use of what we have learned up to now to compute the age of a person given their birth date. Following good Forth practice, we will first define a few simple words which we anticipate will be helpful for writing the actual age calculator:

: this_year ( -- year )
     this_date >r 2drop r> ;

: this_month ( -- month )
     this_date drop nip ;

: this_day ( -- day )
     this_date 2drop ;

The words this_year, this_month, and this_day all use this_date, defined previously, and remove any extra items from the stack. A couple more words will be helpful in our calculation:
: date< ( day1 month1 day2 month2 -- flag )
      rot swap
      2dup                  \ is month1 less than month2?
      < if
        2drop 2drop         \ remove items on stack --- no further test needed
	true                \ leave true flag on the stack
        = if                \ is month1 equal to month2?
          <                 \ flag represents day1 less than day2
	  2drop             \ remove items on stack --- month1 is greater than
          false             \   month2 so return false flag
      then ;

Notice that we used two nested IF ... ELSE ... THEN structures in our definition of DATE<. The first IF examines the flag returned by <, which tests whether or not month1 is less than month2. If month1 is not less than month2, we must then check to see if month1 is equal to month2. The word = tests this condition and returns the appropriate flag, which is examined by the second IF.

: after_today ( day month -- flag | test whether day and month are in future)
     this_day this_month 2swap date< ;

We are ready now to calculate a person's age, given their birth date.

: age ( day month year -- age | calculate age given birth date )
     this_year swap -       \ number of years between birth year and this year
     -rot                   \ move top item to bottom of stack
     after_today if	    \ is birthday later than today?
       1-                   \ yes, subtract one from number of years
     then ;

We may test our definition of AGE by typing

day month year age .

where day, month, and year are the numbers for your birth day, month, and year. kForth will respond by printing your current age.

Factoring a Forth Program

You may have noticed that in our example of the age calculator, we defined several words, not just one. Breaking the calculation into individual short words is a way to make writing a program simpler, easier to understand, and easier to test when, as is inevitable, a program doesn't work like you imagined. Previously defined words can be used to write higher level words, making the higher level words more readable. Well-written Forth programs will often have short low-level words, each of which performs a single and simple computation matching well the name of that word. As an example, consider the game tetris.4th written in Forth ( pure source). Notice how the words defined towards the beginning of the program, such as DRAW-PIT and UPDATE-SCORE are short words with well-defined functions matching their names. Near the end of the program, various words are combined to define the higher level word, PLAY-GAME. Although the working of the lower level words may not be immediately apparent from reading their definitions, in a properly factored Forth program, the high level word(s), such as PLAY-GAME, are very readable and often times resemble a natural language description of the actions performed by the word. Good factoring is a skill acquired through practice with writing programs in any programming language, and often results in programs which are more easy to diagnose and repair when things don't work as expected.