Usage of set command - tcl

I'd like to understand following code (was provided by third-party vendor) to that of simpler version.
#vendor code
set X "This is a string"
set $X {1.24 1.75}
set Y [set $X]
puts $X
puts $Y
#simpler code
set X "This is a string"
set Y {1.24 1.75}
puts $X
puts $Y
Both give same results. Is there any reasoning behind this extra assignment command?
Thanks

There's no reason. Let me explain how the first block works.
set X "This is a string"
Assign string This is a string to the X variable.
set $X {1.24 1.75}
This will be evaluated by interpreter as:
set "This is a string" {1.24 1.75}
I mean, you will set the variable This is a string to the value 1.24 1.75 (yeah, in tcl you can use ANY string as a variable name).
set Y [set $X]
This will be evaluated by interpreter as:
set Y [set "This is a string"]
It means, that variable Y must be set equal to the value of the variable This is a string (which is 1.24 1.75).
Thus, 2 your blocks are equal. Except that in the first block an additional variable This is a string will be defined. I consider this an undesirable definition.

Related

Variable substitution in TCL within curly brace

I'm having a problem where variables are in curly brace.I am trying to perform a variable substitution within curly braces which I understand is not feasible directly in TCL.But if there are other methods to resolve this? because I see the samiliar question in website that the answer is use list [] and others. But I want to countinue use curly brace, could someone can help me to resolve the question?
set top_design a
puts $top_design
puts {aaa %top_design}
output :
a
aaa %top_design
so how to display the subtitute of top_design in second puts.
Aside from the % typo, you're looking for the subst command:
set top_design a
puts {aaa $top_design} ;# => aaa $top_design
puts [subst {aaa $top_design}] ;# => aaa a
There are options to subst so that you have control over which things get substituted:
% puts [subst {aaa $top_design\n[clock seconds]}]
aaa a
1666273294
% puts [subst -nocommands -nobackslashes {aaa $top_design\n[clock seconds]}]
aaa a\n[clock seconds]
I think that you have messed up a little while forming the question.
In Tcl, { and } have special meaning, you can simpy say that this is an escape sequence or a list - depending on where it is used.
Let's see the following simple example:
set v1 aaa
set v2 bbb
set vres1 {$v1 $v2}
set vres2 "\{$v1 $v2\}"
set vres3 "\{\$v1 \$v2\}"
set vres4 {{$v1 $v2}}
This will result in the following output:
% set vres1 {$v1 $v2}
$v1 $v2
% set vres2 "\{$v1 $v2\}"
{aaa bbb}
% set vres3 "\{\$v1 \$v2\}"
{$v1 $v2}
% set vres4 {{$v1 $v2}}
{$v1 $v2}
For vres1, you have variables not evaluated as {} were used as an escape sequence.
For vres2, braces are escaped so are treated as a standard character, thus "" are needed to properly set all the characters as one argument for set command. Note that v1 and v2 are evaluated here as {} are not escape characters but characters that we are just saving in variable.
For vres3, we did manual escaping of braces and $ so that variables are not evaluated and braces are part of the string.
For vres4, first set of braces are escaping the second set and also $.
Now I guees, that you already have something like vres3 or vres4 and you want to get the values of the variables in braces.
When you have nested variables, you should use the eval command like below:
% eval set out1 \"$vres1\"
aaa bbb
% eval set out2 \"$vres2\"
{aaa bbb}
% eval set out3 \"$vres3\"
{aaa bbb}
% eval set out4 \"$vres4\"
{aaa bbb}
I hoped it helped in your issue!
[Ufff, my first answer!]
In Tcl 8.7, you'll be able to do this:
set top_design a
puts $top_design
set input {aaa %top_design}
puts [regsub -all -command {%(\w+)} $input {apply {{- varName} {
upvar 1 $varName var
# Ignore case where variable doesn't exist
return $var
}}}]
The key thing is that %top_design isn't very special at all to Tcl code; the % symbol is only meaningful in a few contexts (format, clock, expr, and Tk's bind). For it to have any other meaning, you have to apply that meaning yourself. That gets much easier with regsub -command (a new feature in 8.7) since that lets you use a command to generate the substitution within a regsub; that pairs well with apply though you could use a procedure instead. In earlier versions, such substitutions required non-trivial quoting and subst; I always found those things difficult to write correctly.

Can be used an expression in the name of variable for reading it in Tcl?

#In Tclsh
% set n 3
3
% set A$n 15
15
% puts $A3
15
But how could I read $A3 as about like ${A{$n}}
Itried:
% puts $[puts \$A$n]
$A3
$
%
I clarify my question date:10Aug2022 (UTC 09.14)
In Bash I can do a deeper indirection as like this:
a=b;b=5;eval echo $`echo $a` # output: 5 is good
How can I do it in Tcl with puts command instead of set command as like this:
set a b;set b 5;eval {puts [puts \$$a]} # it has wrong output: $b rather than 5
Macleod showed that the set command with only one argument is a workaround of a deeper indirection.
That is why the next line is good:
set a b;set b 5;eval puts $[set a] # Output: 5 as required is good
So my question is:
In the above Tcl line how can I replace the set command with puts command and do a deeper indirection in Tcl as like in Bash.
/echo in Bash is as like puts in Tcl/
My question is not for a practical purpose, but for understanding the parsing, substitution in Tcl.
Should work with just:
puts [set A$n]
I have read the Tcl man about substitution process and I have made some experiments.
The puts command unusable for command substitution according to the next examples.
% set x 1; set x [puts 2]; puts "x: >$x<"
x: ><
% set x 1; set x [puts -nonewline 2]; puts "\nx: >$x<"
x: ><
% set x 1; set x [expr 2]; puts "x: >$x<"; # But expr cmd ok of course.
x: >2<
( In Bash the echo command is good for command substitution eg.: a=`echo "apple tree"` )
I checked more type of deeper indirect addressing of variable, here is my experiments:
Now let the names of var is numbers.
% set 1 2; set 2 3; set 3 4; #Here is the chain of var
% puts [set [set [set 1]]]; #Command substitution only
4
% puts [expr $[expr $[expr $[expr 1]]]]; #Command and variable substitution
4
Now let the names of var is alphas and change the expr command to the string trim as a dummy string
expression command.
% set a b; set b c; set c d ; # Here is the chain of var
% puts [set [set [set a]]] ; # Command substitution only
d
# Command and variable substitution:
% puts [eval string trim $[eval string trim $[eval string trim $[string trim a]]]]
d
I would like to know why I had to use eval command unlike in case when the names of var were numbers
and expr command was enough.
In spite of there were deep (indirect) var (and command) substitution was in both two cases .
So it is looks like that deep command substitution controlled by brackets while deep (indirect) var substitution
controlled by eval often.
Likewise in Bash the deep var substituting also happens with eval command, e.g.:
a=b; b=c; c=d # Here is the chain of var
eval echo \$$(eval echo \$$(eval echo \$a))
d

How to pass arguments to tcl scripts when using tclsh [duplicate]

This is the code in TCL that is meant to produce factorial of a number given as parameter by the user.
if {$argc !=1}{
puts stderr "Error! ns called with wrong number of arguments! ($argc)"
exit 1
} else
set f [lindex $argv 0]
proc Factorial {x}{
for {set result 1} {$x>1}{set x [expr $x - 1]}{
set result [expr $result * $x]
}
return $result
}
set res [Factorial $f]
puts "Factorial of $f is $res"
There is a similar SO question, but it does not appear to directly address my problem. I have double-checked the code for syntax errors, but it does not compile successfully in Cygwin via tclsh producing the error:
$ tclsh ext1-1.tcl
extra characters after close-brace
while executing
"if {$argc !=1}{
puts stderr "Error! ns called with wrong number of arguments! ($argc)"
exit 1
} else
set f [lindex $argv 0]
proc Factorial {x}{..."
(file "ext1-1.tcl" line 3)
TCL Code from: NS Simulator for Beginners, Sophia-Antipolis, 2003-2004
Tcl is a little bit more sensitive about whitespace than most languages (though not as much as, say, Python). For instance, you can't add unescaped newlines except between commands as command separators. Another set of rules are that 1) every command must be written in the same manner as a proper list (where the elements are separated by whitespace) and 2) a command invocation must have exactly the number of arguments that the command definition has specified.
Since the invocation must look like a proper list, code like
... {$x>1}{incr x -1} ...
won't work: a list element that starts with an open brace must end with a matching close brace, and there can't be any text immediately following the close brace that matches the initial open brace. (This sounds more complicated than it is, really.)
The number-of-arguments requirement means that
for {set result 1} {$x>1}{incr x -1}{
set result [expr $result * $x]
}
won't work because the for command expects four arguments (start test next body) and it's only getting two, start and a mashup of the rest of other three (and actually not even that, since the mashup is illegal).
To make this work, the arguments need to be separated:
for {set result 1} {$x>1} {incr x -1} {
set result [expr {$result * $x}]
}
Putting in spaces (or tabs, if you want) makes the arguments legal and correct in number.

Cannot evaluating variable within IF and For loop using TCL

In following code in TCL I cannot seem evaluate the variable "a"
I'm evaluating x and y, in the same For Loop I have a IF statement that is checking for a range between x and y.
If valid then I'd like to perform some more calculations within the IF condition.
Every thing is fine up to the IF condition, but I cant seem to evaluate "a".
I'm trying to set "a" to the value of "y" for all the values within the range $min <= $x && $x <= $max
I would kindly request the experts to highlight the mistake.
for {set i 0} {$i < $lenght} {incr i} {
set x [expr ([lindex $cal1 $i])*$offset]
set y [expr ((cal2)/2) ]
if {$min <= $x && $x <= $max } {
puts "is Active"
set a [lindex $y $i]
puts a = $a
}
}
There is a lot that seems problematic in your code.
In the first line, you use the variable lenght. Tcl doesn't care about spelling, but if you don't have such a variable (you might possibly have a length variable instead) you will get an error.
In the invocation expr ([lindex $cal1 $i])*$offset] you have an unnecessary parenthesis but no braces around the expression (the braces aren't mandatory but should be there unless there is a very good reason to omit them). Also: "offset" usually means something you add to, not multiply with, another value. The invocation expr {[lindex $cal1 $i] * $offset}] would be better.
The variable y is used as a list argument to lindex later on, but it's created as a scalar variable. Also, your expression divides a string (or rather, an invalid bareword) with 2. Maybe you meant lappend y [expr {$cal2 / 2}]? If you use lappend, each value will be added to the end of an existing list, or as the first element of a new list if y doesn't exist. This is usually what one wants, but it means that the list y should be reset using set y [list] or set y {} before entering the loop, to get rid of elements added earlier, if any.
puts a = $a won't work, because if there are more than one argument to puts they are expected to be the flag -nonewline and/or a channel id to send the output to. Maybe you meant puts "a = $a".

How to define a variable with argument expansion

The following command runs as expected:
lappend {*}{arr 1}
puts [lindex $arr 0]
Now I am trying to make a variable of "{*}{arr 1}" like this:
set X "{*}{arr 1}"
lappend $X
But this does not work, seems $X is taken as one whole value, argument expansion is not effective.
So is it a requirement that argument expansion can not be through variable?
The {*} is a syntactic feature of Tcl (from Tcl 8.5 onwards) just as […], "…" or $ is. You have to write it in the script in order for it to count as argument expansion; otherwise it's just a sequence of three characters.
If you want something like
set X "{*}{arr 1}"
lappend $X
to work, you need to pass it through eval:
set X "{*}{arr 1}"
eval lappend $X
Note that this then means that X actually contains a script fragment; this can have all sort of “interesting” consequences. Try this for size:
set X "{*}{arr 1};puts hiya"
eval lappend $X
Use of eval in modern Tcl is usually a sign that you're going about stuff the wrong way; the key use in old scripts was for doing things similar to that which we'd use {*} for now.
No, within double quotes, { and } actually lose their meaning, so will {*}. Notice that puts "{}" and puts {} are different.
The closest I can think of to do what you're trying to do would be to use something like this:
set X {arr 1}
lappend {*}$X
So if you now execute puts [lindex $arr 0], you get 1 as output.