Re: format statement: Expanding a string



On Thu, 2 Mar 2006, Andreas Leitgeb wrote:

So what one really needs in this situation is a procedure or command that does the following for a given string:
1.) try to treat it as list: if it works out,
return [lrange $str 0 end].
2.) if any non-blank prefix of the string is acceptable as
a list, find a maximal such prefix, convert that to a
list, and apply "3.)" on the rest.
3.) apply something like [split] on the rest, or treat the
rest as one item.

The second of that is non-trivial on tcl-level, especially when taking performance into account.

Trivial it is, performing well is out of the question :)

I'm not proposing any new core-features (at least not on this topic), but if someone wrote a "reasonable list parser", it might be a valueable gem on wiki or tcllib.

This is just something I cooked together while waiting for Married.. with Children to begin. Now it begins, so I'll post this now. Bye.

<some code below>

package require Tcl 8.5; # lassign

# REAsonable LiSt PArser
# not REAsonable List pARSEr

proc realspa {victim} {
# so, if it is a list, then we'll return it
if {![catch {llength $victim}]} {return [lrange $victim 0 end]}
# so, it isn't a list. fine by me
# there shall be no empty units in either end
set victim [string trim $victim]
# then we need something to cut this at
set cil [regexp -all -indices -inline {\s+} $victim]
# there are basically two ways to find out where a string is no more a list
# 1) add stuff from the beginning until it breaks
# 2) take stuff from the end until it doesn't break
# 1 is the easy way out, but it'll break in my example too early
# so case 2 it'll be
# indices need to be reversed (courtesy of KPV)
set st -1
set en [llength $cil]
foreach tm $cil {
if {[incr st] >= [incr en -1]} {break}
lset cil $st [lindex $cil $en]
lset cil $en $tm
# http://wiki.tcl.tk/43 has following line too much
# incr st
}
# puts "cil=$cil"
foreach sep $cil {
lassign $sep s e
# puts "s=$s, e=$e, r=[string range $victim 0 $s-1]"
if {![catch {llength [string range $victim 0 $s-1]}]} {break}
}
# at this point we should know where it broke, and the rest is final cell
set lst [lrange [string range $victim 0 $s-1] 0 end]
lappend lst [string range $victim $e+1 end]
}

set t "this is one {malfor matted} {list"

puts "trying out >>$t\ninvalid list? [expr {[catch {llength $t}]?yes:no}]"
set l [realspa $t]
puts "recovered (or used) [llength $l] cells"
foreach i $l {puts "->$i<<<"}

<no more code here>

--
-Kaitzschu
s="TCL ";while true;do echo -en "\r$s";s=${s:1:${#s}}${s:0:1};sleep .1;done
.