Hi 4tH-ers!
There was an interesting discussion on c.l.f. on the despised DO..LOOP (or more exotic ?DO..+LOOP).
Everybody agreed that you could not let two words (?DO and +LOOP) decide on where to jump. So we
started discussing the alternatives.
This is the 4tH implementation of the result. Up till now we have *five* tokens dedicated to DO..LOOP.
This implementation could reduce that to *two*. The resulting DO.LOOP should be largely compatible with
the current DO..LOOP, but it also allows an entirely new construct: FOR..STEP..NEXT.
But of course, there are also a few disadvantages:
- No more distinction between DO and ?DO;
- Consequently, THERE IS NO "GRACE EXECUTE" IN LOOP ANYMORE (?DO behavior is standard);
- Completely handled by "inline macros" (a more cryptic decompile).
In essence, it only takes these few lines code:
CODE (FOR) DSIZE (2); a = DPOP; RPUSH (DPOP); RPUSH (a); NEXT;
CODE (STEP) RSIZE (2); DSIZE (1);
RS (1) += DPOP;
DPUSH (((DS(0) > 0L) ? (RS(1) < RS(2)) :
(RS(1) > RS(2))) ? F_T : F_F);
NEXT;
The first is identical to DO - it pushes two parameters on the return stack. The second is derived
from +LOOP - BUT IT DOESN'T JUMP! It simply leaves a flag on the datastack. That flag is handled by
a WHILE. Since this word is the only one that jumps, LOOP can essentially be reduced to "REPEAT UNLOOP".
And now you're getting it: WE'RE COMPLETELY EXPRESSING DO..LOOP IN TERMS OF BEGIN..WHILE..REPEAT!
Added advantage: we can do "WHILE" and "DONE" within a DO..LOOP with no ill effects.
The new *TOKENS* (for) and (step) have the following stack effects:
(for) n1 n2 -- (R: n1 n2)
(step) n1 (R: n2 n3) -- f (R: n2 n3+n1)
We also need a token for RDROP - but that one is useful in itself, of course.
The FOR keyword compiles to:
FOR
<nothing compiled> [=begin]
The STEP keyword compiles to:
STEP
0BRANCH (addr REPEAT) [=while]
The NEXT keyword compiles to:
BRANCH (addr BEGIN) [=repeat]
RDROP (0)
RDROP (0)
So this is the next loop construct for 1..10
11 0 FOR 1 STEP i . NEXT
Which compiles to:
LITERAL (11)
LITERAL (0)
FOR
LITERAL (1) [begin]
STEP
0BRANCH (repeat)
R@
.
BRANCH (begin)
RDROP [repeat]
RDROP
DO compiles to:
FOR
LITERAL (0)
<nothing compiled> [=begin]
+LOOP compiles to the same code as NEXT, while LOOP compiles to 1 +LOOP.
So a classic loop construct for 1..10:
11 1 DO i . 1 +LOOP
Compiles to:
LITERAL (11)
LITERAL (1)
FOR
LITERAL (0)
STEP [begin]
0BRANCH (repeat)
R@
.
LITERAL (1)
BRANCH (begin)
RDROP [repeat]
RDROP
LEAVE can be reduced to:
RDROP
R@
>R
UNLOOP can be reduced to:
RDROP
RDROP
So, reasons for doing so:
- The *main* reason for doing this is fix a flawed DO.LOOP and make it consistent;
- Introduce a clearer and new FOR..STEP..NEXT construct;
- Let DO..LOOP join the BEGIN..WHILE..REPEAT family;
- Consequently, allowing for WHILE keywords within DO..LOOP.
Still, this won't be arriving in v3.63.0. It's just too late for that. I'll experiment with it after that - and only if I find
no adverse effects while testing - but I don't think so. Very few people use DO..LOOP because it executes once.
But we'll see. Share your thoughts..
Hans Bezemer