;; Phase 3 — control flow (IF/ELSE/THEN, BEGIN/UNTIL/WHILE/REPEAT/AGAIN, ;; DO/LOOP, return stack). Grows as each control construct lands. (define forth-p3-passed 0) (define forth-p3-failed 0) (define forth-p3-failures (list)) (define forth-p3-assert (fn (label expected actual) (if (= expected actual) (set! forth-p3-passed (+ forth-p3-passed 1)) (begin (set! forth-p3-failed (+ forth-p3-failed 1)) (set! forth-p3-failures (concat forth-p3-failures (list (str label ": expected " (str expected) " got " (str actual))))))))) (define forth-p3-check-stack (fn (label src expected) (let ((r (forth-run src))) (forth-p3-assert label expected (nth r 2))))) (define forth-p3-if-tests (fn () (forth-p3-check-stack "IF taken (-1)" ": Q -1 IF 10 THEN ; Q" (list 10)) (forth-p3-check-stack "IF not taken (0)" ": Q 0 IF 10 THEN ; Q" (list)) (forth-p3-check-stack "IF with non-zero truthy" ": Q 42 IF 10 THEN ; Q" (list 10)) (forth-p3-check-stack "IF ELSE — true branch" ": Q -1 IF 10 ELSE 20 THEN ; Q" (list 10)) (forth-p3-check-stack "IF ELSE — false branch" ": Q 0 IF 10 ELSE 20 THEN ; Q" (list 20)) (forth-p3-check-stack "IF consumes flag" ": Q IF 1 ELSE 2 THEN ; 0 Q" (list 2)) (forth-p3-check-stack "absolute value via IF" ": ABS2 DUP 0 < IF NEGATE THEN ; -7 ABS2" (list 7)) (forth-p3-check-stack "abs leaves positive alone" ": ABS2 DUP 0 < IF NEGATE THEN ; 7 ABS2" (list 7)) (forth-p3-check-stack "sign: negative" ": SIGN DUP 0 < IF DROP -1 ELSE DROP 1 THEN ; -3 SIGN" (list -1)) (forth-p3-check-stack "sign: positive" ": SIGN DUP 0 < IF DROP -1 ELSE DROP 1 THEN ; 3 SIGN" (list 1)) (forth-p3-check-stack "nested IF (both true)" ": Q 1 IF 1 IF 10 ELSE 20 THEN ELSE 30 THEN ; Q" (list 10)) (forth-p3-check-stack "nested IF (inner false)" ": Q 1 IF 0 IF 10 ELSE 20 THEN ELSE 30 THEN ; Q" (list 20)) (forth-p3-check-stack "nested IF (outer false)" ": Q 0 IF 0 IF 10 ELSE 20 THEN ELSE 30 THEN ; Q" (list 30)) (forth-p3-check-stack "IF before other ops" ": Q 1 IF 5 ELSE 6 THEN 2 * ; Q" (list 10)) (forth-p3-check-stack "IF in chained def" ": POS? 0 > ; : DOUBLE-IF-POS DUP POS? IF 2 * THEN ; 3 DOUBLE-IF-POS" (list 6)) (forth-p3-check-stack "empty then branch" ": Q 1 IF THEN 99 ; Q" (list 99)) (forth-p3-check-stack "empty else branch" ": Q 0 IF 99 ELSE THEN ; Q" (list)) (forth-p3-check-stack "sequential IF blocks" ": Q -1 IF 1 THEN -1 IF 2 THEN ; Q" (list 1 2)))) (define forth-p3-loop-tests (fn () (forth-p3-check-stack "BEGIN UNTIL (countdown to zero)" ": CD BEGIN 1- DUP 0 = UNTIL ; 3 CD" (list 0)) (forth-p3-check-stack "BEGIN UNTIL — single pass (UNTIL true immediately)" ": Q BEGIN -1 UNTIL 42 ; Q" (list 42)) (forth-p3-check-stack "BEGIN UNTIL — accumulate sum 1+2+3" ": SUM3 0 3 BEGIN TUCK + SWAP 1- DUP 0 = UNTIL DROP ; SUM3" (list 6)) (forth-p3-check-stack "BEGIN WHILE REPEAT — triangular sum 5" ": TRI 0 5 BEGIN DUP 0 > WHILE TUCK + SWAP 1- REPEAT DROP ; TRI" (list 15)) (forth-p3-check-stack "BEGIN WHILE REPEAT — zero iterations" ": TRI 0 0 BEGIN DUP 0 > WHILE TUCK + SWAP 1- REPEAT DROP ; TRI" (list 0)) (forth-p3-check-stack "BEGIN WHILE REPEAT — one iteration" ": TRI 0 1 BEGIN DUP 0 > WHILE TUCK + SWAP 1- REPEAT DROP ; TRI" (list 1)) (forth-p3-check-stack "nested BEGIN UNTIL" ": INNER BEGIN 1- DUP 0 = UNTIL DROP ; : OUTER BEGIN 3 INNER 1- DUP 0 = UNTIL ; 2 OUTER" (list 0)) (forth-p3-check-stack "BEGIN UNTIL after colon prefix" ": TEN 10 ; : CD TEN BEGIN 1- DUP 0 = UNTIL ; CD" (list 0)) (forth-p3-check-stack "WHILE inside IF branch" ": Q 1 IF 0 3 BEGIN DUP 0 > WHILE TUCK + SWAP 1- REPEAT DROP ELSE 99 THEN ; Q" (list 6)))) (define forth-p3-do-tests (fn () (forth-p3-check-stack "DO LOOP — simple sum 0..4" ": SUM 0 5 0 DO I + LOOP ; SUM" (list 10)) (forth-p3-check-stack "DO LOOP — 10..14 sum using I" ": SUM 0 15 10 DO I + LOOP ; SUM" (list 60)) (forth-p3-check-stack "DO LOOP — limit = start runs one pass" ": SUM 0 5 5 DO I + LOOP ; SUM" (list 5)) (forth-p3-check-stack "DO LOOP — count iterations" ": COUNT 0 4 0 DO 1+ LOOP ; COUNT" (list 4)) (forth-p3-check-stack "DO LOOP — nested, I inner / J outer" ": MATRIX 0 3 0 DO 3 0 DO I J + + LOOP LOOP ; MATRIX" (list 18)) (forth-p3-check-stack "DO LOOP — I used in arithmetic" ": DBL 0 5 1 DO I 2 * + LOOP ; DBL" (list 20)) (forth-p3-check-stack "+LOOP — count by 2" ": Q 0 10 0 DO I + 2 +LOOP ; Q" (list 20)) (forth-p3-check-stack "+LOOP — count by 3" ": Q 0 10 0 DO I + 3 +LOOP ; Q" (list 18)) (forth-p3-check-stack "+LOOP — negative step" ": Q 0 0 10 DO I + -1 +LOOP ; Q" (list 55)) (forth-p3-check-stack "LEAVE — early exit at I=3" ": Q 0 10 0 DO I 3 = IF LEAVE THEN I + LOOP ; Q" (list 3)) (forth-p3-check-stack "LEAVE — in nested loop exits only inner" ": Q 0 3 0 DO 5 0 DO I 2 = IF LEAVE THEN I + LOOP LOOP ; Q" (list 3)) (forth-p3-check-stack "DO LOOP preserves outer stack" ": Q 99 5 0 DO I + LOOP ; Q" (list 109)) (forth-p3-check-stack ">R R>" ": Q 7 >R 11 R> ; Q" (list 11 7)) (forth-p3-check-stack ">R R@ R>" ": Q 7 >R R@ R> ; Q" (list 7 7)) (forth-p3-check-stack "2>R 2R>" ": Q 1 2 2>R 99 2R> ; Q" (list 99 1 2)) (forth-p3-check-stack "2>R 2R@ 2R>" ": Q 3 4 2>R 2R@ 2R> ; Q" (list 3 4 3 4)))) (define forth-p3-run-all (fn () (set! forth-p3-passed 0) (set! forth-p3-failed 0) (set! forth-p3-failures (list)) (forth-p3-if-tests) (forth-p3-loop-tests) (forth-p3-do-tests) (dict "passed" forth-p3-passed "failed" forth-p3-failed "failures" forth-p3-failures)))