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
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:
@@ -20,7 +20,7 @@
|
|||||||
(frame name val)
|
(frame name val)
|
||||||
(assoc frame :locals (assoc (get frame :locals) name val))))
|
(assoc frame :locals (assoc (get frame :locals) name val))))
|
||||||
|
|
||||||
(define make-tcl-interp (fn () {:result "" :output "" :code 0 :frame (make-frame 0 nil) :commands {}}))
|
(define make-tcl-interp (fn () {:result "" :output "" :code 0 :frame (make-frame 0 nil) :frame-stack (list) :procs {} :commands {}}))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
tcl-register
|
tcl-register
|
||||||
@@ -28,6 +28,50 @@
|
|||||||
(interp name f)
|
(interp name f)
|
||||||
(assoc interp :commands (assoc (get interp :commands) name f))))
|
(assoc interp :commands (assoc (get interp :commands) name f))))
|
||||||
|
|
||||||
|
; --- upvar alias helpers ---
|
||||||
|
|
||||||
|
(define upvar-alias? (fn (v) (and (dict? v) (not (nil? (get v :upvar-level))))))
|
||||||
|
|
||||||
|
; take first n elements of a list
|
||||||
|
(define
|
||||||
|
take-n
|
||||||
|
(fn
|
||||||
|
(lst n)
|
||||||
|
(if
|
||||||
|
(or (<= n 0) (= 0 (len lst)))
|
||||||
|
(list)
|
||||||
|
(append (list (first lst)) (take-n (rest lst) (- n 1))))))
|
||||||
|
|
||||||
|
; replace element at index i in list with val (0-based)
|
||||||
|
(define
|
||||||
|
replace-at
|
||||||
|
(fn
|
||||||
|
(lst i val)
|
||||||
|
(let
|
||||||
|
((go
|
||||||
|
(fn
|
||||||
|
(remaining j acc)
|
||||||
|
(if
|
||||||
|
(= 0 (len remaining))
|
||||||
|
acc
|
||||||
|
(go
|
||||||
|
(rest remaining)
|
||||||
|
(+ j 1)
|
||||||
|
(append acc (list (if (= j i) val (first remaining)))))))))
|
||||||
|
(go lst 0 (list)))))
|
||||||
|
|
||||||
|
; build full-stack = frame-stack + [current-frame]
|
||||||
|
(define
|
||||||
|
tcl-full-stack
|
||||||
|
(fn (interp)
|
||||||
|
(append (get interp :frame-stack) (list (get interp :frame)))))
|
||||||
|
|
||||||
|
; get target frame at absolute level from full-stack
|
||||||
|
(define
|
||||||
|
tcl-frame-nth
|
||||||
|
(fn (full-stack level)
|
||||||
|
(nth full-stack level)))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
tcl-var-get
|
tcl-var-get
|
||||||
(fn
|
(fn
|
||||||
@@ -37,13 +81,50 @@
|
|||||||
(if
|
(if
|
||||||
(nil? val)
|
(nil? val)
|
||||||
(error (str "can't read \"" name "\": no such variable"))
|
(error (str "can't read \"" name "\": no such variable"))
|
||||||
val))))
|
(if
|
||||||
|
(upvar-alias? val)
|
||||||
|
; follow alias to target frame
|
||||||
|
(let
|
||||||
|
((target-level (get val :upvar-level))
|
||||||
|
(target-name (get val :upvar-name)))
|
||||||
|
(let
|
||||||
|
((full-stack (tcl-full-stack interp)))
|
||||||
|
(let
|
||||||
|
((target-frame (tcl-frame-nth full-stack target-level)))
|
||||||
|
(let
|
||||||
|
((target-val (frame-lookup target-frame target-name)))
|
||||||
|
(if
|
||||||
|
(nil? target-val)
|
||||||
|
(error (str "can't read \"" name "\": no such variable"))
|
||||||
|
target-val)))))
|
||||||
|
val)))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
tcl-var-set
|
tcl-var-set
|
||||||
(fn
|
(fn
|
||||||
(interp name val)
|
(interp name val)
|
||||||
(assoc interp :frame (frame-set-top (get interp :frame) name val))))
|
(let
|
||||||
|
((cur-val (get (get (get interp :frame) :locals) name)))
|
||||||
|
(if
|
||||||
|
(and (not (nil? cur-val)) (upvar-alias? cur-val))
|
||||||
|
; set in target frame
|
||||||
|
(let
|
||||||
|
((target-level (get cur-val :upvar-level))
|
||||||
|
(target-name (get cur-val :upvar-name)))
|
||||||
|
(let
|
||||||
|
((full-stack (tcl-full-stack interp)))
|
||||||
|
(let
|
||||||
|
((target-frame (tcl-frame-nth full-stack target-level)))
|
||||||
|
(let
|
||||||
|
((updated-target (frame-set-top target-frame target-name val)))
|
||||||
|
(let
|
||||||
|
((new-full-stack (replace-at full-stack target-level updated-target)))
|
||||||
|
(let
|
||||||
|
((new-frame-stack (take-n new-full-stack (- (len new-full-stack) 1)))
|
||||||
|
(new-current (nth new-full-stack (- (len new-full-stack) 1))))
|
||||||
|
(assoc interp :frame new-current :frame-stack new-frame-stack)))))))
|
||||||
|
; normal set in current frame top
|
||||||
|
(assoc interp :frame (frame-set-top (get interp :frame) name val))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
tcl-eval-parts
|
tcl-eval-parts
|
||||||
@@ -143,6 +224,77 @@
|
|||||||
{:values (quote ()) :interp interp}
|
{:values (quote ()) :interp interp}
|
||||||
words)))
|
words)))
|
||||||
|
|
||||||
|
; --- proc call ---
|
||||||
|
|
||||||
|
; Bind proc parameters: returns updated frame
|
||||||
|
(define
|
||||||
|
tcl-bind-params
|
||||||
|
(fn
|
||||||
|
(frame params call-args)
|
||||||
|
(if
|
||||||
|
(= 0 (len params))
|
||||||
|
frame
|
||||||
|
(let
|
||||||
|
((pname (first params)) (rest-ps (rest params)))
|
||||||
|
(if
|
||||||
|
(equal? pname "args")
|
||||||
|
; rest param: collect remaining call-args as list string
|
||||||
|
(frame-set-top frame "args" (tcl-list-build call-args))
|
||||||
|
(if
|
||||||
|
(= 0 (len call-args))
|
||||||
|
(error (str "wrong # args: no value for parameter \"" pname "\""))
|
||||||
|
(tcl-bind-params
|
||||||
|
(frame-set-top frame pname (first call-args))
|
||||||
|
rest-ps
|
||||||
|
(rest call-args))))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
tcl-call-proc
|
||||||
|
(fn
|
||||||
|
(interp proc-name proc-def call-args)
|
||||||
|
(let
|
||||||
|
((param-spec (get proc-def :args))
|
||||||
|
(body (get proc-def :body)))
|
||||||
|
(let
|
||||||
|
((params (if (equal? param-spec "") (list) (tcl-list-split param-spec))))
|
||||||
|
(let
|
||||||
|
((caller-stack-len (len (get interp :frame-stack)))
|
||||||
|
(new-frame (make-frame (+ (len (get interp :frame-stack)) 1) nil)))
|
||||||
|
(let
|
||||||
|
((bound-frame (tcl-bind-params new-frame params call-args)))
|
||||||
|
(let
|
||||||
|
((proc-interp
|
||||||
|
(assoc interp
|
||||||
|
:frame bound-frame
|
||||||
|
:frame-stack (append (get interp :frame-stack) (list (get interp :frame)))
|
||||||
|
:output ""
|
||||||
|
:result ""
|
||||||
|
:code 0))
|
||||||
|
(caller-output (get interp :output)))
|
||||||
|
(let
|
||||||
|
((result-interp (tcl-eval-string proc-interp body)))
|
||||||
|
(let
|
||||||
|
((code (get result-interp :code))
|
||||||
|
(result-val (get result-interp :result))
|
||||||
|
(proc-output (get result-interp :output)))
|
||||||
|
(let
|
||||||
|
; result-stack = [updated-frame-0..updated-caller-frame]
|
||||||
|
; recover updated caller frame and below-caller frames
|
||||||
|
((result-stack (get result-interp :frame-stack)))
|
||||||
|
(let
|
||||||
|
((updated-below (take-n result-stack caller-stack-len))
|
||||||
|
(updated-caller
|
||||||
|
(if
|
||||||
|
(> (len result-stack) caller-stack-len)
|
||||||
|
(nth result-stack caller-stack-len)
|
||||||
|
(get interp :frame))))
|
||||||
|
(assoc interp
|
||||||
|
:frame updated-caller
|
||||||
|
:frame-stack updated-below
|
||||||
|
:result result-val
|
||||||
|
:output (str caller-output proc-output)
|
||||||
|
:code (if (= code 2) 0 code)))))))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
tcl-eval-cmd
|
tcl-eval-cmd
|
||||||
(fn
|
(fn
|
||||||
@@ -160,7 +312,12 @@
|
|||||||
((cmd-fn (get (get cur-interp :commands) cmd-name)))
|
((cmd-fn (get (get cur-interp :commands) cmd-name)))
|
||||||
(if
|
(if
|
||||||
(nil? cmd-fn)
|
(nil? cmd-fn)
|
||||||
(error (str "unknown command: \"" cmd-name "\""))
|
(let
|
||||||
|
((proc-def (get (get cur-interp :procs) cmd-name)))
|
||||||
|
(if
|
||||||
|
(nil? proc-def)
|
||||||
|
(error (str "unknown command: \"" cmd-name "\""))
|
||||||
|
(tcl-call-proc cur-interp cmd-name proc-def cmd-args)))
|
||||||
(cmd-fn cur-interp cmd-args)))))))))
|
(cmd-fn cur-interp cmd-args)))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -1875,6 +2032,229 @@
|
|||||||
(assoc (tcl-var-set interp varname new-dict) :result new-dict)))))))
|
(assoc (tcl-var-set interp varname new-dict) :result new-dict)))))))
|
||||||
(else (error (str "dict: unknown subcommand \"" sub "\""))))))))
|
(else (error (str "dict: unknown subcommand \"" sub "\""))))))))
|
||||||
|
|
||||||
|
; --- proc command ---
|
||||||
|
|
||||||
|
(define
|
||||||
|
tcl-cmd-proc
|
||||||
|
(fn
|
||||||
|
(interp args)
|
||||||
|
(let
|
||||||
|
((name (first args))
|
||||||
|
(arg-spec (nth args 1))
|
||||||
|
(body (nth args 2)))
|
||||||
|
(assoc interp
|
||||||
|
:procs (assoc (get interp :procs) name {:args arg-spec :body body})
|
||||||
|
:result ""))))
|
||||||
|
|
||||||
|
; --- parse uplevel/upvar level argument ---
|
||||||
|
; Returns absolute level number.
|
||||||
|
; current-level = len(frame-stack)
|
||||||
|
(define
|
||||||
|
tcl-parse-level
|
||||||
|
(fn
|
||||||
|
(level-str current-level)
|
||||||
|
(if
|
||||||
|
(equal? (substring level-str 0 1) "#")
|
||||||
|
; absolute: #N
|
||||||
|
(parse-int (substring level-str 1 (string-length level-str)))
|
||||||
|
; relative: N levels up from current
|
||||||
|
(- current-level (parse-int level-str)))))
|
||||||
|
|
||||||
|
; --- uplevel command ---
|
||||||
|
|
||||||
|
(define
|
||||||
|
tcl-cmd-uplevel
|
||||||
|
(fn
|
||||||
|
(interp args)
|
||||||
|
(let
|
||||||
|
((current-level (len (get interp :frame-stack))))
|
||||||
|
(let
|
||||||
|
; check if first arg is a level specifier
|
||||||
|
((has-level
|
||||||
|
(and
|
||||||
|
(> (len args) 1)
|
||||||
|
(or
|
||||||
|
(equal? (substring (first args) 0 1) "#")
|
||||||
|
(let
|
||||||
|
((fst (first args)))
|
||||||
|
(and
|
||||||
|
(> (string-length fst) 0)
|
||||||
|
(tcl-expr-digit? (substring fst 0 1)))))))
|
||||||
|
(level-str (if (and (> (len args) 1) (or (equal? (substring (first args) 0 1) "#") (and (> (string-length (first args)) 0) (tcl-expr-digit? (substring (first args) 0 1))))) (first args) "1"))
|
||||||
|
(script (if (and (> (len args) 1) (or (equal? (substring (first args) 0 1) "#") (and (> (string-length (first args)) 0) (tcl-expr-digit? (substring (first args) 0 1))))) (nth args 1) (first args))))
|
||||||
|
(let
|
||||||
|
((target-level (tcl-parse-level level-str current-level)))
|
||||||
|
(let
|
||||||
|
((full-stack (tcl-full-stack interp)))
|
||||||
|
(let
|
||||||
|
((target-frame (tcl-frame-nth full-stack target-level)))
|
||||||
|
(let
|
||||||
|
((temp-interp
|
||||||
|
(assoc interp
|
||||||
|
:frame target-frame
|
||||||
|
:frame-stack (take-n (get interp :frame-stack) target-level)
|
||||||
|
:output ""))
|
||||||
|
(saved-output (get interp :output)))
|
||||||
|
(let
|
||||||
|
((result-interp (tcl-eval-string temp-interp script)))
|
||||||
|
(let
|
||||||
|
((updated-target (get result-interp :frame))
|
||||||
|
(new-output (get result-interp :output)))
|
||||||
|
(let
|
||||||
|
((new-full-stack (replace-at full-stack target-level updated-target)))
|
||||||
|
(let
|
||||||
|
((new-frame-stack (take-n new-full-stack (- (len new-full-stack) 1)))
|
||||||
|
(new-current (nth new-full-stack (- (len new-full-stack) 1))))
|
||||||
|
(assoc interp
|
||||||
|
:frame new-current
|
||||||
|
:frame-stack new-frame-stack
|
||||||
|
:result (get result-interp :result)
|
||||||
|
:output (str saved-output new-output)
|
||||||
|
:code (get result-interp :code))))))))))))))
|
||||||
|
|
||||||
|
; --- upvar command ---
|
||||||
|
|
||||||
|
(define
|
||||||
|
tcl-cmd-upvar
|
||||||
|
(fn
|
||||||
|
(interp args)
|
||||||
|
(let
|
||||||
|
((current-level (len (get interp :frame-stack))))
|
||||||
|
(let
|
||||||
|
; check if first arg is a level specifier
|
||||||
|
((has-level
|
||||||
|
(and
|
||||||
|
(> (len args) 2)
|
||||||
|
(or
|
||||||
|
(equal? (substring (first args) 0 1) "#")
|
||||||
|
(tcl-expr-digit? (substring (first args) 0 1)))))
|
||||||
|
(level-str (if (and (> (len args) 2) (or (equal? (substring (first args) 0 1) "#") (tcl-expr-digit? (substring (first args) 0 1)))) (first args) "1"))
|
||||||
|
(pair-args (if (and (> (len args) 2) (or (equal? (substring (first args) 0 1) "#") (tcl-expr-digit? (substring (first args) 0 1)))) (rest args) args)))
|
||||||
|
(let
|
||||||
|
((target-level (tcl-parse-level level-str current-level)))
|
||||||
|
(let
|
||||||
|
((bind-pairs
|
||||||
|
(fn
|
||||||
|
(i-interp remaining)
|
||||||
|
(if
|
||||||
|
(< (len remaining) 2)
|
||||||
|
i-interp
|
||||||
|
(let
|
||||||
|
((remote-name (first remaining))
|
||||||
|
(local-name (nth remaining 1)))
|
||||||
|
(let
|
||||||
|
((alias {:upvar-level target-level :upvar-name remote-name}))
|
||||||
|
(bind-pairs
|
||||||
|
(assoc i-interp :frame (frame-set-top (get i-interp :frame) local-name alias))
|
||||||
|
(rest (rest remaining)))))))))
|
||||||
|
(assoc (bind-pairs interp pair-args) :result "")))))))
|
||||||
|
|
||||||
|
; --- global command ---
|
||||||
|
|
||||||
|
(define
|
||||||
|
tcl-cmd-global
|
||||||
|
(fn
|
||||||
|
(interp args)
|
||||||
|
(reduce
|
||||||
|
(fn
|
||||||
|
(i name)
|
||||||
|
(tcl-cmd-upvar i (list "#0" name name)))
|
||||||
|
interp
|
||||||
|
args)))
|
||||||
|
|
||||||
|
; --- variable command ---
|
||||||
|
|
||||||
|
(define
|
||||||
|
tcl-cmd-variable
|
||||||
|
(fn
|
||||||
|
(interp args)
|
||||||
|
(let
|
||||||
|
((go
|
||||||
|
(fn
|
||||||
|
(i remaining)
|
||||||
|
(if
|
||||||
|
(= 0 (len remaining))
|
||||||
|
i
|
||||||
|
(let
|
||||||
|
((name (first remaining))
|
||||||
|
(rest-rem (rest remaining)))
|
||||||
|
(let
|
||||||
|
((linked (tcl-cmd-upvar i (list "#0" name name))))
|
||||||
|
(if
|
||||||
|
(and (> (len rest-rem) 0) (not (equal? (substring (first rest-rem) 0 1) "-")))
|
||||||
|
(let
|
||||||
|
((val (first rest-rem)))
|
||||||
|
(go (assoc (tcl-var-set linked name val) :result "") (rest rest-rem)))
|
||||||
|
(go linked rest-rem))))))))
|
||||||
|
(go interp args))))
|
||||||
|
|
||||||
|
; --- info command ---
|
||||||
|
|
||||||
|
(define
|
||||||
|
tcl-cmd-info
|
||||||
|
(fn
|
||||||
|
(interp args)
|
||||||
|
(if
|
||||||
|
(= 0 (len args))
|
||||||
|
(error "info: wrong # args")
|
||||||
|
(let
|
||||||
|
((sub (first args)) (rest-args (rest args)))
|
||||||
|
(cond
|
||||||
|
; info level
|
||||||
|
((equal? sub "level")
|
||||||
|
(assoc interp :result (str (len (get interp :frame-stack)))))
|
||||||
|
; info vars / info locals
|
||||||
|
((or (equal? sub "vars") (equal? sub "locals"))
|
||||||
|
(let
|
||||||
|
((frame-locals (get (get interp :frame) :locals)))
|
||||||
|
(assoc interp :result
|
||||||
|
(tcl-list-build
|
||||||
|
(filter
|
||||||
|
(fn (k) (not (upvar-alias? (get frame-locals k))))
|
||||||
|
(keys frame-locals))))))
|
||||||
|
; info globals
|
||||||
|
((equal? sub "globals")
|
||||||
|
(let
|
||||||
|
((global-frame
|
||||||
|
(if
|
||||||
|
(= 0 (len (get interp :frame-stack)))
|
||||||
|
(get interp :frame)
|
||||||
|
(first (get interp :frame-stack)))))
|
||||||
|
(let
|
||||||
|
((global-locals (get global-frame :locals)))
|
||||||
|
(assoc interp :result
|
||||||
|
(tcl-list-build
|
||||||
|
(filter
|
||||||
|
(fn (k) (not (upvar-alias? (get global-locals k))))
|
||||||
|
(keys global-locals)))))))
|
||||||
|
; info commands
|
||||||
|
((equal? sub "commands")
|
||||||
|
(assoc interp :result (tcl-list-build (keys (get interp :commands)))))
|
||||||
|
; info procs
|
||||||
|
((equal? sub "procs")
|
||||||
|
(assoc interp :result (tcl-list-build (keys (get interp :procs)))))
|
||||||
|
; info args procname
|
||||||
|
((equal? sub "args")
|
||||||
|
(let
|
||||||
|
((pname (first rest-args)))
|
||||||
|
(let
|
||||||
|
((proc-def (get (get interp :procs) pname)))
|
||||||
|
(if
|
||||||
|
(nil? proc-def)
|
||||||
|
(error (str "info args: \"" pname "\" isn't a procedure"))
|
||||||
|
(assoc interp :result (get proc-def :args))))))
|
||||||
|
; info body procname
|
||||||
|
((equal? sub "body")
|
||||||
|
(let
|
||||||
|
((pname (first rest-args)))
|
||||||
|
(let
|
||||||
|
((proc-def (get (get interp :procs) pname)))
|
||||||
|
(if
|
||||||
|
(nil? proc-def)
|
||||||
|
(error (str "info body: \"" pname "\" isn't a procedure"))
|
||||||
|
(assoc interp :result (get proc-def :body))))))
|
||||||
|
(else (error (str "info: unknown subcommand \"" sub "\""))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
make-default-tcl-interp
|
make-default-tcl-interp
|
||||||
(fn
|
(fn
|
||||||
@@ -1949,4 +2329,16 @@
|
|||||||
((i (tcl-register i "split" tcl-cmd-split)))
|
((i (tcl-register i "split" tcl-cmd-split)))
|
||||||
(let
|
(let
|
||||||
((i (tcl-register i "join" tcl-cmd-join)))
|
((i (tcl-register i "join" tcl-cmd-join)))
|
||||||
(tcl-register i "dict" tcl-cmd-dict))))))))))))))))))))))))))))))))))))))
|
(let
|
||||||
|
((i (tcl-register i "dict" tcl-cmd-dict)))
|
||||||
|
(let
|
||||||
|
((i (tcl-register i "proc" tcl-cmd-proc)))
|
||||||
|
(let
|
||||||
|
((i (tcl-register i "uplevel" tcl-cmd-uplevel)))
|
||||||
|
(let
|
||||||
|
((i (tcl-register i "upvar" tcl-cmd-upvar)))
|
||||||
|
(let
|
||||||
|
((i (tcl-register i "global" tcl-cmd-global)))
|
||||||
|
(let
|
||||||
|
((i (tcl-register i "variable" tcl-cmd-variable)))
|
||||||
|
(tcl-register i "info" tcl-cmd-info))))))))))))))))))))))))))))))))))))))))))))
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ cat > "$TMPFILE" << EPOCHS
|
|||||||
(eval "tcl-test-summary")
|
(eval "tcl-test-summary")
|
||||||
EPOCHS
|
EPOCHS
|
||||||
|
|
||||||
OUTPUT=$(timeout 30 "$SX_SERVER" < "$TMPFILE" 2>&1)
|
OUTPUT=$(timeout 90 "$SX_SERVER" < "$TMPFILE" 2>&1)
|
||||||
[ "$VERBOSE" = "-v" ] && echo "$OUTPUT"
|
[ "$VERBOSE" = "-v" ] && echo "$OUTPUT"
|
||||||
|
|
||||||
# Extract summary line from epoch 7 output
|
# Extract summary line from epoch 7 output
|
||||||
|
|||||||
@@ -286,6 +286,30 @@
|
|||||||
(ok "dict-incr-missing" (get (run "set d {}\ndict incr d n") :result) "n 1")
|
(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" (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")
|
(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
|
(dict
|
||||||
"passed"
|
"passed"
|
||||||
tcl-eval-pass
|
tcl-eval-pass
|
||||||
|
|||||||
Reference in New Issue
Block a user