;; 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)
