Version 9 of Scripted List

Updated 2014-09-18 02:14:04 by pooryorick

A scripted list is a Tcl script in which every word 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.

Description

One way to look at a Tcl script is as a sequence of words that are separated into commands. It can be useful to represent a list this way, performing the normal substitutions on each word. A convenient way to accomplish this is by splitting the script into commands, passing the words of each command to list in order to get the desired substitutions, and then concatenating all results together. 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 possible.

This provides the oft-requested ability to represent a list in source code, using command and variable substitution to form the values in the list, without having to escape the newline between the values, and with the ability to make comments in between elements of the list and comment out some elements of 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
    }]
}

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:

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

Page Authors

PYK
aspect