Skip to content

eForth FOR .. NEXT

Thomas edited this page Jun 2, 2020 · 10 revisions

Discussion of 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".

Basic properties of eForth FOR .. NEXT

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.

eForth FOR .. AFT .. THEN .. NEXT

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.

eForth FOR .. WHILE .. NEXT .. ELSE .. THEN

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.

Conclusion

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.

Clone this wiki locally