Argument to a Command

Copied from Ask, and it shall be given # 3

LES on May 02 2005:

 $ set foo {Quoting "one" or "two words" is {tough}.}
 $ set foo
 Quoting "one" or "two words" is {tough}.

Great. However...

 $ proc p> {args} {puts "<p>$args</p>"}
 $ p> Quoting "one" or "two words" is {tough}
 <p>Quoting one or {two words} is tough</p>

 $ p> Quoting "one" or "two words" is {tough}.
 extra characters after close-brace

Ouch! Using the proc, one is not quoted, tough is not braced and the quotes became braces in two words. And if the phrase ends in a period, it generates an error. Why does it only happen when attempted with a proc and how can I clean ALL that mess?

RS: Arguments to a command are early parsed as a list. That will remove grouping markup, i.e. quotes and braces around grouped words. The quotesw around "one" disappear as they're redundant in Tcl syntax. "two words" are grouped, but at Tcl's discretion with braces, not quotes. And in this syntax, the period after the close-brace is considered an error. If you don't want the parsing to occur, add another layer of braces around strings you don't want to be parsed further:

 % p> {Quoting "one" or "two words" is {tough}.}
 <p>{Quoting "one" or "two words" is {tough}.}</p>

LES But that does not produce the desired effect. Those braces should not be there. I suppose you left that as an exercise for me? OK, this proc seems to work:

 proc p> {args} {puts <p>[lindex $args 0]</p>}

another solution is to do this:

 proc p> {string} {puts "<p>$string</p>"}
 p> {Quoting "one" or "two words" is {tough}}

Do you see the difference? When you use "args" it takes all your arguments and places them in a list. That is why the [lindex $args 0] trick works. If you're only passing a single string you can eliminate that need by simply not using "args"

"args" is useful when you want a variable number of arguments for your proc. Multiple, individual words. When you give multiple individual words and use "args", those will be combined into a list, losing all original quoting. The reason for the latter is that the quoting is just that, quoting, rather than part of the actual data. To include quotes as part of the data, those quotes must be in, uh, quotes.

GWM I suggest using JOIN as it uses less arguments.

 proc p> {args} {puts <p>[join $args]</p>}

What I want to do however is construct a set of arguments for a button and use the list:

 pack [button .btn -text fred -command "puts {Dont do that}"]

creates a button which works.

 destroy .btn
 lappend args -text fred -command "puts {Dont do that}"
  button .btn $args 

returns: unknown option "-text fred -command {puts {Dont do that}}". I have tried some 'clever' tricks such as:

 button .btn [eval concat $args]
 button .btn [join $args]

but no joy.

How do I use the args which I have built up other than to get RSI from:

  button .btn [lindex $args 0] [lindex $args 1] [lindex $args 2] [lindex $args 3] 

Apart from any other considerations I might have more arguments, -font Verdana -height 21 and so on. So there wont always be 4 arguments.

Thanks! - RS: Do it like this - eval removes one layer of list structure: :^)

 eval button .btn $args

... and in 8.5, like this:

  button .btn {*}$args

Aha! My own answer to my own question - ask and you shall find out the answer yourself, indeed!

  unset args
  lappend args button .btn -text {Destroy the World} -command "puts {Please don't do that}"
  pack [eval $args]

GWM thanks; NB I am overloading a canvas and need "invokehidden" which should look like this:

  interp invokehidden {} $self create [lindex $args 0] $xylist -outline $penc

The command now needs two layers of {}{} to keep the interpreter name as "blank":

  lappend command [lindex $args 0] $xylist -outline $penc
  eval interp invokehidden {{}} $self create $command