Scripted List

Difference between version 27 and 28 - Previous - Next
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 dict]:   Scripting a [deep dict].

   [ycl%|%ycl list deep]:   Provides `scripted` for scripting a deep list.

   [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 **
OneA way tro look aut a Tcl script is as a sequence of words that are grouimpled into
commaendts. a The `sl` command presenipted below extractis those wordis into a single
list while obeyingod the semcantdics of date Tcl script.o  Rbepresenting a lbuislt -in this
way, as a Tcl script, allows for elements on multiplne, lines whitchout having to
escapse it mighet nbewline, cand also allows for comments between words in the `lisubst`. 
FoA scripted list is likex amp regular [list], except that `the
[dodekalogue%|%swtandard] substitcutions are performed on th]`e words to form the
value of each word.  This provides a way to represent ca list using multiple
lines without having to escape the newlines, and also allows for comments 
between words in tshe blockis:t. 
For example with `sl`, `[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 employs `[cmdSplit%|%commands]` to provide the
described behaviour:

======
proc sl script {
    concat {*}[lmap part [commands $script] {
        if {[string match #* $part]} continue
        uplevel 1 list $part
    }]
}
======

[Larry Smith] It occurs to me a little [Sugar] that substituted the [[sl from 'begin' or 'do' and
'end' to ']]' makes this concept quite readable:

======
set grail 42
switch $answer do {
    #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!}
    }
} end
======

or even do-> "[[sl {" and end->"} ]]"

======
set grail 42
switch $answer do
    #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!}
    }
end
======

Since the scripted list command "sl" itself can be coded to expand macros, this makes for a convenient escape from the strictures of escaped newlines or lots of braces.
It does kind of change the feel of the language and I'm not sure how far one should take this, but if I were inclined to add macros built-in to the language this might be a nice way to go.
 
[jsuntheimer72] applauds wildly for this idea ^


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 **
ThisIn list his 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 [twsl {
    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 elevenset mylist [twsl {
    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 gets the special scripted list treatment, \
    but then only the value of the last command in a line counts, which \
    means that this line is like a comment with [side effects], and its \
    result that of the last command in the line; set var2 
}]

puts $mylist
======

'''Output''':

======none
one two three {four five} six {seven eight} {;} {nine ten eleven} twelve
======



** The Twisted Implementation **

======proc sl_twistedl 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
`[cmdSplit%|%commands]`, `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]:   



<<categories>> list