Version 4 of safe.eval

Updated 2003-08-07 07:38:00

GPS: I'm really sick of the discussion over the expand vs. {} vs. {expand} and so on, so I came up with this. Comments are welcome...

 #Copyright 2003 George Peter Staplin
 #You may use this under the same terms as Tcl.
  proc safe.eval args {
  set expand 0
  set cmd [list]
  foreach arg $args {
   if {"~" == $arg} {
    set expand 1
   } else {
    if {$expand} {
     set cmd [concat $cmd $arg]
    } else {
     lappend cmd $arg
    }
    set expand 0
   }
  }
  uplevel 1 $cmd
 }

 proc A {a b c d e f g h} {
  puts "A:$a $b $c $d $e $f $g $h"
 }

 proc B {} {
  return [list 1 2 3]
 }

 proc main {} {
  set l [B]
  safe.eval A ~ $l "\"Hello \{\" World" ~ [list a b c] "Bye World"

  puts "Now testing with a typical Tk usage..."
  package require Tk
  pack [button .b]
  pack [button .b2]
  pack [button .b3]
  puts BEFORE:[winfo children .]
  safe.eval destroy ~ [winfo children .]
  puts AFTER:[winfo children .]
  exit
 }
 main

GPS: To help the reader understand this (after a comment in Tcl'ers chat). The ~ token is discarded in a safe.eval call. ~ is like the proposed ` except it must be preceded and followed by whitespace. Any list or string after a ~ token is expanded. Simple no mess solution to a simple problem. The character is easily changed if you like.


GPS: Here we have revision 3 that solves the problem of passing ~ to a procedure. I've also expanded the tests and they are easier to read.

 #Copyright 2003 George Peter Staplin
 #You may use this under the same terms as Tcl.
 #Thanks to RS and AM for comments via the Tcl'ers Chat.
 #rev 3
 proc safe.eval args {
  set expand 0
  set cmd [list]
  set lastArg ""
  foreach arg $args {
   if {"~" == $arg} {
    if {"~" == $lastArg} {
     #we have a double pattern which means ~ is an argument
     lappend cmd $arg
     set lastArg ""
     set expand 0
     continue
    }
    set expand 1
   } else {
    if {$expand} {
     set cmd [concat $cmd $arg]
    } else {
     lappend cmd $arg
    }
    set expand 0
   }
   set lastArg $arg
  }
  uplevel 1 $cmd
 } 

 proc A {a b c d e f g h} {
  puts "A:"
  foreach v [list a b c d e f g h] {
   puts "  $v: [set $v]"
  }
 }

 proc B {} {
  return [list 1 2 3]
 }

 proc C {a b c d e f g} {
  puts "C:"
  foreach v [list a b c d e f g] {
   puts "  $v: [set $v]"
  } 
 }

 proc main {} {
  set l [B]
  safe.eval A ~ $l "\[hello\]\"Hello \{\" World" ~ [list a b c] "Bye World"

  #We can double ~ to pass ~ as an argument.
  safe.eval C ~ $l ~ ~ ~ $l

  puts "Now testing with a typical Tk usage..."
  package require Tk
  pack [button .b]
  pack [button .b2]
  pack [button .b3]
  puts BEFORE:[winfo children .]
  safe.eval destroy ~ [winfo children .]
  puts AFTER:[winfo children .]
  exit
 }
 main