Subst template expansion helpers

JBR 2010-05-31 :

I often use subst to generate code for my own or one of Tcl's mini languages. Here is a little template helper that I wrote last week. I like the code because it reuses foreach and allows substitution of variables in the current scope, but I'm dissatisfied with the need and technique to awkwardly generate uniq string accumulator variable names to support recursion. Any ideas for improvement would be appreciated.

package require Tcl 8.5

proc template:foreach args {
    incr ::template:level

    set reply [uplevel 1 [subst {
        set _${::template:level} {}
        foreach {*}[lrange $args 0 end-1] { append _${::template:level} \[subst {[lindex $args end]}] }
        set _${::template:level}
    }]]

    incr ::template:level -1
    set reply
}


interp alias {} : {} template:foreach


puts [: x { 1 2 3 } {
        Row $x [: y { a b c } { Column $y }]
     }]

Output:

Row 1  Column a  Column b  Column c 

Row 2  Column a  Column b  Column c 

Row 3  Column a  Column b  Column c 

user 2010-06-01: Why not:

proc : args {
    foreach * [lrange $args 0 end-1] {
        append {} [subst [lindex $args end]]
    }
    set {} 
}

puts -nonewline [: x {1 2 3} {Row $x[: y {a b c} { Col $y}]\n}]

JBR: In this version, subst doesn't run in the caller's scope. This doesn't work:

set XXX {This string too}
puts -nonewline [: x {1 2 3} {Row $x[: y {a b c} { Col $y}] $XXX\n}]

JBR 2010.06.01: Here is a better version that uses info frame to stack var names.

proc template:foreach args {
    return [uplevel 1 [subst {
        set _[info frame] {}
        foreach {*}[lrange $args 0 end-1] { append _[info frame] \[subst {[lindex $args end]}] }
        set _[info frame]
    }]]
}

JBR 2010-05-31: Sitting here watching the A-Team this evening I wrote a similar procedure for template:if:

package require Tcl 8.5


proc K {x y} {set x}


proc shift V {
    upvar $V v
    K [lindex $v 0] [set v [lrange [K $v [unset v]] 1 end]]
}  


proc template:if args {
    set cmd [subst { if { [shift args] } { return \[subst {[shift args]}] } }]
    while { [llength $args] } {
        switch [shift args] {
         else   { append cmd [subst   { else { return \[subst {[shift args]}] } }] }
         elseif { append cmd [subst   { elseif { [shift args] } { return \[subst {[shift args]}] } }] }
        }
    }
    return [uplevel $cmd]
}


interp alias {} ? {} template:if


set x 1
puts [? $x { True } else { False } ]
set x 0
set y 1
puts [? $x { True } elseif { $y } { Y is True!! } else { False } ]

Output:

True
Y is True!!

Here is another little thingy for switch.

proc template:switch {value cases} {
    foreach {case code} $cases {
        switch $code {
               - { lappend body $case - }
         default { lappend body $case "return \[subst {$code}]" }
        }
    }
    return [uplevel 1 [subst {switch $value { $body }}]]
}