20th Anniversary: Forth data structures
The following article first appeared in the September 1989 issue of Embedded Systems Programming magazine.
Whenever I hear some lout claim that Forth code is unreadable, I'm reminded of the tourist who, on returning from Paris, marveled at the French: "Their children are so smart! Why, even the youngest already speak French!" To the Forth programmer, mathematical expressions using reverse Polish notation are no odder than reading right to left is to an Arab; stack-based parameter passing is no stranger than case declension is to a speaker of Czech or Polish.
Having said all that, I must confess that Forth code isn't always as readable as it could be. Often what makes Forth seem incomprehensible is not the language itself but a programmer's poorly factored code or poorly conceptualized data structures. The latter is especially true of Forth programs for control applications. It doesn't have to be this way, of course, and to prove it I'll be presenting several examples of data-structure conceptualization.
All the examples discussed here follow the Forth 83-Standard, with particular reference to the "vanilla" Laxen and Perry F83 public-domain Forth system unless otherwise noted. I'll presume the reader has a working knowledge of Forth and knows about the CONSTANT and VARIABLE words as well as the CREATE ...DOES> construct. As we'll be using examples from control applications, an occasional PC@ or PC! is thrown in to fetch and store bytes in the Intel 808x I/O space.
A few years back I did some consulting for an automotive equipment engineer who had left the shelter of the corporate world for the perils of private entrepreneurship. He had built a marvelously inexpensive and useful piece of automotive repair equipment, then mastered the trigonometric calculations necessary to achieve the desired results. After teaching himself assembly language and beginning to write the program, he switched to Forth to increase his personal productivity. He finally decided to hire a full-time Forth consultant to complete his masterpiece, which was "beginning to be difficult." When we began factoring the code, I saw that the program had indeed become difficult; it started out with 10 screens of variable declarations!
2 CONSTANT CELL \ 16-bit processor : CELLS ( n1 --- n2) CELL * ;
and a word that can create other words:
: ARRAY ( #cell-entries ---) CREATE CELLS ALLOT DOES> ( index --- address) SWAP CELLS + ;
We then created the array and its access methods:
4 CONSTANT #FORCES 0 CONSTANT LEFT 1 CONSTANT RIGHT 2 CONSTANT UP 3 CONSTANT DOWN#FORCES ARRAY FORCE
Note that we used symbolic constants in place of numeric literals, even at the level of the number of elements we're going to declare in our array. This practice ensures that future modifications to the program entail simply changing a few constant declarations rather than hunting through the code for a specific occurrence of a number.