Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
105 lines
3.6 KiB
Plaintext
105 lines
3.6 KiB
Plaintext
;; ifTrue: / ifFalse: / ifTrue:ifFalse: / ifFalse:ifTrue: tests.
|
|
;;
|
|
;; In Smalltalk these are *block sends* on Boolean. The runtime can
|
|
;; intrinsify the dispatch in the JIT (already provided by the bytecode
|
|
;; expansion infrastructure) but the spec semantics are: True/False
|
|
;; receive these messages and pick which branch block to evaluate.
|
|
|
|
(set! st-test-pass 0)
|
|
(set! st-test-fail 0)
|
|
(set! st-test-fails (list))
|
|
|
|
(st-bootstrap-classes!)
|
|
(define ev (fn (src) (smalltalk-eval src)))
|
|
(define evp (fn (src) (smalltalk-eval-program src)))
|
|
|
|
;; ── 1. ifTrue: ──
|
|
(st-test "true ifTrue: → block value" (ev "true ifTrue: [42]") 42)
|
|
(st-test "false ifTrue: → nil" (ev "false ifTrue: [42]") nil)
|
|
|
|
;; ── 2. ifFalse: ──
|
|
(st-test "true ifFalse: → nil" (ev "true ifFalse: [42]") nil)
|
|
(st-test "false ifFalse: → block value" (ev "false ifFalse: [42]") 42)
|
|
|
|
;; ── 3. ifTrue:ifFalse: ──
|
|
(st-test "true ifTrue:ifFalse:" (ev "true ifTrue: [1] ifFalse: [2]") 1)
|
|
(st-test "false ifTrue:ifFalse:" (ev "false ifTrue: [1] ifFalse: [2]") 2)
|
|
|
|
;; ── 4. ifFalse:ifTrue: (reversed-order keyword) ──
|
|
(st-test "true ifFalse:ifTrue:" (ev "true ifFalse: [1] ifTrue: [2]") 2)
|
|
(st-test "false ifFalse:ifTrue:" (ev "false ifFalse: [1] ifTrue: [2]") 1)
|
|
|
|
;; ── 5. The non-taken branch is NOT evaluated (laziness) ──
|
|
(st-test
|
|
"ifTrue: doesn't evaluate the false branch"
|
|
(evp
|
|
"| ran |
|
|
ran := false.
|
|
true ifTrue: [99] ifFalse: [ran := true. 0].
|
|
^ ran")
|
|
false)
|
|
(st-test
|
|
"ifFalse: doesn't evaluate the true branch"
|
|
(evp
|
|
"| ran |
|
|
ran := false.
|
|
false ifTrue: [ran := true. 99] ifFalse: [0].
|
|
^ ran")
|
|
false)
|
|
|
|
;; ── 6. Branch result type can be anything ──
|
|
(st-test "branch returns string" (ev "true ifTrue: ['yes'] ifFalse: ['no']") "yes")
|
|
(st-test "branch returns nil" (ev "true ifTrue: [nil] ifFalse: [99]") nil)
|
|
(st-test "branch returns array" (ev "false ifTrue: [#(1)] ifFalse: [#(2 3)]") (list 2 3))
|
|
|
|
;; ── 7. Nested if ──
|
|
(st-test
|
|
"nested ifTrue:ifFalse:"
|
|
(evp
|
|
"| x |
|
|
x := 5.
|
|
^ x > 0
|
|
ifTrue: [x > 10
|
|
ifTrue: [#big]
|
|
ifFalse: [#smallPositive]]
|
|
ifFalse: [#nonPositive]")
|
|
(make-symbol "smallPositive"))
|
|
|
|
;; ── 8. Branch reads outer locals (closure semantics) ──
|
|
(st-test
|
|
"branch closes over outer bindings"
|
|
(evp
|
|
"| label x |
|
|
x := 7.
|
|
label := x > 0
|
|
ifTrue: [#positive]
|
|
ifFalse: [#nonPositive].
|
|
^ label")
|
|
(make-symbol "positive"))
|
|
|
|
;; ── 9. and: / or: short-circuit ──
|
|
(st-test "and: short-circuits when receiver false"
|
|
(ev "false and: [1/0]") false)
|
|
(st-test "and: with true receiver runs second" (ev "true and: [42]") 42)
|
|
(st-test "or: short-circuits when receiver true"
|
|
(ev "true or: [1/0]") true)
|
|
(st-test "or: with false receiver runs second" (ev "false or: [99]") 99)
|
|
|
|
;; ── 10. & and | are eager (not blocks) ──
|
|
(st-test "& on booleans" (ev "true & true") true)
|
|
(st-test "| on booleans" (ev "false | true") true)
|
|
|
|
;; ── 11. Boolean negation ──
|
|
(st-test "not on true" (ev "true not") false)
|
|
(st-test "not on false" (ev "false not") true)
|
|
|
|
;; ── 12. Real-world idiom: max via ifTrue:ifFalse: in a method ──
|
|
(st-class-define! "Mathy" "Object" (list))
|
|
(st-class-add-method! "Mathy" "myMax:and:"
|
|
(st-parse-method "myMax: a and: b ^ a > b ifTrue: [a] ifFalse: [b]"))
|
|
|
|
(st-test "method using ifTrue:ifFalse: returns max" (evp "^ Mathy new myMax: 3 and: 7") 7)
|
|
(st-test "method using ifTrue:ifFalse: returns max sym" (evp "^ Mathy new myMax: 9 and: 4") 9)
|
|
|
|
(list st-test-pass st-test-fail)
|