yet_another_webdev(blog) 2023-12-15 yet_another_webdev(blog)
PROJECT
Starting FORTH
POST
Chapter 02 - How to get results
DESCRIPTION
Forth arithmetics and stack manipulation words
FORTH Arithmetic -- Calculator Style
Here are four simples integer-arithmetic operators in FORHT:
+ (n1 n2 -- sum) Adds. pronounced "plus"
- (n1 n2 -- diff) Subtracts (n1 - n2). pronounced "minus"
* (n1 n2 -- prod) Multiplies. pronounced "star"
/ (n1 n2 -- qout) Divides (n1/n2). pronounced "slash"
In the first chapter, we learned that we can add two number by putting
them both on the stack, then executing the word + , then finnaly
executing the word . to get the result printed at our terminal.
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
17 5 + . 22 ok
Stack<10>
We can use thie method with all of FORTH's arithmetic operators. In other
words, we can use FORTH like a calculator to get answers, event without
writing a "program". Try a multiplication problem:
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
7 8 * . 56 ok
Stack<10>
By now we've see that the operator comes after the numbers. In the case
of subtraction and division, though, we must also consider the order of
numbers ("7 - 4" is not the same as "4 - 7").
Just remember this rule: To convert to postfix, simple move the operator
to the end of the expression:
Infix Postfix
3 + 4 3 4 +
500 - 300 500 300 -
6 X 5 6 5 *
20 / 4 20 4 /
So to do this subtraction problem: 7 - 4 =
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
7 4 - . 3 ok
Stack<10>
Example of division:
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
20 4 - . 5 ok
Stack<10>
What do you do if you have more that one operator in an expression, like:
4 + (17 * 12) you ask? Let's take it step-by-step: the parentheses tell
you to first multiply 17 by 12, then add 4. So in FORTH you would write:
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
17 12 * 4 + . 208 ok
Stack<10>
17 and 12 go onto the stack. * miltiplies them and returns the result
to the stack. Then the 4 goes onto the stack, on top of 204. + adds the
two numbers returning 208 to the stack.
No there's an interesting problem: (3 + 9) * (4 + 6)
To solve it we have to add 3 to 9 first, then add 4 to 6, then finally
multiply the two sums. In FORTH, we can write:
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
3 9 + 4 6 + * . 120 ok
Stack<10>
For Adventuresome Newcomers Sitting at a Terminal
If you're on of those people who likes to fool around and figure things
out for themselves without reading the book, then you're bount to
discover a copule of weird things. First off, as we told you, these
operators are integer operators . That not only means that you can't do
calculations with decimal values, like
10.00 2.25 +
if also means that you van only get integer results, as in
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
21 4 / . 5 ok
Stack<10>
Another thing is that if you try to multiply:
10000 10 *
or some suck large numbers, you'll get a crazy answer. So we're telling
you up front that with the operators introduced so far and with . to
print the results, you can't have any numbers that are higher than 32767
and lower than -32768. Numbers within this range are called "single-
lenght signed numbers".
Notice, in the list of FORTH words a few pages back, the letter "n",
which stands for "number". Since FORTH uses single-length numbers more
often than other types of numbers, the "n" signifies that the number must
be single-length. And yes, there are other operators that extend this
range ("double-length" operatores, which are indicated by "d").
pForth integers
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
: 2^10 1024 ; ok
Stack<10>
Define the word 2^10 to put 1024 (which is 2 to the 10th power) on the
stack.
: 2^62 2^10 2^10 2^10 2^10 2^10 2^10 2 2 * * * * * * * ; ok
Stack<10>
2^62 is 1 followed by 62 0s.
: 2^62-1 2^62 1 - ; ok
Stack<10>
Subtracting 1 from 2^62 is 0 followed by 62 1s.
: 2^63-1 2^62 2^62-1 + ; ok
Stack<10>
Adding the two numbers results in 2^63 - 1 which is 0 followed by 63 1s.
2^63-1 ok
Stack<10> 9223372036854775807
1 + ok
Stack<10> -9223372036854775808
Adding 1 results in 2^63 which is 1 followed by 63 zeroes. The negative
integer on the stack is the minimum value for a signed 64-bit integer.
FORTH Arithmetic -- Definition Style
In chpater #1 we saw that we could define new words in terms of numbers
and other pre-defined words. Let's explore some further possibilities,
usign some of our newly-learned math operators.
Let's say we want to convert various measurements to inches. We know that
1 yard = 36 inches and 1 foot = 12 inches so we can define these two
words where the names symbolize "yards-to-inches" and "feet-to-inches".
Here' what they do:
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
: YARDS>IN 36 * ; ok
Stack<10>
: FT>IN 12 * ; ok
Stack<10>
10 YARDS>IN . 360 ok
Stack<10>
2 FT>IN . 24 ok
Stack<10>
If we always want our result to be in inches, we can do the following:
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
: YARDS 36 * ; ok
Stack<10>
: FEET 12 * ; ok
Stack<10>
: INCHES ; ok
Stack<10>
10 YARDS 2 FEET 9 INCHES + + . 393 ok
Stack<10>
Notice that the word INCHES doesn't do anything except remind the human
user what the 9 is there for. If we really want to get fancy, we can add
these three definitions:
: YARD YARDS ; ok
Stack<10>
: FOOT FEET ; ok
Stack<10>
: INCH ; ok
Stack<10>
1 YARD 2 FEET 1 INCH + + . 61 ok
Stack<10>
2 YARDS 1 FOOT + . 84 ok
Stack<10>
So far we have only defined words whose definitions contain a single math
operator. But it's perfectly possible to put many operators inside a
definition, if that's what you need to do.
Let's say we want a word that computes the sum of five numbers on the
stack. A few pages back we summed five numbers like this: 17 20 + 132 + 3
+ 9 + . 181 ok But we ca also enter 17 20 132 3 9 + + + + . 181 ok
We get the same answer, even though we've clustered all the numbers into
one group and all operators into another group. We can write out
definition like this:
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
: 5#SUM + + + + ; ok
Stack<10>
17 20 132 3 9 5#SUM . 181 ok
Stack<10>
If we were going to keep 5#SUM for future use, we could enter it into
our ever-growing glossary, along with a note that it "expects five
arguments" on the stack, which it will add together.
Here's another equation to write a definition for: (a + b) * c Thus we
would write the following definition, this will work as long as we make
sure to push the numbers to the stack in the proper order:
: SOLUTION + * ;
c a b SOLUTION
The Division Operators
The word / is FORTH'S simplest division operator. Slash supplies only
the quotient; any remainder is lost:
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
22 4 / . 5 ok
Stack<10>
If you're thinking of a pocket calculator's per-cent operator, then five
is not the full answers.
But / is only one of several division operators supplied by FORTH to
give you the flexability to tell the computer exactly what you want it to
do.
For example, let's say you want to solve this problem: "How many dollar
bills can I get in exchange for 22 quarters". The real answer, of course,
is exactly 5, not 5.5. A computerized money changer, for example, would
not know howto give you 5.5 dollar bills.
Here are two more FORTH division operators:
/MOD (u1 u2 -- u-rem u-quot) Divides. Returns the pronounced "slash-mod"
remainder and the
quotient.
MOD (u1 u2 -- u-rem) Returns the remainder pronounced "mod"
from division.
The "u" stands for "unsigned". We'll see what this means in the chapter
on cumputer numbers. For now though, it means that the numbers can't be
negative.
gives both the remainder and quotient; MOD gives the remainder only.
(For /MOD , the stack notation in the table indicates that the quotient
will be on the top of the stack, and the remainder below. Remember, the
rightmost represents the topmost).
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
22 4 /MOD . . 5 2 ok
Stack<10>
: QUARTERS 4 /MOD . ." ONES AND " . ." QUARTERS " ; ok
Stack<10>
22 QUARTERS 5 ONES AND 2 QUARTERS ok
Stack<10>
Stack Maneuvers
If you worked Prob. 6 int the last set, you discovered that the infix
equation (a - b)/c cannot be solved with a definition unless there is
some way to rearrange values on the stack.
Well, there is a way: by using a "stack manipulation operator" called
SWAP
The word SWAP is defined to switch the order of the top two stack items.
As with other stack manipulation operators, you can test SWAP at your
terminal in "calculator style"; that is, it doesn't have to be contained
within a definition. Using SWAP we can define a solution for (a - b)/c
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
1 2 . . 2 1 ok
Stack<10>
1 2 SWAP . . 1 2 ok
Stack<10>
: SOLVE6b - SWAP / ; ok
Stack<10>
2 10 4 SOLVE6b . 3 ok
Stack<10>
Here is a list of several stack manipulation operators, including SWAP .
SWAP (n1 n2 -- n2 n1) Reverse the top two pronounced "swap"
stack items.
DUP (n -- n n) Duplicates the top pronounced "dupe"
stack items.
OVER (n1 n2 -- n1 n2 n1) Makes a copy of the pronounced "over"
second item and pushes
it on the top.
ROT (n1 n2 n3 -- n2 n3 n1) Rotates the third item pronounced "rote"
to the top.
DROP (n -- ) Discards the top stack pronounced "drop"
item.
DUP operator
the next stack manipulation operator on the list, DUP , simply makes a
second copy (duplicated) of the top stack item. For example, if we have
"a" on the stack, we can compute a^2 as follows
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
3 ok
Stack<10> 3
DUP * . 9 ok
OVER operator
Now somebody tells you to evaluate the expression: a * (a + b) given the
following stack order: (a b -- ) But, you say, I'm going to need a new
manipulation operator: I want two copies of the "a", and the "a" is under
the "b". Here's the word you need: OVER . OVER simply makes a copy of
the "a" and leapfrogss it over the "b": (a b -- a b a) . The expression
a * (a + b) can be solved with: OVER + *
When writing equations in FORTH, it's best to "factor them out" firts.
For example, if somebody asks you to evaluate: a^2 + a*b is FORTH,
you'll find it quite complicated (and maybe even impossible) using the
words we've introduced so for ... unless you factor out the expression to
read: a * (a + b) which is the expression we just evalutated.
ROT operator
The fourth stack manipulator on the list is ROT (pronounced "rote"),
which is short for "rotate". Here's what ROT does to the top three stack
values: (a b c -- b c a)
For example, if we need to evaluate the expression: ab - bc we should
first factor out the "b"s: b * (a - c) Now if our starting-stack order
is this: (c b a -- ) we can use: ROT - *
DROP operator
The final stack manipulation operator on the list is DROP . All it does
is discard the top stack value.
A Handy Hint
Use the word .S for a non-destructive stack print. This will print the
state of the current stack without removing the values from it.
Playing Doubles
The next four stack manipulation operators should look vaguely familiar:
2SWAP (d1 d2 -- d2 d1) Reverse the top two pronounced "two-swap"
pairs of numbers.
2DUP (d -- d d) Duplicates the top pronounced "two-dupe"
pair of numbers.
2OVER (d1 d2 -- d1 d2 d1) Makes a copy of the pronounced "two-over"
second pair of numbers
and pushes it on top.
2DROP (d -- ) Discards the top pair pronounced "two-drop"
of numbers.
the prefix "2" indicates that these stack manipulation operators handle
numbers in pairs. The letter "d" in the stack effects column stands for
"double". "Double" has a special significance that we will discuss when
we discess "n" and "u".
The "2"-manipulators listed above are so straightforward, we won't even
bore you with examples.
One more thing: there are still some stack manipulators we haven't talked
about yet, so don't go crazy by trying too much fancy footwork on the
stack.
FORTH words covered in this chpater:
+ (n1 n2 -- sum) Adds. pronounced "plus"
- (n1 n2 -- diff) Subtracts (n1 - n2). pronounced "minus"
* (n1 n2 -- prod) Multiplies. pronounced "star"
/ (n1 n2 -- qout) Divides (n1/n2). pronounced "slash"
/MOD (u1 u2 -- u-rem u-quot) Divides. Returns the pronounced "slash-mod"
remainder and the
quotient.
MOD (u1 u2 -- u-rem) Returns the remainder pronounced "mod"
from division.
SWAP (n1 n2 -- n2 n1) Reverse the top two pronounced "swap"
stack items.
DUP (n -- n n) Duplicates the top pronounced "dupe"
stack items.
OVER (n1 n2 -- n1 n2 n1) Makes a copy of the pronounced "over"
second item and pushes
it on the top.
ROT (n1 n2 n3 -- n2 n3 n1) Rotates the third item pronounced "rote"
to the top.
DROP (n -- ) Discards the top stack pronounced "drop"
item.
2SWAP (d1 d2 -- d2 d1) Reverse the top two pronounced "two-swap"
pairs of numbers.
2DUP (d -- d d) Duplicates the top pronounced "two-dupe"
pair of numbers.
2OVER (d1 d2 -- d1 d2 d1) Makes a copy of the pronounced "two-over"
second pair of numbers
and pushes it on top.
2DROP (d -- ) Discards the top pair pronounced "two-drop"
of numbers.
Review of Terms
Double-length numbers
integers which encompass a range of over -2 billion to +2 billion (and
which we'll introduce officially in Chapter 7).
Single-length numbers
integers which fall within the rane of -32768 to +32767: the only numbers
which are valid as the arguments or results of any of the operators we've
discussed so far. (This seemingly arbitrary range comes from the way
computers are designed, as we'll see later on.)
toolsV2 yet_another_webdev(blog)