Reputation: 507
I want to pass a string argument to a tcl procedure, without interpreting the contents of brackets as a command, and without using \ to escape the brackets. For example:
proc resolve_signal_path {signal_path} {
puts $signal_path
# Replace [] with ():
set signal_path [string map -nocase {"[" "(" "]" ")"} $signal_path]
return $signal_path
}
puts [resolve_signal_path abc[0]]
This gives an "invalid command name "0"" error. How can I pass such arguments? I tried single quotes '' around the argument to no avail.
UPDATE: In particular is there a way to handle this if the arguments is a list and not a single string, which originates from reading a file line:
proc resolve_signal_path {args} {
set signal_name [lindex $args 0]
puts $signal_name
# Replace [] with ():
set signal_path [string map -nocase {"[" "(" "]" ")"} $signal_name]
return $signal_path
}
# The following "fileline" comes from reading a file so I cannot change it
# fileline: abc[0] 2nd 3rd
puts [resolve_signal_path $fileline]
How can I pass this 'fileline' variable that is a list of strings, considering it contains [] ?
Upvotes: 0
Views: 3518
Reputation: 137557
Tcl uses [
…]
to indicate a command substitution, always, unless you quote it somehow. There are several ways to do the quoting, such as with various backslash sequences (of which \[
…\]
is by far the simplest), but the best method of all is to put the whole argument in {
braces}
, as that simply suppresses all such substitutions (including both backslash and variable substitutions too):
puts [resolve_signal_path {abc[0]}]
puts [resolve_signal_path abc\[0\]]
What Tcl doesn't do is give you a way to say “opt out of doing substitutions for a particular argument to a command” since it formally doesn't look up the meaning/implementation of the command until after it has already processed the substitutions. In that case, you'd need to use the subst
to get the selectivity (it'd be the sort of thing you might conceal inside a command, but that's getting rather more complicated):
puts [resolve_signal_path [subst -nocommands {$name[0]}]]
I don't use that sort of thing very often, to be honest. Using braces and, just occasionally, backslashes mostly works very well.
In the updated question, there's a few extra things to consider.
[
and ]
characters are not actually special to lists, though Tcl's list-generating commands will quote them anyway.args
formal parameter is special (when it is the last argument) and always contains a list of the (remaining) arguments. This means that if $fileline
was itself a list, there's two levels of list-ishness to go through.list
, lrepeat
, lappend
and so on. When parsing general strings to get lists, scan
, regexp
and split
all either return lists or can if asked right.So, I'd parse abc[0] 2nd 3rd
with something like this:
set words [regexp -inline -all {\S+} $line]
Since that will selectively normalise the spacing and add any quoting required. (In this case, it converts the input to {abc[0]} 2nd 3rd
.) You'd need to sort out exactly what value corresponds to $line
— I'm not quite sure from your code — but once the list-conversion is done, you can safely use lindex
to pick out individual words, whatever characters they once had.
set signal_name [lindex $words 0]
# It'll now be exactly “abc[0]”
Parsing is in general a matter of understanding what input you could have, and making it easy for your code to actually understand that input for what it is. Which sounds simple.
Upvotes: 2