Lars H, 2010-03-26: Doing some late winter cleaning (spring won't arrive up here for a few weeks yet), I came across the following little proc which looks useful although it won't fit in with the rest of the package I originally wrote it for. Hence giving it a page on the wiki seems a reasonable housing arrangement.
What it is about is template-based construction of lists. My motivating example was command prefixes (of a complicated sort, which would often contain other command prefixes as elements), but it might well be useful also for other things.
One way of thinking about it is as a sort of format for lists.
## # listtemplate - # minilanguage for constructing lists (e.g. command prefixes) # # Synopsis: # listtemplate $template ?$parameter ...? # # The $template is a list, from which another list (the result) # is built. The following strings will, if encountered as list # elements, be interpreted as preprocessing directives rather # than explicit list elements: # # #<number> # Appends the <number>th (0-based index) $parameter to the list # being constructed. # # #*<number> # Interprets the <number>th $parameter as a list and appends # its elements to the list being constructed. # # #{ # Begins sublist. # # #} # Ends sublist, and contributes it as one list element to the # surrounding list. # # #eval # The next $template element is evaluated in the calling # context, and the result is appended to the list being # constructed. # # #nswc # The next template element is in the calling context given # as an argument to [namespace which -command], and the # result of that is appended to the list being constructed. # # #nswv # The next template element is in the calling context given # as an argument to [namespace which -variable], and the # result of that is appended to the list being constructed. # # ## # Escapes directive-interpretation of next template element. # # # # Next template element is ignored; can be used to embed # a comment. # # A template element is assumed to be a directive if and only if # its first character is #. # # Side-effects: None. # ## proc listtemplate {template args} { set state normal set stack {} set res {} foreach item $template { switch -- $state normal { if {[string index $item 0] != "#"} then { lappend res $item continue } switch -regexp -- $item {^#\{$} { lappend stack $res set res {} } {^#\}$} { if {[llength $stack]} then { set res [linsert [lindex $stack end] end $res] set stack [lreplace $stack end end] } else { error "Too many #\}" } } {^#[0-9]+$} { scan $item "#%d" index lappend res [lindex $args $index] } {^#\*[0-9]+$} { scan $item "#*%d" index eval [linsert [lindex $args $index] 0 lappend res] } {^#(eval|nswc|nswv)$} { set state [string range $item 1 end] } {^##$} { set state verbatim } {^#$} { set state ignore } default { error "Unknown directive: $item" } } verbatim { lappend res $item set state normal } eval { lappend res [uplevel 1 $item] set state normal } nswc { lappend res [uplevel 1 [list\ ::namespace which -command $item]] set state normal } nswv { lappend res [uplevel 1 [list\ ::namespace which -variable $item]] set state normal } ignore { set state normal } } if {$state != "normal"} then { error "Missing data for: $item" } elseif {[llength $stack]} then { error "[llength $stack] sublist(s) not properly closed" } else { return $res } }
For example:
% set w .win % listtemplate {#0 configure -xscrollcommand #{ #1 set #}} $w.c $w.h .win.c configure -xscrollcommand {.win.h set}