CS 331 Spring 2016  >  A Quick Introduction to Forth


CS 331 Spring 2016
A Quick Introduction to Forth

By Glenn G. Chappell
Text updated: 9 Mar 2016

Table of Contents

  1. The Forth Programming Language
  2. The Stack
  3. Arithmetic
  4. Compiled Words
  5. Stack-Effect Notation
  6. Stack Manipulation
  7. Strings
  8. Named Parameters
  9. Flow of Control: Selection
  10. Flow of Control: Iteration
  11. Topics Not Covered (Yet)

1. The Forth Programming Language

Forth is a programming language developed by Charles Moore in the 1960s. In 1994 ANSI issued a Forth standard. A free implementation of this is available from the GNU project: Gforth.

Forth is a concatenative programming language, meaning that the concatenation of two valid Forth programs is another valid Forth program, with the output of the first part becoming the input of the second part. Like most concatenative programming languages, Forth is stack-based, meaning that parameters and return values are all passed via a stack.

Forth is an imperative programming language: programs consist primarily of instructions that tell a computer what to do.

Forth has an extremely simple syntax: programs consist of sequences of words: strings of non-space characters separated by space.

ANSI Forth does have a limited notion of typing. However it has no type checking; rather, values of different types are stored separately, and are handled via different constructions. The majority of Forth operations involve values that are essentially ints. These are used as numbers, booleans, and pointers. There is some support for floating-point.

As with many modern programming languages, Forth can be used interactively. Forth programs can also be stored in source files and compiled for later execution. The filenames of Forth source files traditionally end with the suffix “.fs”.

Examples will mostly use the Gforth interactive environment. As you would expect, this environment allows the user to type in Forth code, which is immediately executed.

Source files can be loaded into the interactive environment. For example, to load the file “myprog.fs”, type the following.

[Interactive Forth]

s" myprog.fs" included

Note the blank after the first quote above; there must be exactly one blank there. Other source files can be loaded in a similar manner. After a file is loaded, words defined in the file can be used interactively.

In the examples below, boldface typewriter-font characters are those typed by the user. Non-bold typewriter-font characters show Gforth output. As you read this introduction, I recommend that you follow along with Gforth.

2. The Stack

Typing a number in Gforth pushes that number on the stack. The word “.s” shows the current stack, with the top of the stack to the right.

[Interactive Forth]

37  ok
-3 17 8 100  ok
52  ok
.s <6> 37 -3 17 8 100 52  ok

Above, the number in angle brackets is the current size of the stack. The “ok” indicates that all processing is done, and Gforth is ready for input again. Note that, to Forth, all space is the same: blanks and newlines are treated identically.

drop” pops the top item off the stack. “clearstack” clears the stack.

[Interactive Forth]

drop  ok
.s <5> 37 -3 17 8 100  ok
-333 .s <6> 37 -4 17 8 100 -333  ok
drop .s <4> 37 -3 17 8 100  ok
drop .s <4> 37 -3 17 8  ok
drop .s <3> 37 -3 17  ok
clearstack  ok
.s <0>  ok

3. Arithmetic

The word “+” pops the top two items off the stack, adds them, and pushes the result.

[Interactive Forth]

\ Comments begin with backslash, continue to end-of-line  ok
\ I assume the stack is clear now  ok
31 8  ok
.s <2> 31 8  ok
+  ok
.s <1> 39  ok
\ We have added 31 and 8 to get 39  ok

The word “.” pops the top number off the stack and prints it, followed by a blank. Thus we do not have to keep looking at the whole stack.

[Interactive Forth]

31 8 + . 39  ok

Words “-”, “*”, and “/” perform the usual arithmetic operations. (Note that the division is integer division.)

This allows us to do more complex computations. Suppose we wish to compute \((1+2) \times (5+6)\). The first sum is “1 2 +” in Forth. This pushes the result (\(3\)) on the stack. The second sum is “5 6 +”. This pushes another result (\(11\)) on the stack. Now we have \(3\) and \(11\) on the stack. Doing “*” multiplies, pushing the final result (\(33\)). Lastly, “.” pops and prints it.

[Interactive Forth]

1 2 + 5 6 + * . 33  ok

Here is an example computation in C++.

[C++]

cout << (23 + 17) * (8 - 27 + 6) + 19 * 25 * 6;

Here is the same computation in Forth.

[Interactive Forth]

23 17 + 8 27 - 6 + * 19 25 * 6 * + . 2090  ok

Notice that, in Forth, we do not need to worry about precedence and associativity. Forth needs no parentheses!

4. Compiled Words

To define a new word, use a colon (“:”), followed by the new word, the code for the new word, and a semicolon (“;”). After that, using the newly defined word executes the given code.

[Interactive Forth]

: triple 3 * ;  ok
6 triple . 18  ok
: printTriple triple . ;  ok
17 printTriple 51  ok

Forth words are not case-sensitive.

[Interactive Forth]

5 PRINTTRIPLE 15  ok
10 printtriPLE 30  ok

5. Stack-Effect Notation

Stack-effect notation indicates the effect of a Forth word by showing a picture of the stack before the word is executed, followed by two dashes (“--”) and then a picture of the stack after the word is executed. Usually, all this is enclosed in parentheses. For example, the stack effect of our word triple is “( x -- 3*x )”.

Here are stack-effect descriptions for a few of the words we have covered.

31     ( -- 31 )
drop   ( x -- )
+      ( x y -- x+y )
-      ( x y -- x-y )

Anything surrounded by parentheses in Forth is a comment. Thus, stack-effect descriptions can be included when defining a word.

[Interactive Forth]

: triple ( x -- 3*x )  compiled
  3 * ;  ok

Above, “compiled” indicates that we are in the middle of defining a compiled word.

6. Stack Manipulation

Forth has a number of standard words that manipulate the stack. Here are a few of them, along with their stack-effect descriptions.

drop   ( a -- )
dup    ( a -- a a )        \ dup for "duplicate"
swap   ( a b -- b a )
rot    ( a b c -- b c a )  \ rot for "rotate"
-rot   ( a b c -- c a b )  \ inverse of rot
nip    ( a b -- b )
tuck   ( a b -- b a b )
over   ( a b -- a b a )

For some of these, prepending a “2” does the same operation on pairs of stack items.

[Interactive Forth]

2drop  ( a1 a2 -- )
2dup   ( a1 a2 -- a1 a2 a1 a2 )
2swap  ( a1 a2 b1 b2 -- b1 b2 a1 a2 )
2rot   ( a1 a2 b1 b2 c1 c2 -- b1 b2 c1 c2 a1 a2 )
2nip   ( a1 a2 b1 b2 -- b1 b2 )
2tuck  ( a1 a2 b1 b2 -- b1 b2 a1 a2 b1 b2 )
2over  ( a1 a2 b1 b2 -- a1 a2 b1 b2 a1 a2 )

Note: There is no general principle here. “2drop” is a completely separate word from “drop”.

The word “pick” pops off the top of the stack, and then uses it as an index for an item in the stack to duplicate as a new top item. Indexing starts from 0 (top of stack). Thus “0 pick” is the same as “dup”, and “1 pick” is the same as “over”.

[Interactive Forth]

966 955 944 933 922 911 900  ok
4 pick  ok
.s <8> 966 955 944 933 922 911 900 944  ok

roll” is similar, but it brings the indexed item to the top of the stack without duplicating it.

[Interactive Forth]

966 955 944 933 922 911 900  ok
4 roll  ok
.s <8> 966 955 933 922 911 900 944  ok

Thus “0 roll” does nothing, “1 roll” is the same as “swap”, and “2 roll” is the same as “rot”.

7. Strings

To enter a Forth string, start with the word “s"”. This should be following by a blank, as usual. After the blank, the next character begins a string, which ends with a double quote. For example the string “abc” would be entered as “s" abc"”. These strings may contain blanks, even at the beginning. The string “ abc def ” would be entered as “s"  abc def "”. Note the two blanks before the “a”.

The result on the stack is two values: a pointer to the start of the string, and its length.

[Interactive Forth]

s" Hello"  ok
.s <2> 138245848 5  ok

The value 138245848 above is the pointer. If you enter the above string, then the pointer you get will probably have a different value. The 5 is the length of the string.

If a string is represented on the stack as address-length, then we can print it using “type”.

[Interactive Forth]

\ type ( addr len -- )  ok
s" Hello, world!"  ok
type Hello, world! ok

8. Named Parameters

A useful variation on stack-effect notation allows for named parameters. Replace the parentheses in the stack-effect notation with braces. The parameters are popped off the stack and given the listed identifiers as (local) names.

Here is triple, redone.

[Forth]

: triple { x -- 3*x }
  x 3 * ;

The part of the stack-effect notation after the double dash (“3*x” above) is treated as a comment and ignored.

9. Flow of Control: Selection

In C++ we have the if-else control structure.

[C++]

if (CONDITION)
{
    THENCODE;
}
else
{
    ELSECODE;
}

Here is the Forth equivalent.

[Forth]

CONDITION if
  THENCODE
else
  ELSECODE
endif

The word “if” pops the top of the stack. If this value is non-zero, then THENCODE is executed; otherwise, ELSECODE is executed.

In this context, comparison operators are useful. These are the following

=  <>  <  <=  >  >=

Note that the not-equal operator is “<>”. But if you prefer the C++, version, then you can easily define it.

[Forth]

: != <> ;

Each comparison operator pops the top two items off the stack, compares them, and pushes the result: \(-1\) for true and \(0\) for false. Bitwise AND, OR, and NOT are the words “and”, “or”, and “invert”. When used with values \(-1\) and \(0\), these function as logical operators.

Here is an example.

[Forth]

: tenToTwenty { x -- }
  x 10 >= x 20 <= and
  if
    x . s" is in the range [10, 20]." type
  else
    s" Alas! " type
    x . s" is NOT in the range [10, 20]." type
  endif
;

[Interactive Forth]

14 tenToTwenty  14 is in the range [10, 20]. ok
26 tenToTwenty  Alas! 26 is NOT in the range [10, 20]. ok

10. Flow of Control: Iteration

Forth has a large number of iteration constructs. In this section we look at a general-purpose loop.

Consider the following C++ code.

[C++]

while (true)
{
    LOOPBODY1;
    if (!CONDITION)
        break;
    LOOPBODY2;
}

Here is the Forth equivalent.

[Forth]

begin
  LOOPBODY1
CONDITION while
  LOOPBODY2
repeat

The word “begin” marks the start of the loop. The word “repeat” marks the end. When the word “while” is executed, the top of the stack is popped. If this is zero, then the loop exits: execution moves to the code just after “repeat”; otherwise, execution continues after the “while”.

For example, here is a Forth word that takes a parameter called n. It prints “Howdy! n times. The word “cr” prints a newline.

[Forth]

: multiHowdy ( n -- )  \ Prints "Howdy!" n times
  cr
  begin
  dup while
    s" Howdy!" type cr
    1 -
  repeat
  drop
;

As mentioned above, Forth has a number of iteration constructs. There are others that would make the above code rather clearer.

As in most programming languages, Forth flow-of-control structures can be nested.

11. Topics Not Covered (Yet)

This has only been a brief introduction. There is more to say about Forth—but not a lot more, as it is a simple programming language. Below are a few topics that were not covered here.