Chris Heithoff
Chris Heithoff

Reputation: 1863

Is there a simple way to parse a line of Tcl into its command and its arguments (not just splitting by whitespace)

Suppose I have a string which is also a Tcl command.

set line {lsort -unique [list a b c a]}

How can I convert this string into a list equivalent to this?

  {
      {lsort}
      {-unique}
      {[list a b c a]}
  }

Because of whitespace inside the square brackets, I can't just use lindex. For example:

> lindex $line 2
   -->  [list

The reason I'm asking is because I have a large Tcl script that I want to parse and re-write. I would like certain lines in the re-written script to have swapped argument order or some numerical arguments scaled by a factor.

I know I could parse the string character by character, keeping track of {}, [], and " characters, but this feels like re-inventing something that might already exist. I've been looking at the info and interp commands but couldn't find anything there.

Upvotes: 0

Views: 261

Answers (1)

Chris Heithoff
Chris Heithoff

Reputation: 1863

I used info complete successfully in this proc.

proc command_to_list {command} {
    # split by whitespace
    set words  [regexp -all -inline {\S+} $command]
    set spaces [regexp -all -inline {\s+} $command]

    set output_list [list]
    set buffer ""

    foreach word $words space $spaces {
        append buffer $word 

        if {[info complete $buffer]} {
            lappend output_list $buffer
            set buffer ""
        } else {
            append buffer $space 
        }
    }
    return $output_list
}

This proc will group whitespace separated 'words' until they have no unmatched curlies, double quotes, or square brackets. Whitespace is preserved inside of matching pairs of curlies, double quotes or square brackets.

> set command {foreach {k v} [list k1 v1 k2 v2] {puts "$k    $v"}}
> foreach word [command_to_list $command] {puts $word}
foreach
{k v}
[list k1 v1 k2 v2]
{puts "$k    $v"}

Upvotes: 1

Related Questions