'''scripted list''', by [Poor Yorick], is a procedure that takes a script, but instead of evaluating each "command" as a command, it builds up a list by concatenating all the words of all the "commands". In other words, what is normally the command name in a script becomes just another item in the resulting list. The advantage is that it isn't necessary to use the backslash character when the list spans multiple lines, and in-line comments are supported. The question often arises: "How can I write a list using command and variable substitution, without having to escape the newline between the elements, and with the ability to make comments in between elements of the list and comment out some elements of the list"? Scripted lists are the answer. ** Example ** ====== set mylist [sl { one two three ;#this is a comment $somevar #ha ha! a two-line \ comment in the list! [some command] {;} ;#literal semicolons must be escaped {some literal} "some $substituted[value]" }] ====== ** More Complex Example ** ====== set six six proc some {args} { return {seven eight} } proc value {} { return {nine ten} } proc side {args} { upvar var2 var2 set var2 twelve } set substituted eleven set mylist [sl { one two three ;#this is a comment {four five} $six #this is a a two-line \ comment in the list! [some command] {;} ;#literal semicolons must be escaped "[value] $substituted" only the first command in a line get the special scripted list treatment, \ but then only only the value othe last command in a line counts which \ means that this line is like a comment with [side effects], and its \ result is whatever the last command produces; set var2 }] puts $mylist ====== Output: ======none one two three {four five} six {seven eight} {;} {nine ten eleven} twelve ====== ** Implementation ** ====== proc sl script { set res {} set parts {} foreach part [split $script \n] { lappend parts $part set part [join $parts \n] #add the newline that was stripped because it can make a difference if {[info complete $part\n]} { set parts {} set part [string trim $part] if {$part eq {}} { continue } if {[string index $part 0] eq {#}} { continue } #Here, the double-substitution via uplevel is intended! lappend res {*}[uplevel list $part] } } if {$parts ne {}} { error [list {incomplete parts} [join $parts]] } return $res } ====== ** See Also ** [scripted templates]: a twist on scripted lists [Convenient list arguments - larg]: a similar idea from [jcw] ** Discussion ** [aspect] 2014-07-05: I notice the implementation above doesn't deal with inline semicolons very well -- `[[sl {one;two;three}]]` tries to evaluate `list one;two;three` instead of interpreting it as three list elements. Adding the inner loop from [cmdSplit] fixes this. .. which leads to this dense implementation in terms of the latter: ====== proc sl script { concat {*}[lmap part [cmdsplit $script { if {[string match #* $part]} continue uplevel 1 list $part }]] } ====== [PYK] 2014-08-01: Yes, at the time I was interested in a version that didn't split on semicolons, but now think its too brittle to be useful. I'm using this dense version more often these days. <> list