<< Home | About Forth | About TurboForth | Download | Language Reference | Resources | Tutorials | YouTube >>
Contents:
Forth is a simple language. Probably far simpler than any other language you have ever used. If you have used BASIC or C you will be pleased to hear that Forth is considerably simpler than both of those languages.
Forth basically consists of words and numbers.
That's it.
Congratulations, you have just learned Forth!
Really. There are no difficult rules to learn about when you need parentheses, opening and closing braces, semi-colons. No function prototypes to worry about. Nothing. Not a jot.
All you have to know is this:
In all seriousness, that really is it.
Let's explore that concept some more. First, we'll talk about the stack.
The stack is the absolute key to understanding Forth. Once you understand the concept of the stack you really are on your way to understanding Forth. There are two types of stack in Forth: The Data Stack, and the Return stack. The return stack will be discussed later. The data stack (sometimes called the parameter stack, the terms are interchangeable) is used so often, that its name has just been shortened to 'the stack' in Forth parlance. So, when we say 'the stack' we mean the data stack. Don't worry about the return stack, we'll get to that later.
So, what is the stack? It is literally a stack of 'things'. In the case of Forth, those 'things' are numbers.
Imagine a stack of plates in a restaurant. The last plate placed on the stack (the plate on the top) will be the first to be removed, naturally. When all the plates are gone, the stack is empty. The stack in Forth behaves in exactly the same way.
When Forth sees that you have given it a number, it simply adds it to the stack, on top of the other numbers (if any).
Let's try it. Start up TurboForth and type this (press ENTER at the end of the line):
42
TurboForth will respond with: ok:1
The OK simply means that TurboForth processed your input without errors. The 1 means that there is now one number (42) on the stack. You can already see that Forth is a whole lot different to most other languages. BASIC or Pascal or C wouldn't know what to do with a single number on its own like that. Forth has no problem.
Let's enter another number: 24 <enter>
TurboForth responds with: ok:2
There are now two numbers on the stack: 42 at the bottom of the stack, and 24 on the top.
Okay, you get the idea. The stack can literally be thought of as a stack of plates (with numbers written on them). The stack grows upwards, just like a stack of plates.
In TurboForth there is room on the stack for 128 16-bit words (256 bytes).
Words make Forth do things. That is it. Simple isn't it?
In practice, most words use the stack in some way. They often take data off the stack, do some processing of some kind, and sometimes put a result back onto the stack.
There is no such thing as punctuation in Forth. Take an example line of code written in the C language:
Here, = ( , ) and ; are punctuation. Without them, the program won't compile or won't work as expected, yet they don't actually do anything as such. They are just there to demarcate one area of the function call from another. They are part of the syntax of the language. For example, the open and closed parentheses indicate 'here comes some data that should be passed into the calculateArea function. This is not the case with Forth. In Forth, every symbol that is not a number (with the exception of the space character) can be an executable word that does something. There are no restrictions, because there is no syntax.
None at all.
Here are some examples of perfectly legal words:
And some illegal ones:
Because nearly all data is passed via the stack, Forth programs look different to programs written in other languages. They often look backwards, and this can be a source of frustration and confusion to newcomers to the Forth language. Hang in there, it becomes natural, like riding a bicycle. Let's take a look at why:
Sounds fancy doesn't it? Reverse Polish Notation (RPN, or sometimes called Postfix Notation) was invented in the 1920s by a Polish gentlemen called Jan Lukasiewicz. It is a simple technique that allows a stack to be used to manipulate numbers.
Here's how we conventionally add two numbers, the way we were taught at school:
10 + 5
So, take 10, and add 5 to it. Simple. It reads nicely from left to right. Let's have a look at the RPN equivalent:
10 5 +
Huh? I hear you say. That looks strange! And indeed it does, until you think of it in the context of a stack. Here's how to read the above in English:
“Put 10 on the stack. Put 5 on stack. Add them.”
See? It also reads nicely from left to right, it's just different, and unusual, because most of us are not familiar with it. However, it makes perfect sense when there is a stack in the mix.
Here's the killer point though: When we say 'add them', the action of adding the two numbers removes them from the stack, to be replaced with their result.
That's possibly a brain hurting moment if you are not familiar with RPN, so let's look at that in a bit more detail. It is essential that you understand Reverse Polish Notation and how the stack works if you are to fully understand Forth.
Let's have a look at what happens to our stack while we evaluate:
10 5 +
First, we start off with an empty stack:
See? Nothing on the stack at all.
Then, we put 10 on the stack (in Forth speak, we 'push' 10 on stack):
Then, we push 5 onto the stack:
Notice that the 10 has moved 'down' on the stack (10 is now at the bottom of the stack. Think of the plates in a restaurant analogy).
Now, here comes the magic bit. Think of + as an action. Even better, think of it as a tiny little computer program that is programmed to do the following:
1. Remove a number from the top of the stack
2. Remove another number from the top of the stack
3. Add them together
4. Place the answer on the stack
Do you see how it works? In step 1, + takes 5 off the stack, leaving 10 on the stack all by itself:
+ then takes another number in step 2 (our number 10), leaving the stack empty:
+ then adds the numbers that its holding together, leaving the result, 15, on the stack:
That's it! That's all there is to RPN. It is essential to visualise the stack when working with RPN. This is probably the fundamental difference between Forth and most other programming languages: Most other languages only require you to worry about execution flow – what stuff is executed in what order. In Forth, you also have to manage data flow via the stack, so that the right thing is on the stack when you expect it to be. Don't worry, it's not as difficult as it sounds, and there are techniques that can help you, such as factoring which we will come to later.
Did you notice that you were able to type your math problem directly in at the keyboard and get the answer immediately? This illustrates one of the major advantages of Forth over other languages: Forth is fully interactive. If you are familiar with languages like C, Pascal, Java, and assembly, you will know that the development cycle normally goes something like this:
1. Edit source code in a text editor
2. Save the code
3. Load the compiler
4. Compile the source code
5. Errors? Go to step 1
6. Load linker
7. Errors? Bang head on wall, kick cat. Go to 1
8. Run program (yes!)
9. Errors? Shoot computer with missile launcher. That'll teach it. Go to 1.
10. Repeat from 1 until it's time to go home, or you leap out of the window.
You get the idea.
In Forth, the above has been reduced to:
1. Type in the code
2. Run it
3. Error? Go to 1
No feline violence, nor weapons of mass destruction are necessary. You can enter your problem, right there and then at the keyboard and test it, and get the results back. Immediately. In a large project, this can be a massive timesaver. It makes de-bugging and unit testing (testing small, isolated parts of your program, independently from other parts) mind numbingly simple.
So, as you can see, Forth is interactive. It will execute what you tell it to execute, immediately and give you the results. Fine. However, what about when you want to store things (like programs) in memory, for execution later?
Well, that's when you turn to...
We saw in the last section that Forth can execute the instructions that you give it immediately and give you your results. That's fine for testing, or balancing your cheque book, but how do you program Forth?
Step up the “colon definition”. Here is what a colon definition looks like in real life:
: NAME STUFF ;
Colon definitions allow you to store Words in memory, for later execution. (Remember, as we learned earlier, what other languages call subroutines, Forth calls Words)
You start by typing (rather unsurprisingly) a colon. Then, you have to enter a name, for example LIFE. Then, after that you write the code that the word will execute when we invoke the word. Lastly, you need a semi-colon, which marks the end of the word.
Here's a (rather stupid) example:
: LIFE 42 ;
Go ahead and type that in to TurboForth exactly as you see it above. Congratulations, you have just written your first Forth program!
The above is a perfectly valid Forth program! You will have noticed that when you typed it into TurboForth, TurboForth didn't really do much. It just said...
ok:0
… and then got on with the really important job of blinking its cursor.
However, that was actually a good thing – it meant that TurboForth actually compiled your Word (LIFE) into memory. Just like that, in the blink of an eye. None of this edit/compile/link/try/die malarky that you find in other languages. You're going to love Forth!
So, we've compiled a little program, but how do we run it? Well, executing a Word is no more complicated than simply typing its name and hitting enter! Try this (by now, you should be used to pressing ENTER at the end of the line):
LIFE
TurboForth responds with:
ok:1
It worked! Our little program actually ran, and we can see that it left something on the stack for us! How do we see what it left on the stack? Simply type a period (full-stop) and hit ENTER (you'll recall that . simply means “remove the topmost number from the stack and display it”):
.
And we get:
42 ok:0
I don't know about you, but I'm just about bouncing off the ceiling right about now. We wrote our first program in Forth, and it ran just fine.
Let's try something a bit more advanced:
: DOUBLE DUP + ;
As soon as you press ENTER after the semi-colon you get the familiar:
ok:0
...and TurboForth is ready and waiting.
Now, let's try our program with:
9 DOUBLE
TurboForth has placed something on the stack for us:
ok:1
Hmm... Let's use . (dot) to see what it is:
.
18 ok:0
Excellent! TurboForth took our 9, doubled it, and left the answer on the stack.
How did that work? I know you're anxious to know, so we'll digress quickly and explore exactly how that program works:
When we typed 9 DOUBLE and pressed ENTER, a few things happened:
1. TurboForth was in 'immediate mode' – waiting for you to give it an instruction.
2. ENTER was pressed and TurboForth started examining the instructions you had given it.
3. It found 9. It realised that 9 is a number, so it pushed it onto the stack.
4. It found DOUBLE. It realised that DOUBLE isn't a number, it's a Word, so it found the word in its dictionary (more on that later) and...
5. Left immediate mode, and entered 'run mode' and ran the Word DOUBLE.
6. DOUBLE did its stuff, placing the answer on the stack.
7. TurboForth entered immediate mode again, and said OK.
Okay, lets have a look at how our program, DOUBLE, actually works. Recall that when we called DOUBLE, 9 was already on the top of the stack:
9 DOUBLE
TurboForth, as described above, then found DOUBLE and executed the code it found in DOUBLE, which is:
DUP +
What is that strange DUP word? Well, DUP is simply short for DUPlicate. Pronounced “dupe” it takes whatever is on the top of the stack, and copies it, placing that copy back on the stack:
Before DUP:
Then DOUBLE calls DUP:
Then, + is executed. We already know what + does: It takes two numbers off the stack, adds them, and replaces them with the result:
And there we have it, our answer is left neatly on the stack for us. Let's take another example. I'm sure you will be able to follow this:
: SQUARED DUP * ;
Then try it with:
9 SQUARED .
TurboForth dutifully responds with:
81 ok:0
If we want, we can save ourselves the bother of having to type . all the time, and we can just move it into the SQUARED word itself:
: SQUARED DUP * . ;
This time, we get a warning from TurboForth:
*Redefined* ok:0
TurboForth is just letting us know that we have changed how SQUARED works, replacing the old definition of SQUARED with a new one. Let's try it out:
12 SQUARED
144 ok:0
It worked! It printed the answer for us! Let's recap exactly what happened:
1. We were in immediate mode (TurboForth isn't running a program, it's waiting for some work for us) when we typed 12 SQUARED and pressed ENTER.
2. TurboForth recognised 12 as a number and placed it on the stack.
3. It recognised SQUARED as a Word, and left immediate mode, going into run mode and ran our SQUARED word.
4. SQUARED DUP'd our 12 on the stack, so the stack now contains 12 & 12. It then ran * which removed the two 12's from the stack, and replaced them with their product.
5. . was then called, which printed the product (144) to the screen.
6. The end of word was reached, and we returned to immediate mode, waiting for more work to do.
Easy! You can see how easy it is, with the interactive nature of Forth to build your application in small steps, testing each part before you move onto the next one. No need to recompile, no need to assemble. Just name the word right there at the keyboard. Allow me quote Elizabeth Rather, one of the worlds most respected, and earliest Forth programmers: “Forth is a living language. You do not build or assemble applications, you grow them.”
Another central concept to Forth is that of the dictionary. Just about every Forth system has a dictionary, though they differ from system to system in terms of how they are implemented.
The dictionary is simply a list of words that the Forth system 'knows'. For example + - * DUP etc. are all words (remember, Forth has no syntax, so don't think of + and – etc. to be operators, they are just words that do something).
In TurboForth you can see all the words in the dictionary by typing WORDS. TurboForth will display a list of all the words in the dictionary. You can press and hold any key to pause the list, or press F4 to stop the display. TurboForth knows in excess of 200 words from power-up, covering everything from stack manipulation to graphics to disk operations to synthesized speech.
When you create a colon definition (i.e. a new 'word') you are adding your new word to the dictionary:
: GREET .” HELLO WORLD” ;
Now, type WORDS again, and then press any key to hold the display. You will see GREET as the first word in the list.
In actuality, the dictionary is a linked list – each dictionary entry is linked to the next dictionary entry. When you type a word, Forth moves from one dictionary entry to another, looking for the word, and if it finds it, it executes the instructions associated with that word. If it doesn't find it, it will complain.
Forth uses a "link field" to 'walk down' the dictionary list when it is looking for a word. If you type DUP at the keyboard and press ENTER, Forth does the following:
1. Asks itself “is this a number?” (er, no, it isn't)
2. Since it is not a number, it goes on a search of the dictionary:
3. It walks the dictionary, looking for a word with a length of 3 (since DUP is three characters).
4. When it finds one, it checks the name field, to see if it matches.
5. If it doesn't, it moves to the next entry in the list
6. If it does, it executes the instructions it finds in <code>
7. If it gets all the way to the end of the dictionary without finding your word, it will tell you.
“So what?” you ask. Well, nothing really. Whilst you can mostly let the dictionary hang out and do its thing without a worry, it's worth knowing, even at this early stage, that Forth really owes its power to the dictionary. Without it, it would be impossible to have the interactive language that we have. You will later learn about some words that manipulate the dictionary, such as CREATE, CONSTANT, VARIABLE and FORGET. We won't go into it in a lot of detail now, suffice it to say that the dictionary and the stack are probably the two most essential ingredients that make Forth “Forth”.
The dictionary leads us nicely to another, somewhat unusual (compared to mainstream languages) paradigm in Forth...
Forth programs are generally written 'the wrong way around' compared to other languages. This is largely due to the Forth dictionary that we have just learned about.
Why is that? Well, if you think about it, you cannot reference a word in your program until that word exists in the dictionary:
: A B ;
: B <clever stuff goes here> ;
The above code won't work. Word A tries to call word B, but at the time word A is written, word B doesn't exist (you haven't typed it in yet), so it can't be located in the dictionary. It would have to be written like this:
: B <clever stuff goes here> ;
: A B ;
Now, when A is compiled, B already exists and can be found in the dictionary, therefore a reference to it can be compiled into A, and all is well.
What does this “bottom-up programming” mean to us in practical terms? Well, it simply means that you program from the lowest level of your program, “up” to the highest level. In other languages, people often develop their code by writing a basic framework at a very high level, and working down to lower levels of detail as they go. This is actually a very natural way to work; after all, if you were designing a car, you wouldn't start with the nuts and bolts. In Forth, you do start with the nuts and bolts to a certain degree, but it's not as difficult as it sounds. And it's actually more fun, and faster.
In most computer programs, the actual thing they are designed to solve constitutes a very small part of the overall program code. Normally, the lion's share of the program is concerned with getting input from the user, validating that input, and formatting output in some nice way, be it on the screen, on paper or whatever. For example, in a cheque book program, the actual piece of code that does the math to balance your finances is probably only a few lines long at most. The rest (maybe 90%?) is just handling the 'interface' between the computer and the human.
In Forth, you are encouraged to solve the fun parts first, and get it working, then move on and 'out' into higher (usually more mundane) levels of the code, which will call words that you have just written and tested. Eventually you arrive at the top level, which might be a single word called GO that starts the program off in a controlled way. The major difference is, in bottom-up programming, all the words you are working with have been written and tested already! There should be no debugging to do, if you have done your unit testing (the testing of the individual words you have written) properly.
Note, we are discussing bottom-up programming. That has nothing to do with the design. You are of course free to design your system in a top-down manner, and indeed, it is probably the best (certainly the most natural) way. However, when you come to code your program in Forth, you will go in the opposite direction, from the bottom to the top, testing all the way, and usually having much more fun than the poor chap that is coding in C or Pascal (and much faster).
Let's look at a quick example. We want to turn on a pump when a level in a sewerage tank is too high, and switch it off when the level is low.
So, at the high level, we want something like:
IF Level=High
PumpOn
If Level=Low
PumpOff
Simple.
In a bottom up scenario, we would start by defining words to control the pump, allowing it to be switched on and off:
: PUMPON <imaginary code to turn pump on> ;
: PUMPOFF <imaginary code to turn pump off> ;
We can now test these words at the keyboard to see if they work, independently of the rest of the program, by (in this case) simply typing their names at the keyboard.
PUMPON
ok:0PUMPOFF
ok:0
Great. Our words work, so we know we can control the pump. Now, we'll look at the water level (we're moving 'outwards' in our problem domain): In our imaginary application, the sewerage level is monitored by an analogue to digital converter. We won't show the code for that, we'll just assume that that part of the program is magic:
: LEVEL <read tank level> ;
Here, LEVEL reads the tank level and places the level on the stack as a number between 1 and 100, with 0 representing an empty tank, and 100 representing a full tank. We test the word by entering its name at the keyboard, and it places a value on the stack that we can display with.
Okay, at the next level, we need to tie the logic in that will decide when the pump should be switched on and off. We'll write a word called CONTROL to control the pump:
: CONTROL 25 LEVEL < IF PUMPOFF THEN 75 LEVEL > IF PUMPON THEN ;
At this point, we need to digress slightly and explain how IF works in Forth (the topic is discussed in more detail in chapter 6, section 6.2). If you are used to languages like C or BASIC you will be used to something like this:
IF <condition> THEN <do this>
In Forth, because we have a stack, it works differently:
<condition> IF <do this> THEN <carry on as normal>
So, if the condition is not met, Forth skips the part just after the IF and continues with the code following THEN. If the condition is met, Forth executes the code after the IF, then it carries on as normal, executing the code after THEN.
Let's quickly examine how this code works: 25 is placed on the stack, then LEVEL is called, which reads the tank level, and places the reading on the stack. We then call < which consumes the values, compares them, and places a flag on the stack to indicate if the tank level was less than 25 or not. If the level is less than 25, < places TRUE on the stack, otherwise it will place FALSE on the stack (TRUE is represented as -1 and FALSE as 0).
IF then consumes this flag. If the flag is TRUE, the word after IF is executed (PUMPOFF). We then do the same, but are looking for a value more than 75, in which case we turn on the pump.
Okay, so now we have a word called CONTROL which, when called will either switch the pump on if the level is too high, or off if too low (the bright sparks among you might be wondering about the 'middle' case, i.e. when the level is in-between the low and high, like, say 50. Clever you. No-one likes a smart-ass, and it's not salient to the issue of bottom up programming. So there.)
Again, we can test our word by simply typing its name at the keyboard. Now, we need a word which ties the whole thing together, running in a permanent loop. We're at the top level of our program:
: PUMPCONTROL BEGIN CONTROL AGAIN ;
BEGIN and AGAIN are Forth words that allow you to repeat the words between BEGIN and AGAIN forever.
There you have it. We type PUMPCONTROL and our application works. Perfectly. We know that, because we tested each word from the bottom up. We can now go help the C chap debug his program!
So, for completeness, here is the complete program:
: PUMPON <code to turn pump on> ;
: PUMPOFF <code to turn pump off> ;
: LEVEL <read tank level> ;
: CONTROL 25 LEVEL < IFPUMPOFF
THEN 75 LEVEL > IF
PUMPON
THEN ;
: PUMPCONTROL BEGIN CONTROL AGAIN ;
Note that each word is defined before another word references it. This is because, as we learned earlier, the word must exist in the Forth dictionary before Forth can recognise it (just like we need to know the meaning of an English word before we can use it in our own sentences or prose). This requirement is the reason for the enforcement of the bottom-up programming paradigm.
Whilst it doesn't feel entirely natural at first, it does allow you to solve the crux of the problem at hand first, and move out to the simpler stuff later. The pay-off is, you get to test as you go, safe in the knowledge that the 'foundations' upon which you build higher level code are solid, because you have tested them already. Compare this to top down, non-interactive languages, where you tend to program top-down, then scratch your head wondering where the bugs are.
As an aside (and a nice preamble) there are better ways to write the program we have just developed. We'll explore that in the next section, entitled...
Good Forth code is about good Factoring. What is Factoring? It is a very simple concept that you will understand immediately, yet its use in Forth is profound, and will help you to write smaller, faster, bug free code.
Factoring is simply taking chunks of code, and building those chunks into words in their own right, hopefully with the goal of re-using them elsewhere in mind.
Consider this example:
: MESSAGE .” I ” 1000 FOR NEXT .” LOVE ” 1000 FOR NEXT .” FORTH!” ;
This silly little word displays I LOVE FORTH! on the screen, with a small delay between each word:
1000 FOR NEXT
...forces Forth to count from 0 to 1000, which takes a little time, thus inducing a delay. However, it's a little wasteful to repeat 1000 FOR NEXT like that all the time. Is there anything we can do to make it better? Of course there is. We could simply move the FOR...NEXT loop into its own word. Let's call it DELAY and we can use that in our definition of MESSAGE.
: DELAY 1000 FOR NEXT ;
: MESSAGE .” I ” DELAY .” LOVE ” DELAY .” FORTH!” ;
Note: If you type this into TurboForth, you need a space after the opening quote mark in .” - like this: .”<space> .” is the equivalent of PRINT in other languages.
We have just “re-factored” MESSAGE and 'factored out' the FOR NEXT loops with a call to DELAY. Now, our code actually requires less space in memory, and is easier to read. We only wrote the word DELAY once, but we used it twice, and even better, we can use DELAY elsewhere if we want to.
In fairness, factoring is not unique to Forth. In other languages, people would call subroutines factors, and they would be right. The central point of a 'factor' of code, is code re-use. In an ideal Forth program, the dictionary will consist of very short words, preferably called from lots of other words. Breaking your problem (or program) down into lots of small, easy to debug, re-usable chunks is the art of factoring.
Let's have a look at the pump control program we developed previously and see if we can re-factor it to make it easier to understand, or more useful, on the way we we'll encounter a few new Forth words, which we'll explain as we go:
Firstly, one improvement we could make would be to separate the pump from its on and off actions. What if we had a word, called PUMP that returned the IO address of the pump on the stack? Then, we could have separate words for ON and OFF. This would make our code a lot more readable:
: PUMP $B000 ;
Here, PUMP simply returns the address of the pump digital output (the address is just made up, for illustration purposes. The $ at the beginning means this is a hexadecimal (base 16) number.
Now, we can write words that turn the pump on, and off. For our example, let's say that writing 1 to the pump address turns the pump on, and a 0 turns the pump off:
: ON 1 SWAP ! ;
: OFF 0 SWAP ! ;
Now, we can turn the pump on with:
PUMP ON
and off with:
PUMP OFF
Nice. It doesn't look like part of a computer program, does it?. It looks like plain English! We have separated, or factored out the operation of the pump (the on and the off) from the pump itself. This could have benefits later on, say, if a RESET command was added to reset the pump after a trip condition:
: RESET 2 SWAP ! ;
Here, we are writing a value of 2, to signify a reset, and would use it like this:
PUMP RESET
But what about that SWAP and that ! ? What's going on?
Well, when we execute PUMP, we put an address on the stack (B000). We then execute ON, which puts 1 on the stack. So our stack looks like this:
Now, we want to write that 1 to the address B000. The word to do this in Forth is ! (which simply means 'write to memory' – it is called ! as a sort of short-hand because it is used a lot) but it requires the address to be on the top of the stack, and value to write underneath it. So, we need to swap the top two stack items, which is exactly what SWAP does:
Now, when ! executes, it writes a 1 into address B000, which switches on the pump. Nice.
Notice at the higher level, the detail of how it is done is hidden away from us, at a lower level. We can just type PUMP ON or PUMP OFF and it just works. What about the word CONTROL? Let's remind ourselves what the word looks like:
: CONTROL 25 LEVEL < IF PUMPOFF THEN 75 LEVEL > IF PUMPON THEN ;
We could remove the 25 and the 75 from the code, making it more readable, by 'hiding' the 25 and 75 in their own words, thus 'factoring them out' of the CONTROL definition: How about if we had a word called TOOHIGH? and a word called TOOLOW? Hmm, sounds promising:
: TOOHIGH? 75 > ;
: TOOLOW? 25 < ;
That means, we could get the tank level with LEVEL and simply 'ask the question' “too low?” or “too high?”:
LEVEL TOOLOW? IF PUMP OFF THEN
LEVEL TOOHIGH? IF PUMP ON THEN
So now, we can write CONTROL as:
: CONTROL LEVEL TOOLOW? IF PUMP OFF THEN LEVEL TOOHIGH? IF PUMP ON THEN ;
The word PUMPCONTROL can be left as it is, since it is just calling CONTROL in an endless loop. Thus our complete program is:
: PUMP $B000 ;
: ON 1 SWAP ! ;
: OFF 0 SWAP ! ;
: LEVEL <read tank level> ;
: TOOHIGH? 75 > ;
: TOOLOW? 25 < ;
: CONTROL LEVEL TOOLOW? IF PUMP OFF THEN LEVEL TOOHIGH? IF PUMP ON THEN ;
: PUMPCONTROL BEGIN CONTROL AGAIN ;
Our program is a little larger than before, but it is also easier to read, because at the high level (the word CONTROL) the program almost reads as plain English. We have factored the program into smaller pieces, making it easier to read, and easier to test (as we have more, smaller, testable pieces).
The Forth environment's convenient and powerful debugging and error control features are an important advantage of the system. Forth allows complete access to the machine, without the restrictions of many other languages such as BASIC and Pascal which try to guard the programmer against mistakes. Most users report that Forth allows them to quickly produce and modify programs which are exceptionally reliable.
Although TurboForth includes some compile-time checking which detects most of the detectable errors such as unterminated loops, the most important error control is in the tools which the Forth environment itself gives to the programmer. Like most other modern languages, Forth encourages "structured programming" design techniques, which helps to control errors. Forth is extremely modular, even compared to other structured languages; each software module can be tested and debugged independently. Usually all communication between a module and the outside world is through an internal stack. Each module relies on earlier modules which have already been debugged, and in turn, the new testing helps catch any errors that may still be hidden in the earlier work.
Testing is immediate and interactive; simply type arguments onto the stack, execute the word, and output the results. If more elaborate test data is needed, a special word can generate it. This ease of testing means that a large number of tests can be run quickly. Each Forth word should be short, in the programming style preferred by most Forth users, so that all possible paths of control can be tested easily.
If correct results are not obtained, it is possible to step through the definition by executing each component word individually, checking the stack whenever desired. TurboForth has a special word, .S , which non-destructively prints the stack contents to help in this kind of debugging. Any unexpected results can be localised to a particular component word, which in turn can then be examined in detail. Because Forth words work identically when compiled, or when executed as commands, the programmer can debug at either a batch or interactive operation mode.
Because Forth is extensible, words can be re-defined to perform their original functions and, in addition, give special debug print-outs or do run-time error checks. These redefinitions can be inserted into programs for testing and removed later; nothing else in the program need be changed.
TurboForth also includes a memory dump and other words for examining or changing memory. These commands can be compiled into programs or executed from the keyboard.
In contrast to most other operating systems, all of these tools are part of the normal Forth environment. No special syntax or command language must be learned for debugging.
Each Forth word is documented by a glossary which lists the arguments it takes from the stack and the results returned, and gives a short verbal description (usually one to three sentences) of its action. Such a glossary completely describes the word as it is seen by any other part of the program. When a new word is being tested, all earlier words should have these descriptions available. Therefore, the programmer seldom needs to look at the source code of any other word; the glossary fully describes its functions.
During testing and debugging, only one word at a time needs to be examined — this greatly cuts down the need for program listings during development.
What are the essences of Forth that make it different from other languages?
We've learned that Forth is:
<< Home | About Forth | About TurboForth | Download | Language Reference | Resources | Tutorials | YouTube >>