A new DO..LOOP?

19 views
Skip to first unread message

The Beez

unread,
Jul 23, 2019, 9:09:28 AM7/23/19
to 4tH-compiler
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


The Beez

unread,
Jul 23, 2019, 10:51:36 AM7/23/19
to 4tH-compiler
Of course, this should be DO:

DO compiles to:
FOR
LITERAL (0)
<nothing compiled>    [=begin]
STEP
0BRANCH (addr repeat)

HB
Reply all
Reply to author
Forward
0 new messages