Linux Goodies

In Pursuit Of The Perfect O/S



Forth Programming Tips And Tricks For Linux Users

Amazon Computers

Linux Programming Tips, Hints, And Tricks

Learn from these smart tips and hints about Forth programming. These tips are not guaranteed to work for you, but hopefully you'll learn a thing or two.

Hints Index

A Simple Array Notation For Forth
A Simple Forth Programming Environment
An Easy Reload Command For Gforth
Local Variables
Speedup Programming With Local Variables

Title: A Simple Array Notation For Forth

If you commonly work with arrays in Forth, here's a simple array notation that you might find useful.

I name my arrays with a trailing { symbol, like

create names{ element_size nelements * allot

Then I create an index operator starting with the } symbol, usually followed by a letter or short descriptor that describes the type of array being indexed. Something like }n or }name. So a reference to an array element would look like this:

name{ 5 }n

for the 6th element in the array. The operator simply leaves the address of the desired element on the stack. For example, lets say I was using a 40 character max length for my name array, and a standard counted string could be stored in each 40 character field. Then the index operator definition would look like this:

: }n ( adr n -- nthadr ) 40 * + ;

I always load a small dictionary that extends the language when I'm working, and when I run my programs. In that extended dictionary I define a number of commonly used indexing operators, like }c to index character array elements, } to index cell array elements, }f to index floating point array elements, and }" to indicate counted string array elements. See if the idea doesn't work for you. For me it makes my Forth code seem easier to read, when the array work looks much like that in any other computer language.

Return To Index

Title: A Simple Forth Programming Environment

I program in both a forth I wrote years ago, and gforth. In both of these I took advantage of the extensibility of the language to create a simple programing environment that provides the ease of use of the old block file system, even when working with forth within another operating system (like Linux).

I created a character array in each that had the word vim, including a trailing space and an allocation for enough additional characters to hold a file name. It looks like this:

create (edit) ," vim " 80 allot

Then I created a word called open that lets me enter a file name, which it appends to the (edit) word. It looks like this:

: open ( -- )
  1 text ( get the file name into pad)
  4 (edit) c! ( reset the length of the edit command, including blank)
  pad count (edit) count + swap cmove ( append file name to command)
  pad c@ (edit) c@ + (edit) c! ( set the total length of the cmd)

Next, I created a simple edit command named ed. Simply typing ed puts me in edit mode, and when I exit I can try out the code. Simple, but very handy and effective. The ed command looks like this:

: ed ( -- ) (edit) count system ; ( tell system to run edit command)

This is the code for gforth. About the only thing you might need to do for your forth is to create the text word (it just uses word and copies here to pad), and replace system with whatever command lets you pass a string to the operating system.

Return To Index

Title: An Easy Reload Command For Gforth

From web browsing I've noticed that others who've tried gforth have been a bit puzzled on how to avoid getting all the redefined messages when re-compiling modified code. I dealt with the problem by creating an open word that stores my desired program code file in an edit string, and a reload string. Then I created a reload word that loads an empty.fs file (which forgets all my compiled code), then reloads my program file. The empty.fs file looks like this:

[IFDEF] empty
marker empty

I needed a string to hold the file name I was working on

create (reload) 80 allot

The open command, described in the previous hint, also has this:

: open ( -- )
1 text
pad (reload) pad c@ 1+ cmove

The reload command looks like this:

: reload ( -- )
s" empty.fs" included
(reload) count included

In use, I enter gforth including my extended dictionary that has the text word, the open, word, the reload word, and many other words I find useful. Then I enter the open word followed by the program file I want to work on. After each editing sequence, I simply type reload to forget and re-compile my code for testing. Edit, then reload, then test.

If you don't have a text word, it looks like this:

: text ( mark -- ) \ gather following text till mark and put into pad
word count dup >r pad 1+ swap cmove r> pad c!
Return To Index

Title: Local Variables

This is my weigh-in on the use of local variables in Forth. I have quite a bit of experience using Forth in that I've used it both at home and on the job for over 20 years. Even so, I'm sure that many Forth experts would consider my code to be sub-optimal quality.

But I have created some pretty functional code that has been used for years and has served well, so I figure I can weigh in as well as the next guy. To be honest, I've been a bit disconnected from the Forth community for some time, having used mostly a version of Forth of my own making. Creating my own version wasn't an exercise or act of vanity, but the solution for the need of a version of Forth that could give full floating point support. To that end, I wrote a version of Forth using the c language, and patterned it after the Forth 79 standard.

Since Forth is so extensible, I've been able to solve my problems nicely with that version for all these years, so I haven't paid much attention to where Forth was going. Recently I've been trying to get caught up a bit by working with the GNU gforth language. I've written a review of my experiences with gforth, which you can read at the Review of Gforth page. Since working with gforth, I've discovered that the ANSI Forth standard has a model for local variables in Forth. Since I've programmed in a number of other languages at least as much as I have with Forth, I found the local variables to be very helpful. I also found when doing some reading that a number of Forth versions have added some implementation of local variables at least a decade ago.

I ran some performance tests with gforth to see how the use of local variable performed compared with other methods of Forth coding. I used a simple test, the object of which was to pass 3 variables (call them a, b, and c) to a word, and have the word compute and return the sum of products ab+bc+ac. This was enough of a problem to make a bit of stack juggling necessary if no variables were used. The results from performing each method the same thousands of times are shown in the following table:

Solution MethodSolution Time
Using Stack Operations9 seconds
Using Global Variables15 seconds
Using Global Values19 seconds
Using Local Values19 seconds

The reference in the table to global variables refers to the classic Forth variables that one must fetch and store. The Global values refers to the Forth values that use the => operator to replace the value. As you can see, while there is a bit of overhead to using local variables vs using only stack operations, it isn't terribly costly, and is about the same cost as using Global variables.

I used gforth to write a program that loads star catalogs. The program lets me select objects to view, and tells me where the point my telescope in order to view the objects. The program has about 50 words or routines in it, and I found that only two of the routines were clumsy enough that local variables helped keep them readable and maintainable. Putting that into perspective, it suggests that even though there is a small time cost in using local variables vs doing stack gymnastics, a typical programmer will probably only need or use local variables in a small percentage of routines, making the tradeoff of code readability vs efficiency a very tolerable tradeoff.

In my general work, I don't use GNU Forth that often. I actually use an old Forth compiler I built by studying a FIG Forth manual. I altered the code just a bit to more resemble F79, as the programming documentation I liked referenced F79. I created my version of Forth with a C compiler, specifically so that I could add floating point support. I've used the system for years. A couple of years ago after reading some suggestions about local variables, I added a simple local variable method to my Forth compiler.

My simple system uses a couple of additional stacks in the C code that hold local and floating point values. A couple of routines keep track of the starting point in these stacks for Forth words that declare the need for local variables. At the end of these words a locals release word is required to restore the locals stack pointer to its previous level. In this way, each Forth word declaring need for locals is using a different area of the locals stack. When I say required, I mean pairs checks for it as it would a then for each if. The locals pointers are similar to the return stack pointer used by the general Forth compiler.

The locals stack pointer is only used when locals are needed and released. In my brain-dead, simplistic approach, the locals always have the same names, n0 through n5 for integer locals, and f0 through f5 for floating point. I use comment statements at the point of declaration to show what the generic names actually relate to. The following is an example of use:

 : sumprod ( a b c -- ab+bc+ac)
   3 locals ( n0=a, n1=b, n2=c) \ declare need for 3 locals
   n0 n1 * n1 n2 * + n0 n2 * + \ do the math
   locals-end \ release locals

In the above example above, the locals declaration moves the locals stack pointer up past any previous declaration point and pops 3 values from the stack and puts them in local variables n0, n1, and n2. The locals-end declaration moves the locals pointer back to the previous value. In this way, each reference to the n0 ... variables points to the current word's values.

The floating point version uses flocals to declare need for locals, and flocals-end to release the support. The scope of the locals is between the declaration and release, so words referenced within this scope also have access to the same locals, unless they declare need for their own locals.

To thumb one's nose completely at the use of local variables seems to suggest to me that developers of other languages like c, perl, scheme, or whatever, have had no good ideas whatsoever, since they all make great use of local variables. I don't find that an acceptable point of view, and have found something quite clever about nearly every computer language I've ever studied.

So my view is that one should consider using local variables anytime that stack operations in a word definition become unwieldy. If factoring a complex word down into a few simpler words is relatively obvious, then factoring is probably a better approach, and will likely create faster code. But at least for me, sometimes I find too much time disappearing while I fumble with factoring. In those instances, using a few local variables lets me quickly get back to working on the rest of my program. And as my star catalog program suggests, generally only a few words in each program seem well suited for local variable use.

So I say -- why not.

Return To Index

Title: Speedup Programming With Local Variables

Of late, I've been using a modified form of a Forth coding technique that I learned long ago when using Forth on microcomputer projects. In that time honored approach, I would write an application entirely in high-level Forth code so that I could minimize development time. Then I would examine the program and identify words that were frequently referenced, such as within loops. These words were then coded in Forth assembly code to make them more efficient. With this approach, a very fast running program could be created even though only a few words were actually written in Forth assembly. The procedure worked well because words called often within loops do perhaps 90% of the actual work. Just optimizing these few words could make a huge difference in execution speed of a program.

Most of my programming nowadays is not on microcomputer projects, but on creating utilities for my desktop. I still write many of these utilities in Forth for a couple of reasons. One, I enjoy coding in Forth. Two, compared to most scripting languages, the Forth engine is very small, about 1/10 the size of perl, for example. So Forth produces fast running code compared to most scripting languages, and loads very quickly, making it a nice utility scripting language.

Since I don't work much with microprocessors at the current time, I don't delve into the Forth assembly language very often. But I do find the assembly optimization concept still useful. In my application of the old optimization approach, I create my first draft by making copious use of local variables, using them whenever a stack manipulation sequence or factoring procedure isn't obvious. This lets me create readable, functional code quickly.

Then, I examine the code and identify words that are called frequently. I then work to factor these words so that I can eliminate the use of local variables. In this way, only high level (infrequently called) words carry the overhead of local variables. By only doing the extra work on a handful of frequently called words, I keep the programming time manageable. The final result is code that is created quickly, and code that is readable, maintainable, and runs fast.

I recently ran some tests to understand the costs of using local variables in my home-grown Forth language. I found that the difference in time between local variables and global variables is nil. But there is a measurable difference in local variables and stack operators. My results showed what in retrospect is an obvious solution: just don't use local variables (if possible) within inner loops. If a local variable value is needed within an inner loop, put it on the stack before entering the loop and use stack operators to work with it. This simple adjustment virtually negates any speed loss of using locals.

This follows along the old advice about looking for bottlenecks in code and converting those bottlenecks into assembly. That generally involves only programming an inner loop or so in assembly, as the inner loops are where all the work is done. With my particular Forth I don't have the assembler option, but I do find that using local variables isn't a time issue as long as I avoid referencing them (and global variables for that matter) within inner loops -- similar reasoning as pertains to the advice about assembly language inserts.

Return To Index