tcl: proc + uplevel + upvar + global + variable + info (+19 tests, 225 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 48s

Phase 3 headline feature: everything falls out of SX's first-class env chain.
- make-tcl-interp extended with :frame-stack and :procs fields
- proc: user-defined commands with param binding, rest args, isolated scope
- uplevel: run script in ancestor frame with correct frame propagation
- upvar: alias local name to remote frame variable (get/set follow alias)
- global/variable: sugar for upvar #0
- info: level, vars, locals, globals, commands, procs, args, body
- tcl-call-proc propagates updated frames back to caller after proc returns
- test.sh timeout bumped to 90s for larger runtime

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 09:30:28 +00:00
parent a49b1a9f79
commit eb5babaf99
3 changed files with 422 additions and 6 deletions

View File

@@ -286,6 +286,30 @@
(ok "dict-incr-missing" (get (run "set d {}\ndict incr d n") :result) "n 1")
(ok "dict-append" (get (run "set d {x hello}\ndict append d x _hi") :result) "x hello_hi")
(ok "dict-append-new" (get (run "set d {}\ndict append d k val") :result) "k val")
; --- proc tests ---
(ok "proc-basic" (get (run "proc add {a b} {expr {$a + $b}}\nadd 3 4") :result) "7")
(ok "proc-return" (get (run "proc greet {name} {set msg \"hi $name\"\nreturn $msg}\ngreet World") :result) "hi World")
(ok "proc-factorial" (get (run "proc factorial {n} {if {$n <= 1} {return 1}\nexpr {$n * [factorial [expr {$n - 1}]]}}\nfactorial 5") :result) "120")
(ok "proc-args" (get (run "proc sum args {set t 0\nforeach x $args {incr t $x}\nreturn $t}\nsum 1 2 3 4") :result) "10")
(ok "proc-isolated" (get (run "set x outer\nproc p {} {set x inner\nreturn $x}\np") :result) "inner")
(ok "proc-caller-unchanged" (tcl-var-get (run "set x outer\nproc p {} {set x inner\nreturn $x}\np\nset dummy 1") "x") "outer")
(ok "proc-output" (get (run "proc hello {} {puts -nonewline hi}\nhello") :output) "hi")
; --- upvar tests ---
(ok "upvar-incr" (tcl-var-get (run "proc incr2 {varname} {upvar 1 $varname v\nincr v}\nset counter 10\nincr2 counter\nset counter") "counter") "11")
(ok "upvar-double" (tcl-var-get (run "proc double-it {varname} {upvar 1 $varname x\nset x [expr {$x * 2}]}\nset val 5\ndouble-it val\nset val") "val") "10")
(ok "upvar-result" (get (run "proc double-it {varname} {upvar 1 $varname x\nset x [expr {$x * 2}]}\nset val 5\ndouble-it val\nset val") :result) "10")
; --- uplevel tests ---
(ok "uplevel-set" (tcl-var-get (run "proc setvar {name val} {uplevel 1 \"set $name $val\"}\nsetvar x 99\nset x") "x") "99")
(ok "uplevel-get" (get (run "proc getvar {name} {uplevel 1 \"set $name\"}\nset y 77\ngetvar y") :result) "77")
; --- global tests ---
(ok "global-read" (get (run "set g 100\nproc getg {} {global g\nreturn $g}\ngetg") :result) "100")
(ok "global-write" (tcl-var-get (run "set g 0\nproc bumping {} {global g\nincr g}\nbumping\nbumping\nset g") "g") "2")
; --- info tests ---
(ok "info-level-0" (get (run "info level") :result) "0")
(ok "info-level-proc" (get (run "proc p {} {info level}\np") :result) "1")
(ok "info-procs" (let ((r (get (run "proc myfn {} {}\ninfo procs") :result))) (contains? (tcl-list-split r) "myfn")) true)
(ok "info-args" (get (run "proc add {a b} {expr {$a+$b}}\ninfo args add") :result) "a b")
(ok "info-commands-has-set" (let ((r (get (run "info commands") :result))) (contains? (tcl-list-split r) "set")) true)
(dict
"passed"
tcl-eval-pass