Version 20 of eval

Updated 2005-12-01 17:07:43 by escargo

Here's the inside scoop: eval is an old, old command, that's been in Tcl from the beginning. As with most things, there's a formal story (the "Unix man-page style" of description), and then there's a what-it-means-to-us level of narrative that is often understandable only to insiders. Immediately below is the standard documentation entry, followed by musings from experts.


http://purl.org/tcl/home/man/tcl8.4/TclCmd/eval.htm

NAME

eval - Evaluate a Tcl script

Synopsis

SYNOPSIS

 eval arg ?arg ...?  

DESCRIPTION

Eval takes one or more arguments, which together comprise a Tcl script containing one or more commands. Eval concatenates all its arguments in the same fashion as the concat command, passes the concatenated string to the Tcl interpreter recursively, and returns the result of that evaluation (or any error generated by it). (From: TclHelp)

Injection Attack

What's really going on with eval is this, at the coding level: formally, eval is about evaluation of scripts (although note that, in modern Tcl, it's merely an abbreviation for "uplevel 0 ..."). This makes one think of code-data duality, such languages as Lisp and Forth, "self-modifying programs", and related esoterica. Occasionally, eval is used in this way, and it's very powerful and crucial to Tcl that this be possible. All the commands--bind, everything with -command, and so on--that receive scripts as arguments depend on this.

However, most Tcl applications don't need eval directly in their own coding for this use at all. They're solving end-user problems, not diving into introspection. Still, eval is pervasive in Tcl coding for a slightly "dirty" reason: it's how we expand arguments.

... [invoke Joe English posting [L1 ], explain variadic parameters, style, ...] ...

That's the background necessary to understand the ruminations that follow.


Use of the eval command is considered "evil", dangerous or bad style by some Tcl'ers (because it leads to double substitution), but there are situations where its feature of removing one layer of list structure (like with concat) comes in just right. If you compose widgets in Tk, first you create them, then you manage them (register at a geometry manager, e.g. pack). You either have to keep track of what widgets you created, and repeat that list in the pack command, or just say

 eval pack [winfo children .]

eval is often used with exec, to flatten input lists:

 eval exec grep foo $filelist

because otherwise grep would receive filelist as one long filename with embedded blanks. Or, if you want to append one list's elements to another:

 set thislist [concat $thislist $thatlist] ;# can also be done as
 eval lappend thislist $thatlist

Another application is in building up a command in pieces (by appending to a string) and finally calling

 eval $cmd

eval concatenates its arguments in the same fashion as


[Document Donal's (controversial?) linsert trick for pure-list evaluation.] eval is an old, old command that's been in Tcl from the beginning.


The arguments to [eval] are concatenated into a string to be interpreted, but this operation does not guarantee that the string will be a well-formed script (i.e., one conforming to the Tcl parsing rules as laid out in the Tcl manual page). eval is useful when one wishes to generate a script and then interpret it. The following script breaks because the concatenation keeps the newlines from the list's string representation, making [eval] interpret the second element as a new command: if 1 ..., which may be byte-compiled, is an efficient

   % set arg {a
   b
   c
   }
   a
   b
   c
   % eval list $arg
   ambiguous command name "b": bgerror binary break

When using eval, it is very easy to leave holes which can be exploited to To solve this, construct the argument using list primitives like lappend, list, etc. DKF says: "list and eval are truly made for each other." eval can often be avoided, particularly with more modern recent versions of

   % eval [linsert $arg 0 list]
   a b c
`[linsert]` converts its list argument to a well-formed list with single
[linsert] converts its list argument to a well-formed list with single spaces separating elements, and then inserts further elements into it (if any of these elements contain newlines, they remain in the resulting string).
It's important to remember that `eval` works on '''strings''', not lists,
It's important to remember that [[eval]] works on '''strings''', not lists, and the rules for interpreting a string as a list are different than the rules for interpreting a string as a script.

----

In some cases [[eval]] does work on lists - and its special.  In particular, if eval is passed a [pure list] then
it gets evaluated directly, without another round of substitution.  However, I think the utility is just short of what it ''could'' be.
** Verbose Evaluation **
When you pass eval a pure list, you can only execute a single command.  What if we were to pass eval a list of pure lists - it should directly evaluate each of them, and return the value of the the last one evaluated.
This sounds a lot like progn in [lisp], and it seems like it would allow for some nifty bits of [introspection] - 
for example, if [info body] returned a pure list-of-lists rather than a string, that could be evaluated directly.  Or modified.  The whole program becomes a list of lists.

The problem is how to signal to eval (or uplevel, namespace, bind, ...) that it is a list of lists rather than just a list.  Could eval determine if its input is a pure list and the first argument of that is a pure list, then evaluate as progn?

Another place this seems like it could be useful: I have a pkgIndex file with the line
 package ifneeded tls 1.4 "[list load [file join $dir libtls1.4.so]];[list source [file join $dir tls.tcl]]"
where most of the lines are like
 package ifneeded tclperl 2.3 [list load [file join $dir tclperl.so]]
since they only need one command; but the tls line needs two commands.  So why can't it be
 package ifneeded tls 1.4 [list [list load [file join $dir libtls1.4.so]] [list source [file join $dir tls.tcl]]]
----
[RS] 2004-02-06 - As usual in Tcl, functionality you miss you can easy roll yourself. I needed a progn-like list eval in [RPN again], and did it similar to this:
 proc leval args {
    foreach arg $args {
       set res [uplevel 1 $arg]
    }
    set res ;# return last ("n-th") result, as Tcl evaluation does
 }

----
[[Explain discussion of {expand} for argument-interpolation, ...]]

For example, if previously I coded this:

 set f [glob -nocomplain *.tcl]
 # some kind of code that ensures that $f has something in it
 eval exec lp $f

what would I code now?
 exec lp {expand}$f

----
[LV] I just had to copy this over to the wiki - it is so neat!


It sounds like you're looking for something similar to /bin/sh "set -v" command

It sounds like you're looking for something similar to /bin/sh "set -v"
command which prints shell input lines as they are read, and "set -x" which
prints expanded commands.  Kind of like a verbose mode.
Nope.  Nothing like that.  But you wouldn't have to write your own shell.  You
Nope.  Nothing like that.  But you wouldn't have to write your own shell.
You could walk through a script (a list of lines), and use [info complete]
to decide how many lines are required to complete the command, then print
both the command and the command's results.  This seems to work.
  proc verbose_eval {script} {
      set cmd ""
      foreach line [split $script \n] {
   if {$line eq ""} {continue}
          append cmd $line\n
          if { [info complete $cmd] } {
              puts -nonewline $cmd
              puts -nonewline [uplevel 1 $cmd]
              set cmd ""
          }
      }
  }

set script {

  set script {
      puts hello
      expr 2.2*3
  }

verbose_eval $script

  verbose_eval $script 

LV: This proc is so slick! I love it!


LV This proc is so slick! I love it! Lars H: Another approach to this (which also can see individual commands in AR Here is my proposal with traces. Rem: the "noop" string is some kind of magic cookie.


Tcl syntax help - Arts and crafts of Tcl-Tk programming - Category Command