FORTH
Forth is one of those programming languages that wrongly is put into the category of esoteric programming languages. I think the most valuable thing about learning a new programming language makes you think about a problem in a new way. Or as they put it in Structure and Interpretation of Computer Programs (a book you should 100% read - it’s free and will make you a better programmer):
A powerful programming language is more than just a means for instructing a computer to perform tasks. The language also serves as a framework within which we organize our ideas about processes. Thus, when we describe a language, we should pay particular attention to the means that the language provides for combining simple ideas to form more complex ideas.
As I’m reacquainting myself with Forth, I thought it’d be a good idea to write a little series of tutorials. There are several other excellent learning resources; I’ll reference some at the end of this blog post.
Who is this for?
But knowing how hard it is to find the time to learn a new programming language, I want to take a little different road: Instead of feeding you a ton of history and theory, I’ll focus on a small subset of Forth in each post. There will be a Github repository you can clone with programming challenges for each part of this series.
I’ll assume that most readers will come with at least a little programming experience. If you understand and can explain terms like function, variable, (arithmetic) operator, recursion, etc., you’ll be fine. I’ll try to explain new concepts as simply as possible (but not too simple).
Research shows that learning in small chucks regularly increases information retention (compared to devouring a lot of new information at once). So I will try to keep each part in this tutorial series very short - reading each post should take you no more than 10 minutes. Solving the accompanying programming challenges should not take more than 10 minutes as well. A little investment of 20 minutes for each part will help you learn a new programming language that will hopefully help you grow as a programmer. It certainly did that for me.
Without further ado, let’s setup up Forth and get started!
Installation and Setup
To learn Forth, you’ll need two things: A computer (which I assume you have since, you know, you’re reading this on the internet) and a Forth implementation. We’ll use Gforth here, an open-source implementation that comes with very extensive documentation and will run on all major platforms.
Windows users download the installer and execute it to install Gforth.
Linux users will want to check their respective package managers for a version of Gforth (any version above 7.1. should be fine).
OSX users can use homebrew to install Gforth:
$ brew install gforth
There’s even an Android version on the Google Play Store if you wish to use it on your smartphone or tablet.
Power users can always build from source.
Once your preferred installation process is done, open a terminal and check whether Gforth is working:
$ gforth
Gforth 0.7.9_20200709
Authors: Anton Ertl, Bernd Paysan, Jens Wilke et al., for more type `authors'
Copyright © 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `help' for basic help
If you get a welcome message similar to the one above, you’re good to go!
Basics
Forth is a procedural, stack-oriented, reflective, and concatenative programming language developed by Charles H. “Chuck” Moore around 1970. If any or all of those paradigms sound strange to you, don’t worry, we’ll explain each one when appropriate. In this part, we’ll limit ourselves to procedural and stack-oriented for the time being.
Your First Word
A procedural language allows you to define a series of computational steps that you can call from any point of your code. Most programming languages call this concept functions or procedures (also subroutines, methods, etc.). In C, you could write something like this:
void hello_world()
{
printf( "Hello, World!" );
}
Forth allows you to define your own functions, but in Forth, we call them words. Words are defined by creating a colon definition. Open Gforth in your terminal and enter the following. I’ll use the symbol ⏎ to mark the places where you should press the enter key, but it will not be actually visible in your terminal:
: hello-world ." Hello, World!" ; ⏎
Whitespace is important in Forth! Make sure there is at least one space (or tab) after the colon and the word ."
, as well as before the final semi-colon ;
. Else, you may get error messages:
: hello-world ."Hello, World!" ;
*the terminal*:3:15: error: Undefined word
: hello-world >>>."Hello,<<< World!" ;
Backtrace:
kernel/int.fs:572:10 0 $6FFFFF7FDCA0 throw
Forth implementations tend to be very friendly. After each line you enter, Forth will acknowledge it by answering ok
(which also signals that Forth has finished executing and is ready to receive new commands).
Let’s look at your first word definition in detail:
: hello-world ." Hello, World!" ;
To define a new word, start a colon definition by entering :
, followed by the name of the new word. After that, you enter all words (i.e., commands) you want Forth to execute when the word is called (think of it as the body of your function). The word ."
is called dot-quote in Forth parlance and will print all characters until it finds a quote "
again. The new word’s definition ends with a semi-colon ;
. NB! many programming languages use the semi-colon to signal the end of an expression. That’s not the case in Forth. The word ;
is called end and just does that: it ends a colon definition. This will add the new word to the dictionary. The dictionary stores all words you define (and all others that Forth ships with). We’ll look at the dictionary in more detail in a later tutorial. Once we’ve defined a word, we can call it by simply entering its name into Forth:
: hello-world ." Hello, World!" ;⏎ ok
: goodbye ." Goodbye, Mars!" ;⏎ ok
hello-world⏎ Hello, World! ok
goodbye⏎ Goodbye, Mars! ok;
Generally, words in Forth are case-insensitive. So you can invoke the words above in any way you like:
heLLo-worlD⏎ Hello, World! ok
hello-wORLd⏎ Hello, World! ok
HELLO-WORLD⏎ Hello, World! ok
That’s the basics of defining your own words. In the next section, we’ll start manipulating the stack. If you wish to leave Gforth, just use the word bye:
bye⏎
I told you Forth is very friendly. Mind that once you exit Forth, your newly defined words are gone. In the next section, I’ll show you how to load a file into Forth so you can store your words permanently.
Stack Notation and Manipulation
The second paradigm Forth follow is stack-oriented. Put simply, stack-oriented means that we pass arguments to functions by a stack. Contrast that with C:
void add( int x, int y )
{
return x + y;
}
Here, the compiler will take care that x and y are replaced with the correct values we pass (depending on your platform and compiler combination, the compiler may pass values by register or by stack; this is something C programmers will often leave to the compiler to optimize).
Let’s look at some simple stack operations. A stack is a LIFO (last in, first out) data structure. To put an object on the data stack, we’ll simply enter it into Forth (we’ll limit ourselves to integers for the moment; we’ll cover floating-point, strings, etc. in a later tutorial):
5⏎ ok 1
You’ll now notice that Forth answered with ok 1
. The number after ok is the stack depth. It tells you how many objects are currently on the stack. Let’s push some more numbers onto the stack:
4 3 2 1⏎ ok 5
As you see, we can push several numbers at the same time by separating them with whitespace. This is in essence what Forth is: a whitespace-delimited series of words, either built-in or user-defined.
We now have a total of 5 elements on the stack. If we want to examine them, we’ll use the word .s
, called dot-S. This word will simply print out all objects currently on the stack:
.s⏎ <5> 5 4 3 2 1 ok
Mind that the top of the stack (i.e., the element we pushed last onto the stack) is to the right, preceded by the other stack objects in descending order. The number between the angled brackets <
, >
again shows the stack depth (the number of elements currently on the stack).
We’ll now look at some of the most basic Forth words for manipulating the stack. To that end, let’s also introduce stack effect notation. Stack effect notation simply indicates how a word affects the stack. We indicate this by using Forth comments. In Forth, comments are delimited by parentheses. The stack effect is by convention placed after a word’s name:
: hello-world ( -- )
.s" Hello, World!" ;
We describe how the stack looks before the double dashes --
, and how the word will leave the stack after it’s called. Since hello-world
doesn’t affect the stack at all, both sides of the double dashes are simply left empty like in the example above. Let’s clarify this with some examples.
If we want to remove the top of the stack (i.e., the last object we pushed onto the stack), we use the word drop
:
.s <5> 5 4 3 2 1⏎ ok
drop drop⏎ ok 3
s. <3> 5 4 3⏎ ok
Assuming we kept the five integers we pushed earlier onto the stack, we can see with dot-S that only four elements are left on the stack, 5 was removed from it. We would indicate this in stack effect notation like this:
: drop ( x -- )
( code for drop ) ;
We can read this as “drop expects (at least) one element on the stack and after execution, it will be removed”.
Here are some more examples of common stack manipulation words:
: swap ( x2 x1 -- x1 x2 )
( swap top two elements ) ;
: dup ( x -- x x )
( copy the top of stack ) ;
: 2dup ( x2 x1 -- x2 x1 x2 x1 )
( duplicate top two elements ) ;
: over ( x2 x1 -- x2 x1 x2 )
( copy the element below top of stack ) ;
: 2over ( x4 x3 x2 x1 -- x4 x3 x2 x1 x4 x3 )
( copy the two elements below the two top of stack ) ;
: nip ( x2 x1 -- x1 )
( remove element below top of stack ) ;
: tuck ( x2 x1 -- x1 x2 x1 )
( push top of stack to third place ) ;
: rot ( x3 x2 x1 - x2 x1 x3 )
( rotate top three elements ) ;
There are a few more basic stack operations, but these are the most common.
At this point, I’d strongly encourage you to play around with Forth and the stack. Push some integers onto the stack, try different combinations of words, and check if your predictions are correct.
Some Details We Left Out
Most of these things may seem a bit boring. We haven’t touched any of the fun features like arithmetic operations, conditionals, bools, etc. We’ll cover those in later tutorials.
Before we end, I’ll explain how you can load a file into Gforth. Open your favorite text editor and create a file foo.4th (the .4th is often used for Forth, sometimes you’ll also see .fs, .th, .fth, etc. - but it doesn’t really matter with the following content:
\ myfile.4th
: hello ( -- )
.s" Hello, you!" ;
Now you can simply invoke Gforth with the file name and it will be loaded automatically:
$ gforth myfile.4th
Gforth 0.7.9_20200709
Authors: Anton Ertl, Bernd Paysan, Jens Wilke et al., for more type `authors'
Copyright © 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `help' for basic help
hello⏎ Hello, you! ok
That’s it. Now you can store your words in a file and experiment with them later again.
The Programming Challenges
Finally, let’s test your knowledge with some simple programming challenges. Clone this repository and cd
into the subdirectory part01
. There’s a makefile that will run the tests. Open a terminal and enter:
$ make check1
As you can see, there’s a list of errors indicating the tests that fail. Your goal is to make all tests pass (i.e., make sure there’s no output).
Open challenges01.4th
in your favorite editor. You’ll find several words there like this:
\ Challenges 01
\ Replace the word definitions with stack manipulation words to pass all tests
: challenge1 ( 1 2 3 -- 1 2 )
( your code ) ;
Next to the word’s name, you’ll see in stack effect notation what to achieve. So for this challenge, your solution could look like this:
: challenge1 ( 1 2 3 -- 1 2 )
drop ;
To check your solution, simply run make tests
again. Compared to the last time, the first error should no longer be displayed.
Now go solve all challenges!
Conclusion
This was a bit of a rough start if you’ve never worked with a stack-based language before. Getting used to interactively developing your program directly in Gforth will take some time to get used to. But other languages like Common Lisp or Python also rely on their respective REPLs heavily during development.
Next time, we’ll look at some basic arithmetic operations and how reverse polish notation relates to them. Thank you for reading and until next time!
Links and Resources
Here are some additional resources to get you started:
- Starting FORTH is a classic introductory textbook that is now available for free online
- There are Forth syntax highlighters both for Atom and VSCode
- Nick Morgan has written an excellent interactive introduction to Forth you can find here (he also wrote this amazing 6502 tutorial)
- The Gforth User Manual has a good tutorial you can follow, found here. It’s also a good source to get more comfortable with Gforth overall.