Chapter 1: Stack Manipulation

Handling Different Data Types

Words in StrongForth always apply to specific stack patterns. If, for example, you want to duplicate the item on top of the data stack, you would apply the word DUP to the contents of the stack:

56 .S
UNSIGNED  OK
DUP .S
UNSIGNED UNSIGNED  OK
. .
56 56  OK

In this example, DUP is applied to an item of data type UNSIGNED. This means, StrongForth has to provide a version of DUP that accepts items of this data type. Let's try the same operation with a different data type:

CHAR G .S
CHARACTER  OK
DUP . .
GG OK

From this example it can be concluded that StrongForth provides a version of DUP that can be applied to items of data type CHARACTER. Given the whole lot of predefined data types, does this mean StrongForth provides one overloaded version of DUP for each data type? Even worse, does it mean that you have to define a new version of DUP for each newly defined data type? No, certainly not. In the above examples, only one version of DUP is used, a version that can be applied to both UNSIGNED and CHARACTER data types. Here's the stack diagram of this version of DUP:

DUP ( SINGLE -- 1ST 1ST )

DUP expects on the stack an item of data type SINGLE or any of this data type's direct and indirect subtypes. Since UNSIGNED and CHARACTER both are subtypes of data type INTEGER, and INTEGER is a subtype of SINGLE, this version of DUP accepts both data types.

But what about the right side of DUP's stack diagram? 1ST is not a data type. It actually is a representative for a data type that is substituted by an actual data type depending on the usage of the word. In the case of DUP, using 1ST two times on the right side of the stack diagram means that DUP produces two stack items of the same data type as the first input parameter. Looking again at the two examples above, you can see that the same version of DUP produces two items of data type UNSIGNED when applied to an item of data type UNSIGNED, and it produces two items of data type CHARACTER when applied to an item of data type CHARACTER. If DUP is defined without using 1ST; strange things may happen:

: DUP2 ( SINGLE -- SINGLE SINGLE ) DUP ;
 OK
CHAR H DUP2 .S
SINGLE SINGLE  OK
. .
72 72  OK

Instead of producing two items of data type CHARACTER, the newly defined word DUP2 leaves two items of data type SINGLE on the stack. The information that you're dealing with a character that has to be treated as such, is lost.

As stated above, DUP expects an item of data type SINGLE or one of it's direct or indirect subtypes on the stack. This includes a large number of predefined data types, but not all. What do you have to do to duplicate a double number? Let's try it the ANS Forth way:

100000. 2DUP

100000. 2DUP ? undefined word
UNSIGNED-DOUBLE

Sorry, that doesn't work. StrongForth does not know 2DUP. Why? Because 2DUP is not required. Since StrongForth is capable of overloading words, it is much more natural to provide a second version of DUP that applies to double numbers, or, more precisely, to items of data type DOUBLE and it's direct and indirect subtypes:

DUP ( DOUBLE -- 1ST 1ST )

The above example can now be fixed:

100000. DUP
 OK
. .
100000 100000  OK

The replacement of 2DUP with an overloaded version of DUP is very typical for StrongForth. Actually, most ANS Forth words dealing with double numbers, like D+ and DABS, are replaced by overloaded versions of the respective words used for single numbers, since the semantics is supposed to be the same from a high-level point of view.

However, you may note that the second version of DUP is not a full replacement for 2DUP, because DUP can not be used to duplicate two single-cell items. This restriction will be discussed later in this chapter.

With these two versions of DUP, all data types can be duplicated. In fact, no other versions of DUP are provided by StrongForth:

WORDS DUP
DUP ( DOUBLE -- 1ST 1ST )
DUP ( SINGLE -- 1ST 1ST )
 OK

The Complete Set

After having discussed the two overloaded versions of DUP, it's now time to have a look at the other stack manipulation words:

Like DUP, DROP expects one item on top of the stack. To be able to apply DROP to all data types, two overloaded versions are required:

DROP ( SINGLE -- )
DROP ( DOUBLE -- )

The latter version is StrongForth's replacement for the ANS Forth word 2DROP.

Now let's have a look at SWAP and OVER. It might seem obvious to provide again two versions for each of these words, one for single-cell items and one for double-cell items:

SWAP ( SINGLE SINGLE -- 2ND 1ST )
SWAP ( DOUBLE DOUBLE -- 2ND 1ST )
OVER ( SINGLE SINGLE -- 1ST 2ND 1ST )
OVER ( DOUBLE DOUBLE -- 1ST 2ND 1ST )

Please note the data type representatives on the right side of the stack diagrams. In the case of SWAP, 2ND will be replaced by the actual data type of the second parameter on the left side of the stack diagram, which is the data type of the item on top of the stack before SWAP is executed. 1ST means that the data type on top of the stack after executing SWAP will be identical to the data type of the first item on the left side of the stack diagram. This guarantees that SWAP doesn't change the data types of the swapped items, as can be seen in the following example:

-100 TRUE .S
SIGNED FLAG  OK
SWAP .S
FLAG SIGNED  OK
. .
-100 TRUE  OK

With OVER, things are similar. Here's an example for double-cell items:

200000. +7375551. .S
UNSIGNED-DOUBLE SIGNED-DOUBLE  OK
OVER .S
UNSIGNED-DOUBLE SIGNED-DOUBLE UNSIGNED-DOUBLE  OK
. . .
200000 7375551 200000  OK

Obviously, the double-cell versions of SWAP and OVER replace the ANS Forth words 2SWAP and 2OVER, respectively. However, there's still something missing. What if you want to swap a single-cell and a double-cell item? Let's try it:

123456789. CHAR K .S
UNSIGNED-DOUBLE CHARACTER  OK
SWAP .S
CHARACTER UNSIGNED-DOUBLE  OK
. .
123456789 K OK

No problem. But neither of the above two versions of SWAP is capable of doing that. For SWAP to be complete, two additional overloaded versions are required:

SWAP ( DOUBLE SINGLE -- 2ND 1ST )
SWAP ( SINGLE DOUBLE -- 2ND 1ST )

For these two words, there seems to be no match in ANS Forth. Well, this is not quite true. SWAP ( SINGLE DOUBLE -- 2ND 1ST ) performs nothing else but the ANS Forth word ROT. For SWAP ( DOUBLE SINGLE -- 2ND 1ST ), you would have to write ROT ROT in ANS Forth, which is sometimes defined as -ROT. However, from a high-level point of view, calling this operation SWAP is much clearer, since using ROT implies that there are three items on the stack. Interpreting one double number like 123456789. as two single-cell items is definitely not intuitive. Think about a program change, where you have to replace a single number variable with a double number variable, because it has turned out that the numeric range of a single number is insufficient. You would have to check all occurrences of the variable to make sure it is handled correctly. In StrongForth, this is not necessary. You just change the data type of the variable, and StrongForth's compiler does the rest.

In total, StrongForth provides four overloaded versions each for SWAP and OVER. The same applies to NIP and TUCK:

WORDS SWAP
SWAP ( DOUBLE DOUBLE -- 2ND 1ST )
SWAP ( DOUBLE SINGLE -- 2ND 1ST )
SWAP ( SINGLE DOUBLE -- 2ND 1ST )
SWAP ( SINGLE SINGLE -- 2ND 1ST )
 OK
WORDS OVER
OVER ( DOUBLE DOUBLE -- 1ST 2ND 1ST )
OVER ( DOUBLE SINGLE -- 1ST 2ND 1ST )
OVER ( SINGLE DOUBLE -- 1ST 2ND 1ST )
OVER ( SINGLE SINGLE -- 1ST 2ND 1ST )
 OK
WORDS NIP
NIP ( DOUBLE DOUBLE -- 2ND )
NIP ( DOUBLE SINGLE -- 2ND )
NIP ( SINGLE DOUBLE -- 2ND )
NIP ( SINGLE SINGLE -- 2ND )
 OK
WORDS TUCK
TUCK ( DOUBLE DOUBLE -- 2ND 1ST 2ND )
TUCK ( DOUBLE SINGLE -- 2ND 1ST 2ND )
TUCK ( SINGLE DOUBLE -- 2ND 1ST 2ND )
TUCK ( SINGLE SINGLE -- 2ND 1ST 2ND )
 OK

What about ROT? Since ROT expects three items on the stack, a total of 2³ = 8 combinations of single-cell and double-cell items has to be taken care of:

WORDS ROT
ROT ( DOUBLE DOUBLE DOUBLE -- 2ND 3RD 1ST )
ROT ( DOUBLE DOUBLE SINGLE -- 2ND 3RD 1ST )
ROT ( DOUBLE SINGLE DOUBLE -- 2ND 3RD 1ST )
ROT ( DOUBLE SINGLE SINGLE -- 2ND 3RD 1ST )
ROT ( SINGLE DOUBLE DOUBLE -- 2ND 3RD 1ST )
ROT ( SINGLE DOUBLE SINGLE -- 2ND 3RD 1ST )
ROT ( SINGLE SINGLE DOUBLE -- 2ND 3RD 1ST )
ROT ( SINGLE SINGLE SINGLE -- 2ND 3RD 1ST )
 OK

That's quite a lot. Fortunately, there are no stack manipulation words that expect more than 3 items on the stack. However, the availability of a complete set of overloaded versions of ROT makes this word much more versatile than its ANS Forth equivalents. Only the first and the last overloaded version of ROT actually have matches in ANS Forth, namely 2ROT and ROT. For the other 6 overloaded versions, more or less tricky and error-prone constructions have to be used in ANS Forth.

More on Double Numbers

In ANS Forth, the stack image of a double number is clearly defined. A double number on top of the stack is stored in two cells, and the cell on top of the stack contains the higher part of the double number. This means, entering

1234.

in ANS Forth is equivalent to entering

1234 0

You can convert a double number to a single number by simply DROPping its cell containing its most significant part, and you can convert an unsigned single number to a double number by pushing 0 onto the stack. To convert a signed single number into a double number, you have to use S>D to make sure the sign is extended correctly.

In StrongForth, the stack image of double numbers is implementation dependent. If the hardware already supports operations that load and store double numbers, using these operations may result in a performance gain. As a consequence, making any assumption on the order of the two cells a double number consist of is dangerous. The resulting code may not be portable.

Since a double number may not be interpreted as two single numbers, StrongForth provides a set of four low-level words that perform the necessary conversions. This is a direct consequence of strong static data typing. Similarly to ANS Forth, StrongForth uses the word S>D to convert single numbers to double numbers. However, S>D must also be used to convert an unsigned single number into a double number. Just pushing 0 onto the stack will not make a double number out of a single number:

1234 0 .S
UNSIGNED UNSIGNED  OK
. .
0 1234  OK
1234 S>D .S
DOUBLE  OK
.
1234  OK

Always using S>D for converting a single number into a double number makes the intention of the programmer much clearer than pushing 0 onto the stack. Actually, StrongForth provides two overloaded versions of S>D:

WORDS S>D
S>D ( SIGNED -- SIGNED-DOUBLE )
S>D ( SINGLE -- DOUBLE )
 OK

The first one is applied to signed numbers, because it performs a sign extension. The second one applies to all other data types, performing only a zero extension. Note that the second version of S>D always returns an item of data type DOUBLE on the stack. Even if it converts an item of data type UNSIGNED, the result will not be an UNSIGNED-DOUBLE, because StrongForth does not know of any semantic relations between single number data types and corresponding double number data types. This behaviour definitely makes S>D a low-level word. However, you can always define your private versions of S>D, that work for specific data types:

: S>D ( INTEGER -- INTEGER-DOUBLE )
  S>D CAST INTEGER-DOUBLE ;
 OK
: S>D ( UNSIGNED -- UNSIGNED-DOUBLE )
  S>D CAST UNSIGNED-DOUBLE ;
 OK
For example, the second overloaded version will convert items of data type UNSIGNED and all its subtypes to items of data type UNSIGNED-DOUBLE. The version for items of data type INTEGER has to be defined first, because otherwise it would hide the version for items of data type UNSIGNED in the dictionary. On the other hand, the word CAST provides a high-level way to convert single to double numbers, which makes additional overloaded versions of S>D obsolete.

Now let's have a look at the other direction. In ANS Forth, converting a double number into a single number can be performed by just DROPping the cell containing its most significant part. In StrongForth, this does not work, because DROP applied to a double number will drop the complete double number, not only its most significant part. However, even ANS Forth provides the word D>S to make the conversion more obvious. D>S also exists in StrongForth. Since it's not necessary to distinguish between signed and unsigned numbers, one version suffices. Here's an example:

WORDS D>S
D>S ( DOUBLE -- SINGLE )
 OK
1234. .S
UNSIGNED-DOUBLE  OK
D>S .S
SINGLE  OK
.
1234  OK

Again, D>S is a low-level word. It always leaves an item of data type SINGLE on the stack, independently of what it found on the stack.

Even though the stack image of a double number is undefined in StrongForth, there's still the fact that each double number is composed of two cells. To be able to access both parts of a double number, or generally to access both cells of a double-cell item, StrongForth provides two additional low-level words: SPLIT and MERGE.

SPLIT splits a double-cell item into two single-cell items in such a way, that in case of a double number the cell containing the most significant part is on top of the stack. Even on machines that usually store the least significant part at the lower address, this behaviour is guaranteed.

WORDS SPLIT
SPLIT ( DOUBLE -- SINGLE SINGLE )
 OK
HEX
 OK
12345678. SPLIT .S
SINGLE SINGLE  OK
. .
1234 5678  OK
DECIMAL
 OK

Note that D>S only returns the least significant part of a double number, while SPLIT returns both parts. Since interpreting the two cells that constitute a double-cell item is totally out of control of StrongForth's type system, SPLIT should be used with care.

MERGE performs the reverse operation. It expects two single-cell items on the stack and merges them into a double-cell item. The single-cell item on top of the stack is always going to be the most significant part of the resulting double-cell item, if the latter one will be interpreted as a double number.

WORDS MERGE
MERGE ( SINGLE SINGLE -- DOUBLE )
 OK
HEX
 OK
2222 1111 MERGE .
11112222  OK
DECIMAL
 OK

What's missing?

?DUP

StrongForth requires that each word has an unambiguous stack diagram. The stack diagram describes what a word expects to be on the stack before it is executed, and what it leaves on stack after execution. For example, the word

D>S ( DOUBLE -- SINGLE)

expects an item of data type DOUBLE on top of the stack, which it replaces with an item of data type SINGLE. Although ANS Forth is an untyped language, its glossary specifies a stack diagram for each word. Most of these stack diagrams are definite in the sense that the stack effect is already known at compile time. However, for some ANS Forth words, the stack effect is not known at compile time. It rather depends on the value of one or more parameters at runtime. For example, the stack diagram of ?DUP is

( x -- 0 | x x )

So, the stack effect of ?DUP is either

( x -- 0 )

or

( x -- x x )

depending on the value of x at the time ?DUP is executed. How can an ambiguous stack diagram like that be implemented in StrongForth? It can not. Since StrongForth adheres to strong static typing, the compiler requires that the stack effect of each word is known at compile time.

Is this a problem? Let's have a look at a typical application of ?DUP. In many cases, ?DUP is used immediately preceding IF, as in the following example:

: SPACES ( n -- )
  0 MAX ?DUP IF 0 DO SPACE LOOP THEN ;

(Note that this definition is ANS Forth code; it will not work in StrongForth.) Here, ?DUP is used as a shortcut to avoid the ELSE branch. Alternatively, the above example could be written as

: SPACES ( UNSIGNED -- )
  0 MAX DUP IF 0 DO SPACE LOOP ELSE DROP THEN ;

Since ?DUP is not used here, this definition can also be compiled in StrongForth. Actually, using ?DUP can always be avoided by either spending an additional IF clause, by adding an ELSE branch to an existing IF clause, or by just inserting DROP. If you're not satisfied, consider using the following definition of ?IF, which replaces the ANS Forth sequence ?DUP IF if no ELSE branch is required:

: ?IF ( -- ORIGIN )
  POSTPONE DUP
  POSTPONE 0=
  POSTPONE IF
  POSTPONE DROP
  POSTPONE ELSE ; IMMEDIATE

?IF inverts the condition and moves the contents of the IF branch into the ELSE branch. The IF branch is now used to drop the null value. Here's how ?IF can be used in our implementation of SPACES:

: SPACES ( UNSIGNED -- )
  0 MAX ?IF 0 DO SPACE LOOP THEN ;

Since UNSIGNED values are not negative, there's an even simpler definition of SPACES in StrongForth:

: SPACES ( UNSIGNED -- )
  0 ?DO SPACE LOOP ;

However, please note that all StrongForth versions of SPACES that were presented in this section are semantically not ANS compatible, because they apply to unsigned numbers only. In ANS Forth, SPACES expects a signed number on the stack.

PICK and ROLL

Now, let's have a look at two other ANS Forth words with ambiguous stack diagrams:

PICK ( xu ... x1 x0 u -- xu ... x1 x0 xu )
ROLL ( xu xu-1 ... x0 u -- xu-1 ... x0 xu )

Can you see why these two words have ambiguous stack diagrams? Yes, the problem is that the compiler is not able to find out the data type of xu. The data type of xu depends on u, and the value of u is not known at compile time. Of course, in many cases PICK and ROLL will be preceded by a numeric constant as in the following ANS Forth example:

: EXAMPLE ( c-addr u d -- char flag )
  3 ROLL ( u d c-addr ) ... ;

Though the stack effect is obvious to the programmer, it is not obvious to the compiler. The compiler doesn't know that ROLL will always find 3 on top of the stack. Well, this problem might be solved by implementing a better compiler. But if the position parameter is not a constant, even a very clever compiler will have to give up. To cover at least some of the cases where the position parameter is constant, it might be tempting to define a set of words like 2PICK, 3PICK, 3ROLL etc. Note that 1 PICK is semantically the same as OVER, and 2 ROLL has the same effect as ROT. However, you have seen that the number of overloaded versions of ROT is already rather high. 2PICK would take 3 parameters, so you'd need 8 overloaded versions as well:

2PICK ( SINGLE SINGLE SINGLE -- 1ST 2ND 3RD 1ST )
2PICK ( SINGLE SINGLE DOUBLE -- 1ST 2ND 3RD 1ST )
2PICK ( SINGLE DOUBLE SINGLE -- 1ST 2ND 3RD 1ST )
2PICK ( SINGLE DOUBLE DOUBLE -- 1ST 2ND 3RD 1ST )
2PICK ( DOUBLE SINGLE SINGLE -- 1ST 2ND 3RD 1ST )
2PICK ( DOUBLE SINGLE DOUBLE -- 1ST 2ND 3RD 1ST )
2PICK ( DOUBLE DOUBLE SINGLE -- 1ST 2ND 3RD 1ST )
2PICK ( DOUBLE DOUBLE DOUBLE -- 1ST 2ND 3RD 1ST )

For 3PICK and 3ROLL, already 16 overloaded versions would be required to apply these words to all combinations of SINGLE and DOUBLE parameters. This can't be a reasonable solution.

On the other hand, if you ever come into a situation where you think you need PICK or ROLL, you should consider changing your code anyway. Since accessing the fourth or fifth item on the stack usually makes the code rather obscure, using PICK and ROLL is discouraged. In StrongForth, this recommendation can be somewhat relaxed. If you have a second look at the above example using ROLL, you'll probably find out that 3 ROLL is not required if the code is ported to StrongForth. Although EXAMPLE uses four stack cells, these four cells constitute only three stack items, and this means an overloaded version of ROT will do:

: EXAMPLE ( DATA -> CHARACTER UNSIGEND SIGNED-DOUBLE -- CHARACTER FLAG )
  ROT ( UNSIGEND SIGNED-DOUBLE DATA -> CHARACTER ) ... ;

Thus, the availability of 8 overloaded versions of ROT proves to be handy. Whenever you want to bring the third item on the stack to the top, ROT will do the job, no matter whether you have to skip 2, 3 or 4 cells. This makes the necessity for ROLL in your code even more unlikely. Another example demonstrates that 2 PICK can be replaced by OVER in some cases. The ANS Forth definition

: EXAMPLE2 ( c-addr u d -- char flag )
  2 PICK ( c-addr d u ) ... ;

can in StrongForth be written as

: EXAMPLE2 ( DATA -> CHARACTER UNSIGEND SIGNED-DOUBLE -- CHARACTER FLAG )
  OVER ( DATA -> CHARACTER SIGNED-DOUBLE UNSIGEND SIGNED-DOUBLE ) ... ;

To summarise it all: StrongForth does not provide PICK and ROLL. In many cases, the overloaded versions of SWAP, ROT and OVER will do. In all other cases, you have to revise your code to avoid any deep or random stack access.

2DUP, 2DROP, 2SWAP, 2OVER and 2ROLL

It was already mentioned that overloading DUP, DROP, SWAP, OVER and ROT for double-cell items makes the ANS Forth words 2DUP, 2DROP, 2SWAP, 2OVER and 2ROT obsolete. This is only partly true, because these words all have two semantics in ANS Forth. Consider the word 2DUP. When the top of the stack contains a double-cell item, its semantic would be to duplicate this item:

2DUP ( d -- d d )

On the other side, if there are two single-cell items on top of the stack, 2DUP will duplicate this pair of single-cell items:

2DUP ( x1 x2 -- x1 x2 x1 x2 )

In ANS Forth, these two interpretations of 2DUP are identical, because each double number is a pair of single numbers. In StrongForth, the two interpretations are completely different. The first one is just an overloaded version of DUP, while the second one is a new word, which expects two items on the stack instead of only one. Of course, it is possible to define 2DUP in StrongForth:

: 2DUP ( SINGLE SINGLE -- 1ST 2ND 1ST 2ND )
  OVER OVER ;

But what if you want to duplicate a pair consisting of one single-cell and one double-cell item? Consequently, StrongForth would have to provide a complete set of overloaded versions of 2DUP, just as it does for other stack manipulation words:

2DUP ( SINGLE SINGLE -- 1ST 2ND 1ST 2ND )
2DUP ( SINGLE DOUBLE -- 1ST 2ND 1ST 2ND )
2DUP ( DOUBLE SINGLE -- 1ST 2ND 1ST 2ND )
2DUP ( DOUBLE DOUBLE -- 1ST 2ND 1ST 2ND )

No problem so far. Now, what about the other words? 2DROP would be similar to 2DUP: Four overloaded versions are required. 2SWAP and 2OVER expect four items on the stack, 2ROT expects six items:

2SWAP ( x1 x2 x3 x4 -- x3 x4 x1 x2 )
2OVER ( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 )
2ROT  ( x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2 )

Providing the complete set of overloaded versions for these words would result in 16 or even 64 different versions, respectively! This is definitely too much. As already stated before, handling four or more stack items simultaneously is not considered being good programming style in Forth, and this is certainly one of the reasons why 2SWAP, 2OVER and 2ROT are usually only applied to pairs or triples of double-cell items. For these applications, the overloaded versions of SWAP, OVER and ROT will do nicely. Therefore, 2SWAP, 2OVER and 2ROT are not provided in StrongForth.

2DUP and 2DROP are used more frequently. However, to avoid confusion with the double-cell versions of DUP and DROP, StrongForth does not even provide these two words. You can always write OVER OVER instead of 2DUP, or DROP DROP instead of 2DROP, so not having these words shouldn't be a real drawback. If you still think it is, don't hesitate to define 2DUP and 2DROP yourself:

: 2DUP ( SINGLE SINGLE -- 1ST 2ND 1ST 2ND )
  OVER OVER ;
: 2DUP ( SINGLE DOUBLE -- 1ST 2ND 1ST 2ND )
  OVER OVER ;
: 2DUP ( DOUBLE SINGLE -- 1ST 2ND 1ST 2ND )
  OVER OVER ;
: 2DUP ( DOUBLE DOUBLE -- 1ST 2ND 1ST 2ND )
  OVER OVER ;

: 2DROP ( SINGLE SINGLE -- )
  DROP DROP ;
: 2DROP ( SINGLE DOUBLE -- )
  DROP DROP ;
: 2DROP ( DOUBLE SINGLE -- )
  DROP DROP ;
: 2DROP ( DOUBLE DOUBLE -- )
  DROP DROP ;

An interesting detail of these definitions is the fact that all overloaded versions of 2DUP and 2DROP seem to be identical except for their stack diagrams. However, they only look identically. For each version of 2DUP, for example, the compiler uses different overloaded versions of OVER. Let's have a closer look at the second overloaded version of 2DUP. When the compiler gets to the first OVER, the data type heap contains ( SINGLE DOUBLE ), so it compiles

OVER ( SINGLE DOUBLE -- 1ST 2ND 1ST )

Now the data type heap contains ( SINGLE DOUBLE SINGLE ). After parsing the second OVER, the compiler chooses a different overloaded version of OVER,

OVER ( DOUBLE SINGLE -- 1ST 2ND 1ST )

resulting in ( SINGLE DOUBLE SINGLE DOUBLE ) to be on the data type heap. And that's is exactly what this overloaded version of 2DUP was supposed to produce.

The other overloaded versions of 2DUP use different overloaded versions of OVER. StrongForth's compiler will always ensure that the compiled words fit to the items on the stack.

If you think it's annoying to write four times almost identical definitions, here's an alternative solution that does the overloading at compile time:

: 2DUP ( -- )
  POSTPONE OVER POSTPONE OVER ; IMMEDIATE

: 2DROP ( -- )
  POSTPONE DROP POSTPONE DROP ; IMMEDIATE

Please note that this alternative, though it looks more elegant, does not behave identically to the first alternative. Using the first alternative compiles only one word, while using the second alternative compiles two words. The second alternative is faster at runtime, because it saves the overhead of entering and exiting a word (2DUP or 2DROP) by inlining the two words it compiles. On the other hand, it consumes one more word of dictionary space each time 2DUP or 2DROP is compiled.


Dr. Stephan Becher - January 4th, 2008