;; 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-run-all (fn () (set! forth-p3-passed 0) (set! forth-p3-failed 0) (set! forth-p3-failures (list)) (forth-p3-if-tests) (dict "passed" forth-p3-passed "failed" forth-p3-failed "failures" forth-p3-failures)))