;; BlockContext>>value family tests.
;;
;; The runtime already implements value, value:, value:value:, value:value:value:,
;; value:value:value:value:, and valueWithArguments: in st-block-dispatch.
;; This file pins each variant down with explicit tests + closure semantics.

(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. The value/valueN family ──
(st-test "value: zero-arg block"     (ev "[42] value")                              42)
(st-test "value: one-arg block"      (ev "[:a | a + 1] value: 10")                  11)
(st-test "value:value: two-arg"       (ev "[:a :b | a * b] value: 3 value: 4")       12)
(st-test "value:value:value: three"  (ev "[:a :b :c | a + b + c] value: 1 value: 2 value: 3") 6)
(st-test "value:value:value:value: four"
  (ev "[:a :b :c :d | a + b + c + d] value: 1 value: 2 value: 3 value: 4")          10)

;; ── 2. valueWithArguments: ──
(st-test "valueWithArguments: zero-arg"
  (ev "[99] valueWithArguments: #()")                                               99)
(st-test "valueWithArguments: one-arg"
  (ev "[:x | x * x] valueWithArguments: #(7)")                                      49)
(st-test "valueWithArguments: many"
  (ev "[:a :b :c | a , b , c] valueWithArguments: #('foo' '-' 'bar')")              "foo-bar")

;; ── 3. Block returns last expression ──
(st-test "block last-expression result" (ev "[1. 2. 3] value") 3)
(st-test "block with temps initial state"
  (ev "[| t u | t := 5. u := t * 2. u] value") 10)

;; ── 4. Closure over outer locals ──
(st-test
  "block reads outer let temps"
  (evp "| n | n := 5. ^ [n * n] value")
  25)
(st-test
  "block writes outer locals (mutating)"
  (evp "| n | n := 10. [:x | n := n + x] value: 5. ^ n")
  15)

;; ── 5. Block sees later mutation of captured local ──
(st-test
  "block re-reads outer local on each invocation"
  (evp
    "| n b r1 r2 |
     n := 1. b := [n].
     r1 := b value.
     n := 99.
     r2 := b value.
     ^ r1 + r2")
  100)

;; ── 6. Re-entrant invocations ──
(st-test
  "calling same block twice independent results"
  (evp
    "| sq |
     sq := [:x | x * x].
     ^ (sq value: 3) + (sq value: 4)")
  25)

;; ── 7. Nested blocks ──
(st-test
  "nested block closes over both scopes"
  (evp
    "| a |
     a := [:x | [:y | x + y]].
     ^ ((a value: 10) value: 5)")
  15)

;; ── 8. Block as method argument ──
(st-class-define! "BlockUser" "Object" (list))
(st-class-add-method! "BlockUser" "apply:to:"
  (st-parse-method "apply: aBlock to: x ^ aBlock value: x"))

(st-test
  "method invokes block argument"
  (evp "^ BlockUser new apply: [:n | n * n] to: 9")
  81)

;; ── 9. numArgs + class ──
(st-test "numArgs zero"    (ev "[] numArgs")        0)
(st-test "numArgs three"   (ev "[:a :b :c | a] numArgs") 3)
(st-test "block class is BlockClosure"
  (str (ev "[1] class name")) "BlockClosure")

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