Next: , Previous: Your first definition, Up: Introduction


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:

     : 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.