A '''scripted [list]''' is a Tcl [script] in which every [word] of every [command] becomes a value in the resulting list, with the first word of each command being considered just another value instead of being used as the command name. ** See Also ** [scripted templates]: a twist on scripted lists [Convenient list arguments - larg]: a similar idea from [jcw] ** Download ** In addition to the full implementation below, `sl` is also available as `[ycl%|%ycl list sl]`. ** Description ** One way to look at a Tcl script is as a sequence of words that are grouped into commands. The `sl` command presented below extracts those words into a single list while obeying the semantics of a Tcl script. Representing a list in this way, as a Tcl script, allows for elements on multiple lines without having to escape the newline, and also allows for comments between words in the list. For example a `[switch]` can contain comments between its blocks: ====== set grail 42 switch $answer [sl { #set course for Traal {a lurgid bee} { lindex {Do not be alarmed} } #By Agrajag! $grail { lindex {Oh no, not again.} } #clueless Golgafrinchams default { puts [list whoa $answer] error {get your cave redecorated!} } }] ====== the following procedure provides the described behaviour: ====== proc sl script { concat {*}[lmap part [scriptSplit $script] { if {[string match #* $part]} continue uplevel 1 list $part }] } ====== Another use for a scripted list is in parsing data formatted as a Tcl script: ====== proc configure script { foreach command [cmdSplit $script] { set command [namespace eval configns [ list [namespace which sl] $script]] set cmdargs [lassign $command name] switch $name { someparameter { do something with $cmdargs } someotherparameter { do something with $cmdargs } } } } ====== ** A Twisted Scripted List ** This list is a twist on the standard scripted list in which only the first command in a series of semicolon-separated commands becomes part of the result. The additional commands in such a series of commands might have side effects that influence the composition of the list. The current examples don't show a compelling use case, but perhaps someone will do something interesting with it. '''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 } ====== ** History ** The following was migrated here from [Additional string functions]: '''Substitution that Preserves Grouping ''' [RHS] 2005-02-23: There have been a number of times where I have wanted a version of '''subst''' that preserves grouping. For example, I want to be able to do the following: ====== % array set myarr [subst { a $x b [getConfigValue something] c { a b c d } }] % array get myArr a 1 b blah c { a b c d } ====== My main reason for this is, when the dataset get large, I tend to find \ line continuations fairly ugly... That, and my emacs config doesn't indent well for multiple levels of line continuations if there's sub levels. In pursuit of the above, I came up with the following: ====== proc gsubst input { set data {} foreach line [split $input \n] { if {[info complete $data]} { append data " $line" } else { append data "\n$line" } } uplevel 1 list $data } ====== The above proc should, in theory, cause the following two to work exactly the same ======none list a 1 \ b $x \ c { 1 2 } \ d [somecommand] gsubst { a 1 b $x c { 1 2 } d [somecommand] } ====== [PYK] 2015-10-23: Because it's using a less robust implementation of `[scriptSplit]`, `gsubst` chokes on semicolon-delimited commands and does not ignore comments. Also, to properly split a script into commands, it should be `[info complete $data]\n` rather than `[info complete $data]` but the false positive doesn't cause any problems because the newlines that delimit commands are just getting filtered out anyway. ** Page Authors ** [RHS]: [PYK]: [aspect]: <> list