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 is to prevent 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.
Certain kinds of words do not 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 always 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 means that the text interpreter
produces a warning when encountering this word in interpretation
state; ticking the word (with '
or [']
also produces a
warning.
It is never necessary to use compile-only
(and it is not even
part of Standard 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
warning.
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
) is 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 is 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
.