codeshot
codeshot

Reputation: 1193

How to perform expansions in a string in the context of a calling function in tcl

How can I implement expand in the script below (expanding commands and variables as normal in tcl), I want it to print out:

'
    hello {
        $b
    }
    {tcl world}
'

This is how I imagine the script will look:

proc clever_func {script} {
    set script [uplevel 1 expand [list $script]]
    puts "'$script'"
}

proc isolated_client_function {} {
    set a hello
    set b difficult
    set c tcl
    set d world

    clever_func {
        $a {
            $b
        }
        [list $c $d]
    }
}

isolated_client_function

Upvotes: 1

Views: 193

Answers (2)

glenn jackman
glenn jackman

Reputation: 247210

The subst command will take you most of the way there.

proc expand {script} {
    uplevel 1 [list subst $script]
}

set a hello
set b difficult
set c tcl
set d world

expand {
    $a {
        $b
    }
    [list $c $d]
}

outputs

    hello {
        difficult
    }
    tcl world

I'm not sure off the top of my head how to prevent $b from being substituted. You'll probably have to parse the script yourself.

If you want the last part to have the quoting you demonstrate, you need another level of list-ness

expand {
    $a {
        $b
    }
    [list [list $c $d]]
}

outputs

    hello {
        difficult
    }
    {tcl world}

Upvotes: 1

Anton Kovalenko
Anton Kovalenko

Reputation: 21517

An example isn't a sufficient replacement for a specification, but it seems that there are no built-in TCL facility doing what you want. It would be possible to perform a substitution on a single command tail (by prepending list to a command and evalling it at any level you want), but not for a pseudo-script with two "commands". (It's also possible to do what string interpolation does, using subst, but you probably already know why it's not what you want: it will also expand $b).

I see two possibilities to get what you want:

  1. Tokenize your input using sugar::scriptToList, then perform substitution manually in the list of tokens, then convert the result back to textual form with sugar::listToScript. These features of sugar macroprocessor are designed to let you modify scripts semantically while preserving formatting, comments and layout.

  2. Break down your input into separate "commands": first split at each newline and semicolon, then use info complete to collect pieces corresponding to complete "commands" (IIRC there will be some corner cases with backslash-newline continuation: beware). On each complete "command" use the trick of prepending "list ", evaluating the result in necessary context (uplevel). Thus for each command you'll get a list where substitution has already been performed when appropiate. You will lose nuances of formatting inside each "command" (like the number of spaces separating words and the kind of space), and you will lose original command separators unless you take care to remember them yourself. It might not be bad for you if the goal is to get a kind of "pre-expanded script" to be evaluated later in some other context.

Upvotes: 2

Related Questions