Forth words are usually very short for two reasons:
: F>C ( n1 -- n2 | convert degrees Fahrenheit to degrees Celsius) 32 - 5 * 9 / ;
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> ;
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 )
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:
>R
must be "popped" from the return stack
with a corresponding R>
before the end of the word.
R>
for every >R
inside of a DO ... LOOP
.
DO
loops, the loop index words I
and J
must not be used when items have been pushed onto the
return stack but not yet popped.
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 ;
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
else
= if \ is month1 equal to month2?
< \ flag represents day1 less than day2
else
2drop \ remove items on stack --- month1 is greater than
false \ month2 so return false flag
then
then ;
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< ;
: 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 ;
AGE
by typing age .