-
Notifications
You must be signed in to change notification settings - Fork 66
eForth FOR .. NEXT
Among "small Forth implementations" the eForth FOR .. NEXT
construct is unique, and some of its properties and idioms are rather difficult to understand. The following discussion deals with the main properties, idioms and limitations of the this "eForth-ism".
A the basic FOR .. NEXT
loop accepts a loop count parameter n
:
: test-for ( n -- )
FOR I . NEXT ;
NEXT
decrements the loop counter on the return stack (accessible through I
or R@
) and loops while the counter is positive. 4 test-for
prints 4 3 2 1 0
, 0 test-for
prints 0
, and -1 test-for
prints -1
.
The following FOR .. NEXT
properties are noteworthy:
- at least one iteration, with the loop counter equal to the input to
FOR
, will be performed, - the loop count will be "one-too-many" compared to standard loop structures (e.g.
DO .. LOOP
) and - the number of iterations is limited to 32768
The implementations of the word FOR
and NEXT
are very simple: at compile time, FOR
puts the next code address on the data stack. The compiled code puts the loop counter on the return stack. An alternative way to use a FOR .. NEXT
loop is this:
: alt-for ( n -- )
>R BEGIN I . NEXT ;
At compile-time NEXT
pops the loop address BEGIN
address for the NEXT
run-time code from the data stack. The run time code then works with the loop counter on the return stack, and removes it after the last iteration.
The most unusual feature eForth provides is the word AFT
:
: test-aft1 ( n -- )
FOR
." for" \ first iteration
AFT
." aft" \ following iterations
THEN
I . \ all iterations
NEXT ;
2 test-aft1
prints for 2 aft 1 aft 0
. Why one would need such a control structure isn't obvious. The eForth core implementation use of AFT
shows that it not only works around the "one-too-many" iterations in FOR .. NEXT
, but it also provides loop count testing if the actual "loop code" is in the AFT .. THEN
block:
: test-aft2 ( n -- )
FOR AFT
I .
THEN NEXT ;
Running 3 test-aft2
prints 2 1 0
, and 0 test-aft2
or -1 test-aft2
print nothing at all. This structure is widely used in the eForth core, e.g. in CMOVE
, SAME?
and TYPE
. The effect is that input testing around FOR .. NEXT
isn't needed, especially for 0
counts.
The code for AFT
on page 42..43 of eForth Overview looks a bit cryptic:
: >MARK ( --A ) HERE 0 , ;
: AHEAD ( --A ) COMPILE branch >MARK ; IMMEDIATE
: AFT ( a --a A ) DROP [COMPILE] AHEAD [COMPILE] BEGIN SWAP ; IMMEDIATE
With the explanation above the implementation easy to understand:
- drop the loop address from
FOR
- insert an unconditional jump to
THEN
(which will be executed once) - put a new loop address for
NEXT
on the stack
This example also shows how in Forth the compiler can be extended on the fly.
After the introduction above, we'll look at a loop structure similar to DO .. LEAVE .. LOOP
in the eForth core. It's readability isn't stellar since it was implemented in "pure eForth" with an idiomatic combination of FOR .. NEXT
, WHILE
, and ELSE .. THEN
:
: test-for-while ( limit n -- )
FOR
DUP ( limit ) I = NOT WHILE
I . \ limit not reached
NEXT
." end"
ELSE
." limit"
R> DROP \ remove the FOR loop counter - the NEXT runtime couldn't do it
THEN
DROP \ drop limit
;
In this example, 5 10 test-for-while
prints 10 9 8 5 6 limit
and 5 4 test-for-while
prints 4 3 2 1 0 end
.
WHILE
puts the address of its conditional branch target on the stack (above the loop address from FOR
which is used when compiling NEXT
). During compilation, ELSE
then uses this address to write the "exit address" to the branch target of WHILE
. The ELSE
clause is responsible for removing the loop counter from the return stack, which has to be done manually. Failing to do that will lead to a crash. A less hacky implementation of this structure would at least provide a special word for ELSE R> DROP
(but it's better to just implement DO .. LEAVE .. +LOOP
- that's what STM8 eForth did).
Note that the FOR .. WHILE .. NEXT .. ELSE .. THEN
idiom can't be mixed with the FOR .. AFT .. THEN .. NEXT
idiom.
Due to the implicit input range checking FOR .. AFT .. THEN .. NEXT
is useful for creating minimal Forth systems, even if the code is difficult to understand until one gets used to it. For improving readability it's advisable to use the STM8 eForth DO .. LEAVE .. LOOP/+LOOP
structure instead of FOR .. NEXT
and its variants.