Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
170 lines
4.5 KiB
Plaintext
170 lines
4.5 KiB
Plaintext
;; 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-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)
|
|
(dict
|
|
"passed"
|
|
forth-p3-passed
|
|
"failed"
|
|
forth-p3-failed
|
|
"failures"
|
|
forth-p3-failures)))
|