Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Conklin E.K.Forth programmer's handbook.2000.pdf
Скачиваний:
321
Добавлен:
23.08.2013
Размер:
2.04 Mб
Скачать

Forth Programmer’s Handbook

characters between the S" (the name of the executing word) and the delimiting ". All spaces are included, even leading spaces, because in this case a space is not the delimiter. COUNT converts the counted string address to a character string address and length; these two parameters are passed to SLITERAL, which compiles the string into the definition. The POSTPONE command preceding SLITERAL causes SLITERAL’s compilation behavior to occur rather than its execution behavior. When ALARM-MESSAGE executes, the run-time behavior of SLITERAL returns the address and count of the stored message Too Hot! for TYPE to display.

Glossary

 

 

SLITERAL

( — c-addr u )

String

Compile into a definition a string that is characterized by the starting address and length on the stack at compile time. At run time, return the string’s address and length to the stack. In general, the run-time address will not be the same as the compile-time address. “S-literal”

References S" and C", Section 4.1.5.2

Defining words, Section 4.2

String comparisons, Section 2.3.4

POSTPONE, Section 4.4.1

4.4 COMPILER DIRECTIVES

A compiler directive in Forth is a word that is executed at compile time, i.e., during a : compilation. Many such words exist: DO; LOOP and +LOOP; BEGIN and UNTIL; IF, ELSE, and THEN; literals; and others. It is rare that a user needs to add compiler directives; it is not difficult, but requires mastery of

IMMEDIATE and POSTPONE.

Some compiler directives have only compile-time behavior (such as BEGIN). Other directives need to perform some actions at compile time and other actions at run time. For example, at compile time DO must mark the position to which LOOP or +LOOP will return; at run time, it must push the index and limit for the loop onto the return stack.

158 The Forth Interpreter and Compiler

Forth Programmer’s Handbook

The way these functions are managed is to define (usually with CODE) the runtime activity as a separate word and then to have the compile-time definition, which is IMMEDIATE, compile the address of the run-time code (in addition to its other activities).

4.4.1 Making Compiler Directives

The word IMMEDIATE is used directly after a definition. It signals the compiler that this definition is to be executed at compile time (when all non-imme- diate words are being compiled). This is done by setting the word’s precedence bit (usually the high-order bit in the count field).

The word POSTPONE is used inside IMMEDIATE definitions. It has the opposite function from IMMEDIATE. Used in the form POSTPONE <name>, it causes the compilation behavior of name, rather than the execution behavior of name, to be added to the current definition. POSTPONE can be used with IMMEDIATE words (such as compiler directives) and with normal, non-immediate words, as shown in the following examples.

Consider a common definition of BEGIN:

: BEGIN

HERE ; IMMEDIATE

This definition of BEGIN is simply an IMMEDIATE version of HERE. The difference is that, when HERE appears in a normal definition, its address is compiled; it will push the value of the dictionary pointer onto the stack when the word that contains HERE is executed. BEGIN, on the other hand, compiles nothing; it pushes the dictionary pointer onto the stack at compile time, to serve as the address UNTIL (or a similar structure word) needs to compile a conditional return to that location.

Structure words such as IF provide classic examples of the use of POSTPONE. Most of these words have a run-time behavior that we usually think of. For example, IF checks the truth of the top stack item, and conditionally branches. But there is also a compile-time behavior for IF, which is to compile a reference to the run-time behavior and also to provide for whatever branching is associated with the structure.

If the run-time behavior is defined as a word called (IF), we could define the

The Forth Interpreter and Compiler 159

Forth Programmer’s Handbook

compiler directive IF this way:

: IF ( -- addr )

POSTPONE (IF) HERE 0 , ; IMMEDIATE

When executed during compilation of a word, this IF will compile the reference to (IF) and leave a one-cell space in the definition, placing the address of that cell on the stack, as shown in Figure 14. Subsequent execution of ELSE or THEN will resolve the branch by storing an appropriate offset in that space.

(rest of head) IF

Code Field

xt

xt

xt

literal 0

xt

xt

POSTPONE

(IF) HERE

,

EXIT

 

 

This phrase

Addr left

This phrase

 

 

 

compiles...

 

 

 

compiles...

 

 

 

 

 

on stack

 

 

 

(head, etc.)

(first part of definition)

xt

0 (rest of definition)

 

(IF)

 

 

 

 

 

 

 

 

 

 

 

 

 

Addr on stack

Figure 14. Compile-time action of IF

As a second example, suppose you often use the phrase … ?DUP IF … in definitions, and want to create a word ?IF that performs both functions. Here is how ?IF would need to be defined:

: ?IF

POSTPONE ?DUP POSTPONE IF ; IMMEDIATE

?IF is an IMMEDIATE word because it needs to set up a conditional branch at compile time. However, we do not want the run-time behavior for ?DUP and IF to execute at compile time; instead, we want these words’ compilation behaviors to occur. Hence, each must be preceded by POSTPONE. ?DUP is nonimmediate, and IF is IMMEDIATE, but the syntax for POSTPONE is identical.

POSTPONE is very similar to ['] except, whereas ['] compiles as a literal the execution token of the word that follows (so the address will be pushed onto the stack at run time), POSTPONE lays down a pointer to the execution token, so the word can be executed by the Forth virtual machine.

160 The Forth Interpreter and Compiler

 

 

 

 

Forth Programmer’s Handbook

 

 

 

 

 

Glossary

 

 

 

 

IMMEDIATE

( — )

Core

 

 

Make the most recent definition an immediate word. When the compiler

 

 

encounters an immediate word it causes it to execute at that time rather than

 

 

compiling a reference to it.

 

POSTPONE <name>

( — )

Core

 

 

At compile time, add the compilation behavior of name, rather than its execution

 

 

behavior, to the current definition. Usually used in IMMEDIATE definitions.

 

 

Colon definitions, Section 4.2.4

 

References

 

 

 

DO ... LOOP, program structure words, Sections 2.5, 5.8

Literals, Section 4.3.5

The Forth compiler, Section 4.3.3 Use of BEGIN, Section 2.5.1 Word lists, Section 4.6

['], Section 4.3.6

Compiler directives, Section 4.4

4.4.2 The Control-flow Stack and Custom Compiling Structures

The standard branching constructs in Forth (IF ELSE THEN, BEGIN UNTIL, BEGIN AGAIN, and DO LOOP) are examples of control-flow words. In direct management of control flow, every branch must terminate at some destination. An origin (abbreviated orig in Table 8) is the location of the branch itself; a destination (abbreviated dest in Table 8) is where control will continue if the branch is taken. A natural implementation to manage control flow uses a stack to remember the origin of forward branches and the destination of backward branches. This is the control-flow stack in Forth. How it is implemented is system dependent, and generally is not of concern to the user; in virtually all implementations, it is the data stack at compile time.

This section describes some additional primitive words which directly access the control-flow stack. With these words, a programmer can create branching structures of any needed degree of complexity. The abilities required are compilation of forward and backward conditional and unconditional branches,

The Forth Interpreter and Compiler 161

Forth Programmer’s Handbook

and compile-time management of branch origins and destinations. These are provided by just three new words: AHEAD, CS-PICK, and CS-ROLL. Table 8 summarizes the compilation behavior of these words and of the other basic Forth words that affect control flow.

Table 8: Summary of compile-time branch words

Word

Control-flow stack

Function

IF

( — orig )

Marks the origin of a

 

 

forward conditional branch.

THEN

( orig — )

Resolves the branch originated by

 

 

IF or AHEAD.

BEGIN

( — dest )

Marks the destination of a backward branch.

AGAIN

( dest — )

Resolves a backward unconditional branch.

UNTIL

( dest — )

Resolves a backward conditional branch.

AHEAD

( — orig )

Marks the origin of a

 

 

forward unconditional branch.

CS-PICK

( i*x u — i*x xu )

Copies item on control-flow stack.

CS-ROLL

( i*x u — (i-1)*x xu )

Reorders items on control-flow stack.

All other branching words—such as WHILE, REPEAT, and ELSE—can be defined in terms of the primitive words in Table 8. For example:

: ELSE ( addr1 -- addr2 )

\ Resolve IF, set up for THEN

POSTPONE AHEAD

\ Set

up forward branch

1 CS-ROLL

\

Get

addr of IF’s branch

POSTPONE THEN ;

\

Resolve IF’s branch

IMMEDIATE

 

 

 

In this definition, the phrase POSTPONE AHEAD marks the origin of an unconditional branch (around the “false clause”) to be taken at the end of the “true clause.” This will later be resolved by the THEN which occurs at the end of the IF statement. Since POSTPONE AHEAD places one item on the control-flow stack, the phrase 1 CS-ROLL (the equivalent of SWAP) is needed to restore the previous origin which was placed there by the IF. Next, POSTPONE THEN compiles the branch resolution for this origin, providing entry to the “false clause” following ELSE if the conditional branch at IF was taken.

162 The Forth Interpreter and Compiler

Соседние файлы в предмете Электротехника