TCL: How to escape a single brace '{' in a string quoted using braces '{...}'?
Asked Answered
I

2

5

I am trying to use curly-braces to define a string { } instead of double-quotes " ", so that I don't have to escape several characters (such as $, [, ]).

However, I am running into some problems when my string needs to contain a single { within it.
I know that I can achieve this by simply using a double-quoted string and escape the {, but how would I do it using a "curly-brace string"?

Eg. I want to puts the following string 'proc foo { } {' to stdout.

puts "proc foo \{ \} \{" gives me the desired output: 'proc foo { } {'

However, puts { proc foo \{ \} \{ } gives me: 'proc foo \{ \} \{' by literally printing the backslashes.

If I skip the backslashes, puts { proc foo { } {, it complains about a missing brace.

Also, if the desired string has a matching closing-brace within it, it works fine.
puts { proc foo { } { } } gives me the expected: 'proc foo { } { }'

What is the correct way to escape a single unmatched curly-brace in a "curly-brace string"?

Irrecusable answered 20/7, 2015 at 21:29 Comment(0)
C
9

Sorry, the terms of service for braced strings require you to have a matching close brace for every open brace. The only way to have non-matching braces is to quote/escape them with a backslash, but no backslash substitutions will be performed, so the backslash goes into the string along with the lone brace.

That doesn't mean you can't construct the string you want, for instance like this:

set foo { proc foo { } }
# ->  proc foo { } 
append foo "{ "
# ->  proc foo { } { 

(BTW, you don't need to backslash the braces in a doublequoted string, " proc foo { } { " will do fine.)

However, the string you are building seems to imply that you are building a procedure definition within a script, and going about it the wrong way. Instead of assembling the definition like this:

set foo { proc foo { } }
# ->  proc foo { } 
append foo "{ "
# ->  proc foo { } { 
append foo { puts foo }
# ->  proc foo { } {  puts foo 
append foo " }"
# ->  proc foo { } {  puts foo  }

You should do this:

set name foo
set args {}
set body {puts foo}
proc $name $args $body

which defines the procedure directly. If you need it as a string, wrap it up like this:

set foo [list proc $name $args $body]
# -> proc foo {} {puts foo}

Yes, I am using list to construct a string. It's because wrapping proc $name $args $body in double quotes will lead to loss of the list structure of the command. I create a list, and then use the string representation of that list as a string.

If you do it this way, you're working with the Tcl interpretation rules and taking advantage of them, rather than working against them. That means less work and fewer errors.

Documentation: append, list, proc, set

Contravene answered 20/7, 2015 at 22:13 Comment(0)
H
1

The best solution is to use subst. Other constructions may work, but are unclear or ad hoc. In your example, write:

set str {proc foo \{ \} \{}
==> proc foo \{ \} \{     # string is unchanged
set str2 [subst $str]
==> proc foo { } {        # backslashes are replaced

Note that by using subst, '\n' will be replaced by a literal end-of-line. Use '\\n' instead, to retain '\n'.

If the string contains square brackets or '$' characters that you want to retain, you can add: -nocommands, or -novariables. For example:

set str [subst -novar {proc foo \{ \} \{$var}]
==> proc foo { } {$var

You can find more similar tips at: https://ipenv.com/ins-and-outs-tcltk

Hierolatry answered 16/1, 2019 at 8:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.