Next: Forth is written in Forth, Previous: Your first definition, Up: Introduction
Now we're going to take another look at the definition of add-two
from the previous section. From our knowledge of the way that the text
interpreter works, we would have expected this result when we tried to
define add-two
:
: add-two 2 + . ;<RET> *the terminal*:4: Undefined word : >>>add-two<<< 2 + . ;
The reason that this didn't happen is bound up in the way that :
works. The word :
does two special things. The first special
thing that it does prevents the text interpreter from ever seeing the
characters add-two
. The text interpreter uses a variable called
>IN
(pronounced “to-in”) to keep track of where it is in the
input line. When it encounters the word :
it behaves in exactly
the same way as it does for any other word; it looks it up in the name
dictionary, finds its xt and executes it. When :
executes, it
looks at the input buffer, finds the word add-two
and advances the
value of >IN
to point past it. It then does some other stuff
associated with creating the new definition (including creating an entry
for add-two
in the name dictionary). When the execution of :
completes, control returns to the text interpreter, which is oblivious
to the fact that it has been tricked into ignoring part of the input
line.
Words like :
– words that advance the value of >IN
and so
prevent the text interpreter from acting on the whole of the input line
– are called parsing words.
The second special thing that :
does is change the value of a
variable called state
, which affects the way that the text
interpreter behaves. When Gforth starts up, state
has the value
0, and the text interpreter is said to be interpreting. During a
colon definition (started with :
), state
is set to -1 and
the text interpreter is said to be compiling.
In this example, the text interpreter is compiling when it processes the
string “2 + . ;
”. It still breaks the string down into
character sequences in the same way. However, instead of pushing the
number 2
onto the stack, it lays down (compiles) some magic
into the definition of add-two
that will make the number 2
get
pushed onto the stack when add-two
is executed. Similarly,
the behaviours of +
and .
are also compiled into the
definition.
One category of words don't get compiled. These so-called immediate
words get executed (performed now) regardless of whether the text
interpreter is interpreting or compiling. The word ;
is an
immediate word. Rather than being compiled into the definition, it
executes. Its effect is to terminate the current definition, which
includes changing the value of state
back to 0.
When you execute add-two
, it has a run-time effect that is
exactly the same as if you had typed 2 + . <RET>
outside of a
definition.
In Forth, every word or number can be described in terms of two properties:
Numbers are always treated in a fixed way:
Words don't behave in such a regular way, but most have default semantics which means that they behave like this:
The actual behaviour of any particular word can be controlled by using
the words immediate
and compile-only
when the word is
defined. These words set flags in the name dictionary entry of the most
recently defined word, and these flags are retrieved by the text
interpreter when it finds the word in the name dictionary.
A word that is marked as immediate has compilation semantics that are identical to its interpretation semantics. In other words, it behaves like this:
Marking a word as compile-only prohibits the text interpreter from
performing the interpretation semantics of the word directly; an attempt
to do so will generate an error. It is never necessary to use
compile-only
(and it is not even part of ANS Forth, though it is
provided by many implementations) but it is good etiquette to apply it
to a word that will not behave correctly (and might have unexpected
side-effects) in interpret state. For example, it is only legal to use
the conditional word IF
within a definition. If you forget this
and try to use it elsewhere, the fact that (in Gforth) it is marked as
compile-only
allows the text interpreter to generate a helpful
error message rather than subjecting you to the consequences of your
folly.
This example shows the difference between an immediate and a non-immediate word:
: show-state state @ . ; : show-state-now show-state ; immediate : word1 show-state ; : word2 show-state-now ;
The word immediate
after the definition of show-state-now
makes that word an immediate word. These definitions introduce a new
word: @
(pronounced “fetch”). This word fetches the value of a
variable, and leaves it on the stack. Therefore, the behaviour of
show-state
is to print a number that represents the current value
of state
.
When you execute word1
, it prints the number 0, indicating that
the system is interpreting. When the text interpreter compiled the
definition of word1
, it encountered show-state
whose
compilation semantics are to append its interpretation semantics to the
current definition. When you execute word1
, it performs the
interpretation semantics of show-state
. At the time that word1
(and therefore show-state
) are executed, the system is
interpreting.
When you pressed <RET> after entering the definition of word2
,
you should have seen the number -1 printed, followed by “
ok
”. When the text interpreter compiled the definition of
word2
, it encountered show-state-now
, an immediate word,
whose compilation semantics are therefore to perform its interpretation
semantics. It is executed straight away (even before the text
interpreter has moved on to process another group of characters; the
;
in this example). The effect of executing it are to display the
value of state
at the time that the definition of
word2
is being defined. Printing -1 demonstrates that the
system is compiling at this time. If you execute word2
it does
nothing at all.
Before leaving the subject of immediate words, consider the behaviour of
."
in the definition of greet
, in the previous
section. This word is both a parsing word and an immediate word. Notice
that there is a space between ."
and the start of the text
Hello and welcome
, but that there is no space between the last
letter of welcome
and the "
character. The reason for this
is that ."
is a Forth word; it must have a space after it so that
the text interpreter can identify it. The "
is not a Forth word;
it is a delimiter. The examples earlier show that, when the string
is displayed, there is neither a space before the H
nor after the
e
. Since ."
is an immediate word, it executes at the time
that greet
is defined. When it executes, its behaviour is to
search forward in the input line looking for the delimiter. When it
finds the delimiter, it updates >IN
to point past the
delimiter. It also compiles some magic code into the definition of
greet
; the xt of a run-time routine that prints a text string. It
compiles the string Hello and welcome
into memory so that it is
available to be printed later. When the text interpreter gains control,
the next word it finds in the input stream is ;
and so it
terminates the definition of greet
.