;; whileTrue: / whileTrue / whileFalse: / whileFalse tests.
;;
;; In Smalltalk these are *ordinary* messages sent to the condition block.
;; No special-form magic — just block sends. The runtime can intrinsify
;; them later in the JIT (Tier 1 of bytecode expansion) but the spec-level
;; semantics are what's pinned here.

(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. whileTrue: with body — basic counter ──
(st-test
  "whileTrue: counts down"
  (evp "| n | n := 5. [n > 0] whileTrue: [n := n - 1]. ^ n")
  0)

(st-test
  "whileTrue: returns nil"
  (evp "| n | n := 3. ^ [n > 0] whileTrue: [n := n - 1]")
  nil)

(st-test
  "whileTrue: zero iterations is fine"
  (evp "| n | n := 0. [n > 0] whileTrue: [n := n + 1]. ^ n")
  0)

;; ── 2. whileFalse: with body ──
(st-test
  "whileFalse: counts down (cond becomes true)"
  (evp "| n | n := 5. [n <= 0] whileFalse: [n := n - 1]. ^ n")
  0)

(st-test
  "whileFalse: returns nil"
  (evp "| n | n := 3. ^ [n <= 0] whileFalse: [n := n - 1]")
  nil)

;; ── 3. whileTrue (no arg) — body-less side-effect loop ──
(st-test
  "whileTrue without argument runs cond-only loop"
  (evp
    "| n decrement |
     n := 5.
     decrement := [n := n - 1. n > 0].
     decrement whileTrue.
     ^ n")
  0)

;; ── 4. whileFalse (no arg) ──
(st-test
  "whileFalse without argument"
  (evp
    "| n inc |
     n := 0.
     inc := [n := n + 1. n >= 3].
     inc whileFalse.
     ^ n")
  3)

;; ── 5. Cond block evaluated each iteration (not cached) ──
(st-test
  "whileTrue: re-evaluates cond on every iter"
  (evp
    "| n stop |
     n := 0. stop := false.
     [stop] whileFalse: [
       n := n + 1.
       n >= 4 ifTrue: [stop := true]].
     ^ n")
  4)

;; ── 6. Body block sees outer locals ──
(st-test
  "whileTrue: body reads + writes captured locals"
  (evp
    "| acc i |
     acc := 0. i := 1.
     [i <= 10] whileTrue: [acc := acc + i. i := i + 1].
     ^ acc")
  55)

;; ── 7. Nested while loops ──
(st-test
  "nested whileTrue: produces flat sum"
  (evp
    "| total i j |
     total := 0. i := 0.
     [i < 3] whileTrue: [
       j := 0.
       [j < 4] whileTrue: [total := total + 1. j := j + 1].
       i := i + 1].
     ^ total")
  12)

;; ── 8. ^ inside whileTrue: short-circuits the surrounding method ──
(st-class-define! "WhileEscape" "Object" (list))
(st-class-add-method! "WhileEscape" "firstOver:in:"
  (st-parse-method
    "firstOver: limit in: arr
       | i |
       i := 1.
       [i <= arr size] whileTrue: [
         (arr at: i) > limit ifTrue: [^ arr at: i].
         i := i + 1].
       ^ nil"))

(st-test
  "early ^ from whileTrue: body"
  (evp "^ WhileEscape new firstOver: 5 in: #(1 3 5 7 9)")
  7)

(st-test
  "whileTrue: completes when nothing matches"
  (evp "^ WhileEscape new firstOver: 100 in: #(1 2 3)")
  nil)

;; ── 9. whileTrue: invocations independent across calls ──
(st-class-define! "Counter2" "Object" (list "n"))
(st-class-add-method! "Counter2" "init"
  (st-parse-method "init n := 0. ^ self"))
(st-class-add-method! "Counter2" "n"
  (st-parse-method "n ^ n"))
(st-class-add-method! "Counter2" "tick:"
  (st-parse-method "tick: count [count > 0] whileTrue: [n := n + 1. count := count - 1]. ^ self"))

(st-test
  "instance state survives whileTrue: invocations"
  (evp
    "| c | c := Counter2 new init.
     c tick: 3. c tick: 4.
     ^ c n")
  7)

;; ── 10. Timing: whileTrue: on a never-true cond runs zero times ──
(st-test
  "whileTrue: with always-false cond"
  (evp "| ran | ran := false. [false] whileTrue: [ran := true]. ^ ran")
  false)

(list st-test-pass st-test-fail)
