Search code examples
tclstack-traceprocedure

pass square brace to uplevel TCL


I have a code in which I am passing a list to be evaluated, by TCl uplevel #0. While it works well if I give it a code which uses curly braces in order to wrap the square braces, for example:

uplevel #0 { puts [ info vars CCK_* ] }

I cannot get to accept when I use a list, i.e.:

uplevel #0 [list puts "\[" info vars CCK_* "\]" ]

I get:

wrong # args: should be "puts ?-nonewline? ?channelId? string"
    while executing
"puts {[} info var CCK_* \]"
    ("uplevel" body line 1)
    invoked from within
"uplevel #0 [ list puts "\[" info var CCK_* "\]" ]"

I need the list command , because some of the rest of the code requires evaluation of variable names, that must happen before uplevel takes order ( i.e., input to uplevel). For example:

if { [ getpoint $elem ] == $pointy }

when [ getpoint $elem ] is to be evaluated in the uplevel, but pointy actually is defined and set in the calling proc , hence I cannot use curly braces for it, there will be evaluation before uplevel is called, and it would get just a number.
Thanks,


Solution

  • Assembling a script (or command sequence) to be submitted to uplevel etc. is not necessarily best achieved using list. This is the case for a script with nested evaluations, for instance.

    Your question wording is not fully clear to me (so I might have interpreted it incorrectly), but you might want to consider using [subst] or [string map] for your purposes?

    Watch:

    set CCK_1 ""
    
    proc foo {someVarName} {
        uplevel "#0" [subst -nocommands {
        if {"$someVarName" in [info vars CCK_*]} {
            puts "Found $someVarName"
        }
       }]
    }
    
    foo CCK_1; # prints "Found CCK_1"
    foo CCK_2
    

    List are better suited for command sequences without excessive evaluation nesting; for complete scripts, better use script templates based on [subst] or [string map]. A word of caution: [subst] and [string map] don't protect the substitution values and position them in the script in their literal form.

    Update

    This is not to say that your original snippet could not be made to work:

    set CCK_1 ""
    # a) non-robust variant
    proc bar {pattern} {
        uplevel "#0" puts "\[info vars $pattern\]"
        # equiv of
        uplevel "#0" [concat puts "\[info vars $pattern\]"]
        # versus
        uplevel "#0" [list puts "\[info vars $pattern\]"]
    }
    
    bar CCK_*
    
    set "CCK _1" ""
    # b) robust variant
    proc bar-robust {pattern} {
        uplevel "#0" puts "\[[list info vars $pattern]\]"
        # equiv of
        uplevel "#0" [concat puts "\[[list info vars $pattern]\]"]
    }
    
    bar-robust "CCK _*"
    

    uplevel assembles the script to be evaluated by [concat]ing its arguments. Like providing a single [concat]'ed the argument. You would not use list here to assemble the entire script, but rather to protect the script components under assembly (see bar-robust). Protection here means that complex values are maintained in their original meaning during script assembly (e.g., a match pattern incl. whitespace: CCK _*).