4.4 How does that work?

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:

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:

Its interpretation semantics describe how it will behave when the
text interpreter encounters it in interpret state. The
interpretation semantics of a word are represented by an execution
token.

Its compilation semantics describe how it will behave when the
text interpreter encounters it in compile state. The compilation
semantics of a word are represented in an implementation-dependent way;
Gforth uses a compilation token.

Numbers are always treated in a fixed way:

When the number is interpreted, its behaviour is to push the
number onto the stack.

When the number is compiled, a piece of code is appended to the
current definition that pushes the number when it runs. (In other words,
the compilation semantics of a number are to postpone its interpretation
semantics until the run-time of the definition that it is being compiled
into.)

Words don't behave in such a regular way, but most have default
semantics which means that they behave like this:

The interpretation semantics of the word are to do something useful.

The compilation semantics of the word are to append its
interpretation semantics to the current definition (so that its
run-time behaviour is to do something useful).

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:

The interpretation semantics of the word are to do something useful.

The compilation semantics of the word are to do something useful
(and actually the same thing); i.e., it is executed during compilation.

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:

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 stateat the time that the definition ofword2is 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.