Updated 2011-06-12 06:10:29 by RLE

ulis: If, for some syntax analysis, you need to know the level of nested braces inside a list, here is a tiny proc that do that.

It returns 0 for "this is a list", 1 for "{this is a list}" or "this {is a} list", and so on.
  proc listlevel {text} { 
     set len [llength [split $text]]
     for {set count 0} {$len!=[set len [llength $text]]} {incr count} {
        # Used to be [eval concat $text] but this is safer
        set text [eval [linsert $text 0 concat]]
     }
     return $count
  }

ulis: This proc doesn't get the good result because [llength $text] can't differentiate between {{this is a list}} and {{{this is a list}}} (each returns 1). Applied to the test below it returns: 0 0 0 2 2 3 1. (Expected results are: 0 0 0 1 1 2 2).

ulis: Here is a proc that gets the expected results from the test below. (Maybe somebody will find a flaw?)
  proc listlevel {list} \
  {
    # initial length & count
    set len [string length $list]
    set count 0
    # while string length differs between list & concatened list
    while {[string length [set list [eval concat $list]]] != $len} \
    { 
      # update length & count
      set len [string length $list]
      incr count 
    }
    return $count
  }

Can somebody explain me why [eval [linsert $list 0 concat] ] is better than [eval concat $list] in this context?

AK -- eval is given a pure list and goes through a specially optimized path for the eval/linsert combo. Without the linsert 'concat' is no pure list and thus eval does the regular stuff, which is slower.

Results:
  set text "this is a list"
  puts "$text: [listlevel $text]"
  -> this is a list: 0

  set text {this is a list}
  puts "$text: [listlevel $text]"
  -> this is a list: 0

  set text [list this is a list]
  puts "$text: [listlevel $text]"
  -> this is a list: 0

  set text [list "this is a list"]
  puts "$text: [listlevel $text]"
  -> {this is a list}: 1

  set text {{this is a} list}
  puts "$text: [listlevel $text]"
  -> {this is a} list: 1

  set text {{this {is a}} list}
  puts "$text: [listlevel $text]"
  -> {this {is a}} list: 2

  set text {{{this is a list}}}
  puts "$text: [listlevel $text]"
  -> {{this is a list}}: 2

ulis: Tcl makes no difference between "this is a list" and [list this is a list] so I gave them the 0 level.

You can need to differentiate empty strings, one word strings and multi words strings before nested lists.

Slight problem. It doesn't like list encapsulated single element lists. Nil and multi-element lists work:
 % listlevel [list a]
 0
 % listlevel [list {a}]
 0
 % listlevel [list {}]
 1
 % listlevel [list {a b}]
 1
 %

[wcg] - 2009-08-21 22:36:50

This can easily be fooled - make a list with leading or trailing spaces and it'll miscount the eval concat result You'll need to trimleft and trimright of the original result of all spaces for the string lengths to line up correctly.