The FIG UK web site and the accompanying magazine Forthwrite has been dormant for some time now for lack of volunteers.
I've established a forum and it is possible, given enough interest, that Forthwrite will return in a new form. That interests you, and there is some way in which you can help, please get in touch.
.
2017: The forum is now defunct. The website address above is still valid (for now) but please link instead to the mirror at the Forth Foundation Library
Friday, November 18, 2005
Wednesday, September 21, 2005
Mixing and Matching Control Structures
Forth uses IMMEDIATE words to build control structures at compile time. The various opening and closing words communicate with each other by passing items on a control stack to ensure proper nesting.
Sometimes, however, strict nesting is not what is needed, and the Standard provides the words CS-PICK and CS-ROLL to change the
order of items on the stack. (0 CS-ROLL is a no-op, 1 CS-ROLL is a swap; 0 CS-PICK is a dup, 1 CS-PICK is an over, and so on...). The details of the control stack and the size and format of items is implementation-dependant, but by far the most common choice is to use the data stack, with either one or two items for each item.
In the examples below I shall use CS-PICK and CS-ROLL explicitly, but they are intended to be used for the definition of new control structure words, as in the examples of ELSE and WHILE below:
: ELSE \ orig1 -- orig2
POSTPONE AHEAD \ orig1 orig2
1 CS-ROLL POSTPONE THEN ; \ consumes orig1
IMMEDIATE
: WHILE \ dest -- orig dest
POSTPONE IF 1 CS-ROLL ; IMMEDIATE
Using WHILE
BEGIN ... WHILE is normally resolved by REPEAT, which is another way of saying AGAIN THEN - but could also be closed with
... UNTIL do this on normal exit only THEN ...
and you can use multiple WHILEs within the loop, each resolved by its own THEN outside
BEGIN ... WHILE ... WHILE ... UNTIL ... THEN ... THEN
But you’ll notice that WHILE does not modify the dest on the top of the control stack. In fact, unless your Forth is overly pedantic, any other item will do as well. For example, in:
IF ... WHILE ... THEN ... THEN
(I don't recommend this - each time I look at it I have to work out again what it actually does).
Generally it is best to strictly nest both forward branches and loops, but it is sometimes useful to branch out of a loop (as in the examples above) or into the middle of a loop:
IF BEGIN maybe ignore first time [ 1 CS-ROLL ] THEN the rest of the loop UNTIL
WHILE may equally well be used to exit a DO ... LOOP, but here you need to use UNLOOP to get rid of the redundant loop index.
DO ... WHILE ... LOOP normal exit ELSE early exit UNLOOP THEN
is very messy, even more so with multiple WHILEs. It would be better to place the ELSE clauses within the loop, changing the sense of the test:
DO ... IF early exit UNLOOP ELSE [ 1 CS-ROLL ] ... LOOP normal exit THEN ...
Of course. if the definition ends at THEN, you can simply do an early return
DO ... IF early exit UNLOOP EXIT THEN ... LOOP normal exit
Or if you just want to exit the loop and perform whatever code follows in all cases:
DO ... IF early exit LEAVE THEN ... LOOP all exits
This can be done using CS-PICK. The simplest example mimics the Java’s continue keyword:
BEGIN ... WHILE ... [ 0 CS-PICK ] REPEAT ... UNTIL
DO ... WHILE ...[ 0 CS-PICK ] REPEAT ... LOOP
may possibly compile too, but is not guaranteed to work, since do-sys may not be identical to dest. In this case, since the loop counter is decremented by LOOP rather than DO, each time the program hits REPEAT it effectively repeats the same iteration.
The same trick can be used within a CASE statement. Suppose you want the same action for a range of values, with one exception:
CASE
exception OF do-exception ENDOF
DUP range WITHIN IF DROP do-range ELSE [ 1 CS-ROLL ]
...
ENDCASE
THEN
On some (perhaps most) Forths this is unnecessary, since OF is the strict equivalent of OVER IF DROP and so can be paired with THEN, or IF paired with ENDOF.
Of course, if you find this uselful, it's better to define and document your own control words:
: DONE \ branch out of the current loop (resolved by implicit or explicit THEN
POSTPONE ELSE 1 CS-ROLL ; IMMEDIATE
: REPRISE \ return control to nearest BEGIN
0 CS-PICK POSTPONE REPEAT ; IMMEDIATE
What goes on the control stack
- BEGIN leaves dest to be consumed by UNTIL or AGAIN
- AHEAD and IF leave orig to be consumed by THEN
- (AHEAD compiles an unconditional forward jump)
- DO and ?DO leave do-sys to be consumed by LOOP or +LOOP
- CASE leaves case-sys to be consumed by ENDCASE
- OF leaves of-sys to be consumed by ENDOF
Sometimes, however, strict nesting is not what is needed, and the Standard provides the words CS-PICK and CS-ROLL to change the
order of items on the stack. (0 CS-ROLL is a no-op, 1 CS-ROLL is a swap; 0 CS-PICK is a dup, 1 CS-PICK is an over, and so on...). The details of the control stack and the size and format of items is implementation-dependant, but by far the most common choice is to use the data stack, with either one or two items for each item.
In the examples below I shall use CS-PICK and CS-ROLL explicitly, but they are intended to be used for the definition of new control structure words, as in the examples of ELSE and WHILE below:
: ELSE \ orig1 -- orig2
POSTPONE AHEAD \ orig1 orig2
1 CS-ROLL POSTPONE THEN ; \ consumes orig1
IMMEDIATE
: WHILE \ dest -- orig dest
POSTPONE IF 1 CS-ROLL ; IMMEDIATE
Using WHILE
BEGIN ... WHILE is normally resolved by REPEAT, which is another way of saying AGAIN THEN - but could also be closed with
... UNTIL do this on normal exit only THEN ...
and you can use multiple WHILEs within the loop, each resolved by its own THEN outside
BEGIN ... WHILE ... WHILE ... UNTIL ... THEN ... THEN
But you’ll notice that WHILE does not modify the dest on the top of the control stack. In fact, unless your Forth is overly pedantic, any other item will do as well. For example, in:
IF ... WHILE ... THEN ... THEN
(I don't recommend this - each time I look at it I have to work out again what it actually does).
Generally it is best to strictly nest both forward branches and loops, but it is sometimes useful to branch out of a loop (as in the examples above) or into the middle of a loop:
IF BEGIN maybe ignore first time [ 1 CS-ROLL ] THEN the rest of the loop UNTIL
WHILE may equally well be used to exit a DO ... LOOP, but here you need to use UNLOOP to get rid of the redundant loop index.
DO ... WHILE ... LOOP normal exit ELSE early exit UNLOOP THEN
is very messy, even more so with multiple WHILEs. It would be better to place the ELSE clauses within the loop, changing the sense of the test:
DO ... IF early exit UNLOOP ELSE [ 1 CS-ROLL ] ... LOOP normal exit THEN ...
Of course. if the definition ends at THEN, you can simply do an early return
DO ... IF early exit UNLOOP EXIT THEN ... LOOP normal exit
Or if you just want to exit the loop and perform whatever code follows in all cases:
DO ... IF early exit LEAVE THEN ... LOOP all exits
Branching to a Common Start
This can be done using CS-PICK. The simplest example mimics the Java’s continue keyword:
BEGIN ... WHILE ... [ 0 CS-PICK ] REPEAT ... UNTIL
DO ... WHILE ...[ 0 CS-PICK ] REPEAT ... LOOP
may possibly compile too, but is not guaranteed to work, since do-sys may not be identical to dest. In this case, since the loop counter is decremented by LOOP rather than DO, each time the program hits REPEAT it effectively repeats the same iteration.
The same trick can be used within a CASE statement. Suppose you want the same action for a range of values, with one exception:
CASE
exception OF do-exception ENDOF
DUP range WITHIN IF DROP do-range ELSE [ 1 CS-ROLL ]
...
ENDCASE
THEN
On some (perhaps most) Forths this is unnecessary, since OF is the strict equivalent of OVER IF DROP and so can be paired with THEN, or IF paired with ENDOF.
Of course, if you find this uselful, it's better to define and document your own control words:
: DONE \ branch out of the current loop (resolved by implicit or explicit THEN
POSTPONE ELSE 1 CS-ROLL ; IMMEDIATE
: REPRISE \ return control to nearest BEGIN
0 CS-PICK POSTPONE REPEAT ; IMMEDIATE
Thursday, September 15, 2005
All the Standard Words
Somehow this puts me in mind of C K Ogden and his 850 words of Basic English...
Neal Bridges has produced a four-sheet reference for Standard Forth -- all 371 words with their respective Standard stack diagrams. It's in PDF format, and if printed '4-up' makes a pretty handy single-sheet reference (if a bit hard on the eyes).
I haven't seen this anywhere else in so compact a form. It's very useful for reminding yourself what words are in the Standard and how they are spelt.
Neal Bridges has produced a four-sheet reference for Standard Forth -- all 371 words with their respective Standard stack diagrams. It's in PDF format, and if printed '4-up' makes a pretty handy single-sheet reference (if a bit hard on the eyes).
I haven't seen this anywhere else in so compact a form. It's very useful for reminding yourself what words are in the Standard and how they are spelt.
Friday, August 06, 2004
Win32Forth discussion group
Win32Forth is probably the oldest-established Windows-based Forth for hobbyists. It has recently received a lot of development. A discussion group has been set up at http://groups.yahoo.com/group/win32forth/ and now has over 170 subscribers.
Forth Wikis
The original and biggest Wiki http://c2.com deals with "Extreme Programming" and Software Patterns. The Extreme Programming methodology has a lot in common with good Forth practice, so it is no surprise to find http://c2.com/cgi/wiki?ForthLanguage Any Forth content added to this wiki should be relevant to the main themes in order to aviod forming a "Walled Garden" - a sub-wiki with no links out to the main body.
What is different about Forth?
I like this summation in a comp.lang.forth Usenet post by Joe Knapka, and particularly the reply from Jeff Fox, one of great names of Forth
Joe Knapka's post
Date: Wed, 07 Apr 2004
Subject: Re: ANN : Forth Versus C - some Infrequently Asked Questions
Date: 11 Apr 2004
In the light of Jeff's reminder that "Forth is based on the word," it might be a good idea to have a look at one of the classic references for writing clear English: William Strunk's Elements of Style
Compare:
to:
Joe Knapka's post
Date: Wed, 07 Apr 2004
Subject: Re: ANN : Forth Versus C - some Infrequently Asked Questions
Jeff's reply:
Agreed. I'm trying to like Forth, but a lot of the Forth evangelism
really reads as if it's directed toward cult members: "People who use
other languages often think X, Y, or Z. Those people are misguided;
you can tell because it takes them three times as long to write their
code. They don't care about making high-quality products; only *we*
care about making high-quality products. Drink the Kool-Aid now..."
It's really quite a turn-off. I've produced what I consider to be
high-quality products in perhaps a dozen different languages; I don't
really like being told that's impossible.
It usually works better to simply say, "Welcome to Language X, which
is based on the W principle: everything is a Widget. This Widget-
centric approach addresses the following specific perceived
shortcomings of the object-oriented, functional, and thingamajig-based
paradigms: .... Language X also provides the following specific
unique features that are not available in any other language: ..."
People who are intrigued by the introduction will then dive in
and write some code, and decide for themselves whether they like
it.
Let's see.
"Welcome to Forth, a mid-level, interactive, stack-based
language. Forth's interactive and stack-based nature addresses the
following specific perceived shortcomings of other mainstream
languages:
- Unlike many popular languages, Forth is fully-interactive; no
edit/compile/test cycle is required. Instead, you can edit and run
code directly from the Forth command line. This means that
exploratory programming is very easy in Forth. (However, the fact
that control structures usually can't be used outside procedure
definitions somewhat limits one's ability to run ad-hoc code at
the command prompt. [Does any Forth permit loops etc in
interpreted code?])- Unlike many popular scripting languages, Forth can compile
directly to native code, which means you can have native-code
performance in a fully-interactive environment.- Unlike many popular languages, Forth does not impose any
particular structure to your code (no "everything is an X"); you
are free to use procedural, structured, object-oriented, and
limited functional techniques as circumstances warrant. (However,
you may need to build your own toolbox.)- Unlike virtually all popular languages, a complete Forth
development environment can run in a surprisingly small memory
footprint (a few 10's of K for a really large, highly-functional
implementation), and without an underlying general-purpose
operating system. Furthermore, the Forth compiler typically
produces very compact object code. This makes Forth ideal for
exploratory programming in embedded and resource-constrained
environments.- Unlike most popular languages, Forth makes it possible and natural
to directly manipulate the hardware on which the Forth system
runs. Such direct manipulation can be done from the Forth command
line -- another advantage when doing exploratory development in
embedded environments.- Unlike most popular languages, Forth is extensible - it allows the
programmer to add to the language facilities (such as control
structures) that are completely indistinguishable from the
built-in facilities. Forth takes you beyond the ability to design
libraries, and makes you a partner in the design of the language
itself; building a high-level language atop the Forth environment
is therefore fairly simple. Further, any such high-level language
can be made immediately available to the user as well as the
programmer. (Note: do not use this power in the service of evil!)- Unlike nearly all popular languages, the internal structure of a
Forth development environment is extremely simple and easy to
understand, even for folks with little programming experience.
While to an experienced C programmer, for example, Forth may seem
to be full of strange inconsistencies and edge cases, a full
understanding of the nature of Forth makes it clear that all of
its features are natural consequences of its simple and elegant
implementation; and such a full understanding is easy to achieve.
Date: 11 Apr 2004
I would have probably said something similar to describe Forth
in my first twenty years of working with the language. I have
come to think of it as the most common misconception about Forth
among Forth programmers.
Forth is based on the word. Words are like subroutines but
shorter. To facilitate Forth style factoring of code into
words a parameter passing stack is used.
Words also are names. To allow to programmer to focus on the
things that are important enough to be given names there is a
parameter stack where items do not have names.
These aspects of Forth address shortcomings of languages with
rigid syntax and unchangable keywords. The use of short word
definitions increased code re-use and eliminates much of the
work required in other languages to duplicate sections of
their own code. Short word definitions are easier to write,
debug, and maintain.
Forth only requires single pass compilation which in combination
with the use of highly factored code facilitates very fast
compilation times and supports a very fast interactive edit-
compile-test cycle. Forth uses a dictionary to contain and
access words by their name. The dictionary is similar to
the ubiquitous file directories that most programmers use
except that they are early bound and under explicit control
of the programmer while file directories add runtime overhead
by being late bound and remove control from the programmer to
give it to a file system.
In short, Forth uses two stacks as a mechanism to support
a language based on space delimited words. I think it helps
to realize that Forth is word based, not stack based.
If a programmer focuses on stacks as the basis of the language
it is likely that a programmer will not be as focused on factoring
into words, will write stack juggling routines, or may even start
giving stack items names.
I know for the first twenty years that I did Forth while thinking
of it as a stack based language I wrote more code than I needed to
write and made it less readable, less maintainable, and generally
speaking experienced less productivity than when I approached
Forth as a word based language. Of course I am sure that there
were other factors as well that contributed to my learning to
use the language more effectively.
In the light of Jeff's reminder that "Forth is based on the word," it might be a good idea to have a look at one of the classic references for writing clear English: William Strunk's Elements of Style
Compare:
The use of short word definitions increased code re-use and eliminates much of the work required in other languages to duplicate sections of their own code. Short word definitions are easier to write, debug, and maintain.
to:
Vigorous writing is concise. A sentence should contain no unnecessary words, a paragraph no unnecessary sentences, for the same reason that a drawing should have no unnecessary lines and a machine no unnecessary parts. This requires not that the writer make all his sentences short, or that he avoid all detail and treat his subjects only in outline, but that every word tell.
Subscribe to:
Posts (Atom)