Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
- Coroutines (generator-style): coroutine/yield/yieldto commands; eager yield collection during body execution, pop-on-call dispatch via registered command closures; coro-yields + coroutines threaded through tcl-call-proc - info exists varname (plus hostname/script/tclversion stubs) - clock seconds/milliseconds/format/scan stubs - File I/O stubs: open/close/read/eof/seek/tell/flush + file subcommands - format command: full %-specifier parsing with flags, width, zero-pad, left-align - Fixed dict set/unset/incr/append/update to use tcl-var-get (upvar alias aware) - Fixed lappend and append to use tcl-var-get for reading (upvar alias aware) - 20 coroutine tests (coro.sx) + 20 idiom corpus tests (idioms.sx) - event-loop.tcl program: cooperative scheduler demo using coroutines - Note: coroutines eagerly collect yields (generator-style, not true suspension) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
137 lines
4.0 KiB
Plaintext
137 lines
4.0 KiB
Plaintext
; Tcl-on-SX coroutine tests (Phase 6)
|
|
(define tcl-coro-pass 0)
|
|
(define tcl-coro-fail 0)
|
|
(define tcl-coro-failures (list))
|
|
|
|
(define
|
|
tcl-coro-assert
|
|
(fn
|
|
(label expected actual)
|
|
(if
|
|
(equal? expected actual)
|
|
(set! tcl-coro-pass (+ tcl-coro-pass 1))
|
|
(begin
|
|
(set! tcl-coro-fail (+ tcl-coro-fail 1))
|
|
(append!
|
|
tcl-coro-failures
|
|
(str label ": expected=" (str expected) " got=" (str actual)))))))
|
|
|
|
(define
|
|
tcl-run-coro-tests
|
|
(fn
|
|
()
|
|
(set! tcl-coro-pass 0)
|
|
(set! tcl-coro-fail 0)
|
|
(set! tcl-coro-failures (list))
|
|
(define interp (fn () (make-default-tcl-interp)))
|
|
(define run (fn (src) (tcl-eval-string (interp) src)))
|
|
(define
|
|
ok
|
|
(fn (label actual expected) (tcl-coro-assert label expected actual)))
|
|
|
|
; --- basic coroutine: yields one value ---
|
|
(ok "coro-single-yield"
|
|
(get (run "proc gen {} { yield hello }\ncoroutine g gen\ng") :result)
|
|
"hello")
|
|
|
|
; --- coroutine yields multiple values in order ---
|
|
(ok "coro-multi-yield-1"
|
|
(get (run "proc cnt {} { yield a; yield b; yield c }\ncoroutine c1 cnt\nc1") :result)
|
|
"a")
|
|
|
|
(ok "coro-multi-yield-2"
|
|
(get (run "proc cnt {} { yield a; yield b; yield c }\ncoroutine c1 cnt\nc1\nc1") :result)
|
|
"b")
|
|
|
|
(ok "coro-multi-yield-3"
|
|
(get (run "proc cnt {} { yield a; yield b; yield c }\ncoroutine c1 cnt\nc1\nc1\nc1") :result)
|
|
"c")
|
|
|
|
; --- coroutine with arguments to proc ---
|
|
(ok "coro-args"
|
|
(get (run "proc gen2 {n} { yield $n; yield [expr {$n + 1}] }\ncoroutine g2 gen2 10\ng2") :result)
|
|
"10")
|
|
|
|
(ok "coro-args-2"
|
|
(get (run "proc gen2 {n} { yield $n; yield [expr {$n + 1}] }\ncoroutine g2 gen2 10\ng2\ng2") :result)
|
|
"11")
|
|
|
|
; --- coroutine exhausted returns empty string ---
|
|
(ok "coro-exhausted"
|
|
(get (run "proc g3 {} { yield only }\ncoroutine c3 g3\nc3\nc3") :result)
|
|
"")
|
|
|
|
; --- yield in while loop ---
|
|
(ok "coro-while-loop-1"
|
|
(get (run "proc counter {max} { set i 0; while {$i < $max} { yield $i; incr i } }\ncoroutine cw counter 3\ncw") :result)
|
|
"0")
|
|
|
|
(ok "coro-while-loop-2"
|
|
(get (run "proc counter {max} { set i 0; while {$i < $max} { yield $i; incr i } }\ncoroutine cw counter 3\ncw\ncw") :result)
|
|
"1")
|
|
|
|
(ok "coro-while-loop-3"
|
|
(get (run "proc counter {max} { set i 0; while {$i < $max} { yield $i; incr i } }\ncoroutine cw counter 3\ncw\ncw\ncw") :result)
|
|
"2")
|
|
|
|
; --- collect all yields from coroutine ---
|
|
(ok "coro-collect-all"
|
|
(get
|
|
(run
|
|
"proc counter {n max} { while {$n < $max} { yield $n; incr n }; yield done }\ncoroutine gen1 counter 0 3\nset out {}\nfor {set i 0} {$i < 4} {incr i} { lappend out [gen1] }\nlindex $out 3")
|
|
:result)
|
|
"done")
|
|
|
|
; --- two independent coroutines ---
|
|
(ok "coro-two-independent"
|
|
(get
|
|
(run
|
|
"proc seq {start} { yield $start; yield [expr {$start+1}] }\ncoroutine ca seq 0\ncoroutine cb seq 10\nset r [ca]\nappend r \":\" [cb]")
|
|
:result)
|
|
"0:10")
|
|
|
|
; --- yield with no value returns empty string ---
|
|
(ok "coro-yield-no-val"
|
|
(get (run "proc g {} { yield }\ncoroutine cg g\ncg") :result)
|
|
"")
|
|
|
|
; --- clock seconds stub ---
|
|
(ok "clock-seconds"
|
|
(get (run "clock seconds") :result)
|
|
"0")
|
|
|
|
; --- clock milliseconds stub ---
|
|
(ok "clock-milliseconds"
|
|
(get (run "clock milliseconds") :result)
|
|
"0")
|
|
|
|
; --- clock format stub ---
|
|
(ok "clock-format"
|
|
(get (run "clock format 0") :result)
|
|
"Thu Jan 1 00:00:00 UTC 1970")
|
|
|
|
; --- file stubs ---
|
|
(ok "file-exists-stub"
|
|
(get (run "file exists /no/such/file") :result)
|
|
"0")
|
|
|
|
(ok "file-join"
|
|
(get (run "file join foo bar baz") :result)
|
|
"foo/bar/baz")
|
|
|
|
(ok "open-returns-channel"
|
|
(get (run "open /dev/null r") :result)
|
|
"file0")
|
|
|
|
(ok "eof-returns-1"
|
|
(get (run "set ch [open /dev/null r]\neof $ch") :result)
|
|
"1")
|
|
|
|
(dict
|
|
"passed"
|
|
tcl-coro-pass
|
|
"failed"
|
|
tcl-coro-fail
|
|
"failures"
|
|
tcl-coro-failures)))
|