yet_another_webdev(blog) 2023-07-28 yet_another_webdev(blog)
PROJECT
Starting FORTH
POST
Chapter 01 - Fundamental FORTH
DESCRIPTION
Using pForth to go over the 1st chapter. Covering the basics of syntax,
interpreter and the stack.
pForth
Going through the book I am using pForth unless specified otherwise.
$ type forth
forth is aliased to ‘/home/sergei/pforth/platforms/unix/pforth_standalone'
Below is an example of running pForth interactively, CTRL+d to exit.
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
1 ok
Stack<10> 1
2 ok
Stack<10> 1 2
3 ok
Stack<10> 1 2 3
+ + . CR 6
ok
Stack<10>
$
Same forth program can ran from file.
$ cat > test.fth <<EOF
1
2
3
+ + . CR
EOF
$ forth -q test.fth
6
A Living Language
Forth lets you organize your own procedures and communicate them to
computer in by:
1. defining useful tasks and giving each task a name, then
2. group related tasks together into larger tasks and give each of
these a name, and so on.
An example of the method above is defining a program for a washing
machine. In the following code, the colon denotes a begining of a
"definition", semicolon indicates its end.
: WASHER WASH SPIN RINSE SPIN;
: RINSE FILL AGITATE DRAIN;
: FILL FAUCETS OPEN TILL-FULL FAUCETS CLOSE;
1. "WASHER" defines 4 different cycles of the washing process in
order left-to-right.
2. "RINSE" defines 3 actions in order.
3. "FILL" definition references things (faucets) and actions (open,
close). The word TILL-FULL was created to define a "delay loop"
that ends when the tub is full.
If Forth a "definition" is called a "word". The "x-bit" memory volume
usually known as "word" is called a "cell" in Forth.
All This and ... Interactive!
$ cat > letter_F.fth <<EOF
: STAR 42 EMIT ;
: STARS 0 DO STAR LOOP ;
: MARGIN CR 30 SPACES ;
: BLIP MARGIN STAR ;
: BAR MARGIN 5 STARS ;
: F BAR BLIP BAR BLIP BLIP ;
F CR
EOF
$ forth -q letter_F.fth
*****
*
*****
*
*
A brief explanation of the script above:
1. "STAR" uses the already defined word "EMIT" to print to the screen
the character with an ascii value of 42.
2. "STARS" uses "DO" and "LOOP" to create a loop where each iteration
executes STAR.
3. "MARGIN" executes CR (carriage return) to start a new line in
outpu and print 30 space characters using SPACES.
4. "BLIP" uses "MARGIN" to print a margin of 30 spaces and then pring
a single * using STAR.
5. "BAR" does the same as "BLIP" but prints 5 asterisk characters,
not one.
6. "F" prints the capital letter F by printing a BAR then a BLIP and
so on.
7. The last line of the script runs words "F" and "CR", in that
order. "F" is defined on previous line.
The Dictionary
Each new word you define is added to Forths "dictionary" where other
words already contained, such as CR and EMIT. The process of taking the
new definition and writing them to the dictionary is called "compiling".
Execution of a word is done by activating the INTERPRET word:
1. The interpreter scands the input for tokens separated by spaces.
2. A token is looked up in the dictionary.
3. If a definition exists, it is passed to the EXECUTE word, which
executes the definition.
4. If the interpreter did not find a definition, it calls the number-
runner (NUMBER).
5. If the passed token is indeed a number, NUMBER stores it in
temporary storage for numbers.
6. If the token is not a number (and has no definition) a question
mark will be printed.
The colon character is used when defining new words, the semicolon is a
word in Forth and the following happens:
1. The interpreter find the colon in the input.
2. EXECUTE passes the new word definiotion to EXECUTE.
3. The compiler translates the definition into dictionary form and
writes is to dictionary.
4. When the compiler gets the semicolon, it stops.
5. Execution is returned to the interpreter, "ok" is returned.
Say What?
In Forth, a word is a character or group of characters that have a
definition. Here are the characters that can not be used is a naming a
word:
• return - signals end of input.
• backspace - used for input editing.
• space - used as input delimiter.
• caret - will be discussed in chapter 3.
The following words name consists of two punctuation marks. The name is
pronounced "dot-quote" and it is written as ." . This word can be used
inside definitions to type a "string" of text to the terminal.
$ cat > dot_quote_test.fth <<EOF
: GREET ." Hello, I speak Forth " CR ;
GREET
EOF
$ forth -q dot_quote_test.fth
Hello, I speak Forth
The quotation mark at the end marks the end of the text and won't be
typed. It's called a "delimiter".
The Stack: FORTH's Worksite for Arithmetic
Here is an example of doing arithmetics with Forth:
$ cat > arithmetics.fth <<EOF
3 4 + . CR
EOF
$ forth -q arithmetics.fth
7
In the example above, the numbers 3 and 4 are "pushed onto the stack", by
NUMBER , and then the operation works on the numbers.
In Forth, there is one central location where numbers are temporarily
stored before being operated on, the "stack".In the example above, NUMBER
pushes 3 onto the stack. Then NUMBER pushes 4 onto the stack. 4 goes on
the "top" of the stack with 3 "below" it. The next word in the example is
+ which can be found in the dictionary. + is defined to "take the top
two numbers off the stack, add them, and push the result back onto the
stack". The following word . (dot) is also found in the dictionary. It
has been defined to "take the number off the stack and print it at the
terminal". the numbers 3 and 4 are "pushed onto the stack", by NUMBER ,
and then the operation works on the numbers.
Postfix Power
The last example demonstrated how to calculate "3+4". The expression "3 4
+" convey the same meaning as "3+4" but is written in the "postfix"
notation. Unlike the "infix" (3+4). Forth uses postfix notation to put
numbers on the stack before executing words that need them.
Examples:
• the word + gets two numbers from the stack and adds them
• the word . gets one number from the stack and prints it to
terminal
• the word SPACES gets one number from the stack and prints that
many space-characters
• the word EMIT gets one a number that represents a character and
prints the character
when all operators are defined to work on the values that are already on
the stack, interactions between many operations remain simple even when
the program gets complex.
Forth lets you execute a word in either two ways: by simply naming it, or
by putting it in the definition of another word and naming that word.
Postfix is part of what makes this possible.
For example, lets define a word that will add the number 4 to whatever
number is on the stack:
$ cat > example.fth <<EOF
: FOUR-MORE 4 + ;
3 FOUR-MORE . CR
-10 FOUR-MORE . CR
EOF
$ forth -q example.fth
7
-6
The "4" inside the definition goes onto the stack. Then the + adds the
two numbers on the stack. Since + always works on the stack, it doesn't
matter that the "4" came from inside the definition and the "3" and "-10"
from outside.
Keep Track of Your Stack
Forth's stack is described as "last-in, first-out" (LIFO). In general,
the only accessible value at any given tiem is the top value. This can be
demonstrated by the . word. It removes the top number from the stack and
prints is:
$ cat > stack.fth <<EOF
2 4 6 8 . . . . CR
EOF
$ forth -q stack.fth
8 6 4 2
The system reads input from left to right. Numbers are added to the stack
when encountered, words are executed. Since "8" was the last number added
to the stack, it is the stacks top and will be the 1st to be removed.
If we try and remove a number from and empty stack we will encounter a
"Stack underflow!" error message, not "ok".
The dot always prints whatever is on the top, so if there is nothing on
the stack, it prints whatever is just below the stack, which is usually
zero. Only then is the error detected; the offending word (in this case
dot) is returned to the screen, followed by the "error message".
The opposite condition, when the stack completely fills up, is called
"stack overflow". The stack is so deep, however, that this condition
should never occur except when you've done something wrong.
It's important to keep track of new words' "stack effects"; that is, the
sort of numbers a word needs to have on the stack before you execute it,
and the sort of numbers it will leave on the stack afterwards.
If you maintain a list of your newly created words with their meanings as
you go, you or anyone else can easily understand the words' operations.
In Forth, such a list is called a "glossary".
To communicate stack effects in a visual way, Forth programmers
conventionally use a special stack notation in their glossaries or tables
of words. Here is the basic form:
(before -- after)
The dash separates the things that should be on the stack, before the
word is executed, from the things that will be left there afterwards. For
example, here's the stack notation for the dot word:
. (n -- )
(The letter "n" stands for "number") This shows that . expects one
number on the stack and leaves no number on the stack.
Here's the stack notation for the word + :
+ (n1 n2 -- sum)
When there is more than one n, we number them n1, n2, n3, etc.,
consecutively. The numbers 1 and 2 do not refer to position on the stack.
Stack position is indicated by the order in which the items are written.
The rightmost item, on either side of the dash, is the topmost item on
each stack. For example, in the stack notation of + , the n2 is on the
top.
Here's a list of the Forth words you've learned so far, including their
stack notations ("n" stands for number; "c" stands for character):
: xxx yyy ; ( -- ) Create a new definition with the name xxx,
consisting of word or words yyy.
CR ( -- ) Performs a carriage return and line feed
at your terminal.
SPACES (n -- ) Prints the given number of blank spaces at
your terminal.
SPACE ( -- ) Prints one blank space at your terminal.
EMIT (c -- ) Transmits a character to the output device.
." xxx" ( -- ) Prints the character string xxx at your
terminal. The " character terminates the
string.
+ (n1 n2 -- sum) Adds.
. (n -- ) Prints a number, followed by one space.
Review of Terms
• Compile - to generate a dictionary entry in computer memory from
source text (the written-out form of a definition). Distinct from
"execute".
• Dictionary - in Forth, a list of words and definitions including
both "systems" definitions (predifined) and "user" definitions
(which you invent). A dictionary resided in the computer memory in
compiled form.
• Execute - to perform. Specifically, to execute a word is to
perform the operations specified in the compiled definition of the
word.
• Extensibility - a characteristic of a computer language which
allows a programmer to add new features of modify existing ones.
• Glossary - a list of words defined in Forth, showing their stack
effects and an explanation of what they do, which serves as a
reference for programmers.
• Infix notation - the method of writing operators between the
operands they affect, as in "2 + 5"
• Input stream - the text to be read by the text interpreter. This
may be text that you have just typed in at your terminal, or it
may be text that is stored on disk.
• Interpreter - (when referring to Forth's text interpreter) to read
the input stream, then to find each word in the dictionary or,
failing that, to convert it to a number.
• LIFO - (last-in, first-out) the type of stack which Forth uses. A
can of tennis balls is a LIFO structure; the last ball you drop in
is the one you must remove first.
• Postfix notation - the method of writing oprators after the
operands they affect, as in "2 5 +" for "2 + 5". Also known as
Reverse Polish Notation.
• Stack - in Forth, a region of memory which is controlled in such a
way that data can be stored or removed in a last-in, first-out
(LIFO) fashion.
• Stack overflow - the error condition that occurs when the entire
area of memory allowed for the stack is completely filled with
data. Stack underflow - the error condition that occurs when an
operation expects a value on the stack, but there is no valid data
on the stack.
• Word - in Forth, the name of a definition.
Problems
Problem #1
Define a word called GIFT which when executed, will type out the name of
some gift. For example:
: GIFT ." BOOKENDS " ;
Now define a word called GIVER which will print out a person's first
name. Finally, define a word called THANKS which includes the new Forth
words GIFT and GIVER, and prints out a message something like this:
DEAR STEPHANIE,
THANKS FOR THE BOOKENDS.
Solution #1
$ cat > sol01.fth <<EOF
: GIFT ." BOOKENDS. " ;
: GIVER ." STEPHANIE, " ;
: THANKS CR ." DEAR " GIVER CR 4 SPACES ." THANKS FOR THE " GIFT CR ;
THANKS
EOF
$ forth -q sol01.fth
DEAR STEPHANIE,
THANKS FOR THE BOOKENDS.
Problems #2
Define a word called TEN.LESS which takes a number on the stack,
subtracts ten, and returns the answer on the stack. (Hint: you can use +
).
Solution #2
$ cat > sol02.fth <<EOF
: TEN.LESS -10 + ;
7 TEN.LESS . CR
14 TEN.LESS . CR
47 TEN.LESS . CR
EOF
$ forth -q sol02.fth
-3
4
37
Problems #3
After entering the word in Problem #1, enter a new definition for GIVER
to print someone else's name, then execute THANKS again. Can you explain
why THANKS still prints out the first giver's name?
Solution #3
$ forth
PForth V2.0.1, LE/64, built May 12 2023 10:51:31 (static)
: GIFT ." BOOKENDS. " ; ok
Stack<10>
: GIVER ." STEPHANIE, " ; ok
Stack<10>
: THANKS CR ." DEAR " GIVER CR 4 SPACES ." THANKS FOR THE " GIFT CR ; ok
Stack<10>
THANKS
DEAR STEPHANIE,
THANKS FOR THE BOOKENDS.
ok
Stack<10>
: GIVER ." CHUCK, " ; GIVER redefined.
ok
Stack<10>
THANKS
DEAR STEPHANIE,
THANKS FOR THE BOOKENDS.
ok
Stack<10>
: THANKS CR ." DEAR " GIVER CR 4 SPACES ." THANKS FOR THE " GIFT CR ; THANKS redefined.
ok
Stack<10>
THANKS
DEAR CHUCK,
THANKS FOR THE BOOKENDS.
ok
Stack<10>
When GIVER was defined again with the new name, a message was printed:
GIVER redifined. Executing THANKS again resulted in the old message.
Repeating the THANKS definition without any changes, but with a redefined
GIVER, resulted in the same GIVER redifined. message. Excuting THANKS
resulted in message with the new name.
toolsV2 yet_another_webdev(blog)