subst variable names with "point" [duplicate] - tcl

This question already has an answer here:
Double variable substitution in tcl
(1 answer)
Closed 3 months ago.
Let's say I have this code :
set a 1
set a.b a
set thing a.b
puts [subst $$thing]
The answer I would expect on the last line would be "a", but tcl answers 1.b
I tried to put \ everywhere before the . but it didn't changed anything.
Is there away to get a from thing variable ?

Tcl does not double evaluate two consecutive dollar signs.
The $thing characters in your command subst $$thing are first replaced by the value of $thing, which is a.b.
Subsequently, the subst command is evaluated like this:
subst $a.b
The above subst command replaces $a with 1, which explains why you get 1.b returned.
A reliable way to do multiple variable interpolation is with the set command without a second argument. Chain together multiple set commands to interpolate multiple times.
puts [set thing]
--> a.b
puts [set [set thing]]
--> a
puts [set [set [set thing]]]
--> 1

Related

TCL commenting out items in a list

Is there a simple way of commenting out items in a list?
set ll [list \
tom \
# dick \
# harry \
martha]
puts [llength $ll]
Quite annoyingly notepad++ and vim highlight the lines with #, and the the line containing martha, fooling me into thinking that they were commented out.
I thought the length would be 2 but it was 6 - it counted the # as separate list items. I have a fairly long list and sometimes I would like to run the script without those items. I would like the keep the original items in the list so that the next person modifying it knows what is available and can comment/uncomment the items accordingly.
The only alternative I could think of was if the item in the list is a #, then skip the next item. Is there a simpler way of doing this?
To allow configurable content in a data structure, so that you can have every item available but only actually use those that you want to use in the current configuration, it is better to use one of several strategies to select the items to be used, instead of trying to make the interpreter do the selection for you.
One possible way to do this:
lmap item [concat {*}{
tom
dick
#harry
#martha
}] {if {[string match #* $item]} {
continue
} else {
set item
}}
In Tcl, only commands can be commented out, and there are pitfalls involved even when you try that.
The # syntactic marker is only interpreted as the start of a comment if the name of a command was expected in that place. Everywhere else, it's just a normal character (even if syntax highlighting often mistakes it for a comment).
# ceci n'est pas une 'comment'
upvar #0 foo bar
To temporarily remove items from a list, for e.g. debugging purposes, it is better to just redo the definition:
set ll [list \
tom \
dick \
harry \
martha]
set ll [list \
tom \
martha]
The last definition will be used. This can become confusing if the change isn't limited to one editing session. To avoid this, saving the original code and completely rewriting the code in use can be a good choice.
See the Scripted List command on the Tcl wiki: http://wiki.tcl.tk/scripted+list
You can't comment out elements like that. Tcl's built-in comment processing only finds comments at points where command names can start, and that's not in the middle of arguments to the list command. Or any other command (though some commands take scripts, which can contain comments that are parsed as such when the script is parsed).
If you were building the list like this:
set myList {
a b c
# commented out
d e f
}
then it would be possible to make things work by post-processing that string before treating it as a list. I do this quite a bit in my longer scripts.
set myList [regsub -all -line {^\s*#.*$} $myList ""]
It's not a perfect solution as it possible to defeat it by being tricky, but it works fine for me.
However, when dealing with list construction like this:
set myList [list a b c \
# commented out \
d e f]
That's much more complicated! The problem is that the newline has already gone by the time the list is written into myList; the only workable fix I can think of is to make source preprocess the whole script! A way to do that is below. (This is not perfect; some introspection techniques can detect what is going on.)
proc source args {
# Argument parsing; the full works
set enc [encoding system]
if {[llength $args] > 1 && [lindex $args 0] eq "-encoding"} {
set enc [lindex $args 1]
set args [lrange $args 2 end]
}
if {[llength $args] != 1} {
return -code error \
"wrong # args: should be \"source ?-encoding name? fileName\""
}
set fileName [lindex $args 0]
# Read in the script
set f [open $fileName]
fconfigure $f -encoding $enc -translation auto -eofchar \x1a
set script [read $f]
close $f
# Pre-process the script; note that we're more careful with backslashes here
set script [regsub -all -line {^\s*#.*([\\]?)$} $script {\1}]
# Evaluate the script in the caller while setting [info script]
info script $fileName
uplevel 1 $script
}
Again, this isn't perfect, but it's likely to work fine for your code as long as you define this procedure replacement for source before loading in your real code. But I just use the first technique — post-processing the lists I've put comments in after construction — instead, and I don't use it for short lists anyway (since those can always have a comment before instead).

how lappend $varname1 $varname2 will be executed

I have searched for lappend $var1 $var2, but don't find any exact answer, how it will be executed.
% set a {a b c}
a b c
% set b {d e}
d e
% puts [lappend $c $b]
can't read "c": no such variable >>> here it throws error like variable not exist
% puts [lappend $a $b]
{d e} >>> here it doesn't throw any error, can someone explain it and how to print the value of $a, if $a is a new variable
% puts $$A
can't read "A": no such variable
% puts $$a
$a b c
% puts ${$a}
can't read "$a": no such variable
Tcl's got a two level syntax that it applies rigorously to everything. The first level is the Tcl generic syntax, which takes:
lappend $var1 $var2
and parses it out to three words: lappend, a word obtained by reading the variable var1, and a word obtained by reading the variable var2.
Then Tcl dispatches to the command named by the first word (lappend, a Tcl built-in) which applies command syntax handling. In the case of lappend, it's pretty simple: the first argument names a variable and the second and subsequent arguments are words to append to the list in the named variable.
In your case, the first argument that names a variable is obtained by reading another variable (var1) and the value to append to the list is coming from a variable (var2); a name like a b c d e is a legal variable name in Tcl, but it's really awkward to use. And the chance is very high that you don't want to write that: putting variable names in a variable is usually an indicator of confusing code. You can do it, but you hardly ever want to do it (except when you're using the variable name with upvar). You probably really meant to write:
lappend var1 $var2
Tcl is very exact about the distinction between variable names and variable contents. The $ is not decorative! It's there to say “read this variable, right now”, and $var1 is virtually equivalent to [set var1] in semantic terms. (The $ shorthand was later, a Tcl 2.0 feature from way back in the day!)
Tcl also doesn't allow double-dereferencing with $$vrbl. In the rare cases you need it, you do [set $vrbl]. And if you do that, you probably should immediately see if you can use an array instead as that's typically a better choice…
lappend's first parameter is a variable name, not a value. Therefore, in general, it should be:
lappend var1 $var2
where both var1 and var2 are list variables. See the Tcl lappend man page for more details.

variable substitution - each value becomes an argument

When a variable is substituted and the value is a list, is it possible that each value of the list is regarded as one argument?
Here is an example code:
set a "-nonewline hi"
puts $a
What I really want is after the substitution, the puts command becomes puts -nonewline hi instead of puts "-nonewline hi".
Is it possible?
Yes, you can use something like this:
puts {*}$a
In versions prior to 8.5, you would have to use eval:
eval puts $a

How tcl curly braces in ${variableName} is interpreted?

I am a newbie in TCL Programming. I was having confusion about curly braces, answer to this question tcl curly braces cleared most of my doubts.
I can understand $var, {var}, and {$var}, But recently I came across another use of curly braces, ${var}. How is this interpreted by TCL?
I have seen this is used when accessing variables in namespaces when namespaces name is in variable.
for example:
set x myNamespace ;#myNamespace is name of namespace
puts [set ${x}::var1] ;#var1 is variable in the namespace
It gives error when you don't use curly braces around 'x'.
And I also don't understand the difference between {a b c} and [list a b c], what is the difference in result of interpretation of these two commands by TCL interpretation.
elaborated explanation would be highly appreciated.
See rule 8 of the manual. It allows you to have variable names that might get mis-interpreted. For instance:
% set dotted.name 1
1
% puts $dotted.name
can't read "dotted": no such variable
% puts ${dotted.name}
1
Read section 8 carefully as it actually explains all this quite explicitly.
Update to answer edited question
In the example you provide using a namespace name in a variable you must consider section 8 part 1: a variable name includes letters, digits, underscores and namespace separators. This means that x::var1 is a valid variable name. So $x::var1 will attempt to dereference the var1 variable in the x namespace. As this is not what you meant, you must dereference your x variable separately. There are two ways to do this. You can either use the set command or the dollar operator.
set x myNamespace
puts [set ${x}::var1]
puts [set [set x]::var1]
The two puts statements are equivalent here with the second version showing an explicit separate pass to obtain the value of the x variable which is then substituted into the expression for the outer set command. The same occurs in the first version but just uses the grouping operator to restrict the effect of the dollar to the x variable name.

Why the subst command only does variable substitution in my case? [duplicate]

This question already has answers here:
TCL subst or eval command is not working in my case ..
(3 answers)
Closed 9 years ago.
Tcl's subst command should do command , variable , backslash substitution .
proc sum {a b} {
return [expr $a+$b]
}
%
% set a 1
1
% set b 2
2
subst {sum {$a} {$b}}
subst command should do the variable and command subsdtitutions , Here why command substitution not happend .
sum {1} {2}
subst command should do the variable and command subsdtitutions , Here why command substitution not happend . why it is only substituting the variables ?
Command substitution will look for the pattern [...] and replace it with the returned result of the command inside the brackets, but your string doesn't have any text that matches that pattern.