3

Tcl manuals say that curly braces do not allow variable substitution. However this works only with some commands but not with others.

What is the difference and how to identify the cases where the substitution will occur and the cases where it won't occur?

% set x 3
3
% puts {$x}
$x
% expr {$x}
3
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
coder56
  • 43
  • 4
  • You can read https://ipenv.com/ins-and-outs-tcltk. This page give a detailed discussion on the meaning of braces in Tcl. – HanT Aug 20 '19 at 10:36
  • It is about double substitution. Check these links: https://wiki.tcl-lang.org/page/double+substitution https://wiki.tcl-lang.org/page/Brace+your+expr-essions – Yaroslav Kornachevskyi Aug 18 '19 at 16:26
  • Thank you. I understand that some commands like expr do additional substitutions on their own. The question is rather how to tell which command does it and which doesn't. I could not find a complete list of the commands that do the second round of the substitution. This incompleteness is quite confusing for beginning programmers and may expose code to injection attacks. – coder56 Aug 18 '19 at 17:16
  • I think at the very beginning https://wiki.tcl-lang.org/page/double+substitution would be enough ) – Yaroslav Kornachevskyi Aug 18 '19 at 17:35

2 Answers2

3

Referring to the list of standard commands: any command that takes a "body" or "script" argument will eventually evaluate that body as code. With no guarantees about exhaustiveness:

after, apply, catch, eval, expr, fileevent (and chan event), for, foreach, if, interp eval, lmap, some namespace subcommands, some oo::* commands, proc, subst, switch, try, uplevel, while

This is truly one of Tcl's greatest strengths. It gives you the power to easily write your own control structures. For example, Tcl does not provide a do-while loop, but you can do this:

proc do {body while condition} {
    if {$while ni {while until}} {
        error "some message about usage..."
    }
    while true {
        uplevel 1 $body
        set status [uplevel 1 [list expr $condition]]
        if {$while eq "while" && !$status} then break
        if {$while eq "until" &&  $status} then break
    }
}

so that

% set i 0; while {[incr i] < 3} {puts "$i"}
1
2
% set i 0; do {puts "$i"} while {[incr i] < 3}
0
1
2
% set i 0; do {puts "$i"} until {[incr i] == 3}
0
1
2
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
2

Some commands are explicitly described as treating an argument or arguments as a script or an expression; when evaluation of the script or expression happens (which might be immediately, or might be later, depending on the command) the substitutions described inside that string that is a script or expression are performed. (The subst command is a special case that can only apply a selected subset of substitutions.)

How do you know which is which? It depends on the command. Literally. Go and read the documentation. For example, in the documentation for catch we see:

SYNOPSIS

catch script ?resultVarName? ?optionsVarName?

DESCRIPTION

The catch command may be used to prevent errors from aborting command interpretation. The catch command calls the Tcl interpreter recursively to execute script, and always returns without raising an error, regardless of any errors that might occur while executing script. […]

In this case, we see that the first argument is always evaluated (immediately) as a Tcl script by calling the Tcl interpreter (or rather it's actually bytecode compiled in most cases, but that's an implementation detail).

Similarly, in the documentation for proc we see:

SYNOPSIS

proc name args body

DESCRIPTION

The proc command creates a new Tcl procedure named name, replacing any existing command or procedure there may have been by that name. Whenever the new command is invoked, the contents of body will be executed by the Tcl interpreter. […]

In this case, it's the body that is going to be evaluated as a script (“by the Tcl interpreter” is a form of language that means that) but later, when the procedure is called. (catch said nothing about that; by implication, it acts immediately.)

A third case is the documentation for while:

SYNOPSIS

while test body

DESCRIPTION

The while command evaluates test as an expression (in the same way that expr evaluates its argument). The value of the expression must a proper boolean value; if it is a true value then body is executed by passing it to the Tcl interpreter. […]

From this, we can see that the test argument is an expression (which uses expression rules) and body is a script.


If you want to create a substitution-free single-command script where you can use arbitrary values for everything (this perfect for setting up a callback) use the list command as that is defined to produce lists in canonical form, which happens (by design) to be exactly the form that single commands without substitution-surprises can take:

set xyz "123 456"
set callback [list puts $xyz]
set xyz {[crash bang wallop]}
puts "READY..."
eval $callback
Community
  • 1
  • 1
Donal Fellows
  • 133,037
  • 18
  • 149
  • 215