One person’s data is another person’s program.
Years ago, a friend of mine showed me an elegant program running on a
tiny 32K machine, the BBC Micro, that accepted any mathematical
expression such as sin(x)
+
cos
(x**2)
and graphed it.
Fresh from a study of parsers, I’d wondered how many hundreds
of lines it took him to write it. He showed me the code; the entire
program fit on the small screen. He had used the
eval
statement provided by BASIC.
Most self-respecting scripting languages such as BASIC (some versions, anyway), Perl, Tcl, LISP, and Python have a feature that clearly sets them apart from systems programming languages like C: the ability to treat character strings as little programs.[22]
For me, Perl’s
run-time
evaluation capability is one of the biggest reasons for using the
language. (The other is its terrific support for regular
expressions.) I use run-time evaluation for creating little snippets
of code on the fly, which then execute at typical Perl speeds (i.e.,
fast!), for writing sophisticated interpreters for little
languages.[23] The eval
function is the gateway to
this power. We will use this feature in Chapter 7,
for creating object accessor functions, and in Chapter 11, for building an SQL query evaluator, among
other things.
As it turns out, Perl’s eval
function works
in two somewhat distinct ways, depending on the type of its argument.
If given a string, eval
treats the string as a little program and compiles and executes it
(as mentioned above); this is called dynamic expression evaluation.
The contents of the string may or may not be known at compile time.
Alternatively, if given a block of code —
that is, the code is known at compile time — eval
traps run-time exceptions.
Dynamic expression evaluation and exception handling are very
different topics and one would expect them to be performed by
different keywords. Larry Wall once mentioned that he had toyed with
the idea of using a different keyword, try
, for
the exception-handling version, but he was into keyword conservation
at that point. I find that a single keyword actually works well
because expressions evaluated on the fly have a greater chance of
generating run-time exceptions as code known at compile-time.
In this chapter, you will gain an in-depth understanding of how the
two forms of eval
work and add an important
dimension to your toolkit of idioms.
When Perl is given a file to execute
or a string as a command line
option (using -e), it needs to parse the
contents, check it for syntax errors, and, if all is fine, execute
it. Perl makes this feature available to the programmer through the
eval
string form. This
contrasts powerfully with languages such as C, C++, or Java, where
the compiler itself is a separate beast from your program, not
available to it at run-time. In other words, the Perl interpreter
itself works somewhat like this:
# Slurp in the entire file while ($line = <>) { $str .= $line; # Accumulate the entire file. } # $str now contains the entire file. Execute it ! eval $str;
As you can see, eval
handles any Perl script
handed to it. The beauty of this thing is that this facility is
available not just to Larry, but to mortals like you and me. Try
this:
# put some code inside $str $str = '$c = $a + $b'; # Perl doesn't care what's inside $str $a = 10; $b = 20; eval $str; # Treat $str as code, and execute it. print $c; # prints 30
In this snippet, $str
is treated as an ordinary
string at first, because that is what it is. But
eval
thinks of it as a program and executes it.
The important point is that it doesn’t think of it as a
separate program, but as if it belonged right
there in the original code instead of the
eval
statement, as shown in Figure 5.1.
For this reason, the string that is given to eval
can use
variables and subroutines available to it
at that point, including my
and
local
variables, and optionally produce new ones
in the same environment. In the preceding example, the string given
to eval
adds two initialized variables
($a
and $b
) and produces a new
variable, $c
.
If you have more than one statement inside the string (remember that
the string can be as big a program as you want),
eval
evaluates all of them and returns the result
of the last evaluation:
$str = '$a++; $a + $b'; # Contains two expressions $a = 10; $b = 20; $c = eval $str; # $c gets 31 (result of the 2nd expression, $a+$b)
Of course, it’s quite pointless to eval
a
piece of code that you know at compile time, as in the example above.
Things get interesting if $str
comes from
elsewhere—standard input, a file, or over the network. We will
shortly look at some examples that make use of this.
The
string form of eval
is a security risk. If the
string argument comes from an untrusted source and contains, say,
system(
'rm
*
')
the code would be merrily executed—and result in a distinct
lack of merriment on your part. In situations in which you cannot
trust input, you can use the taint-checking option provided by Perl,
which prevents you from using data derived from outside the program
to affect files or things outside the program [Section 5.8]. You can also use the Safe module bundled
with the Perl distribution, which provides safe compartments in which
to eval
strings, similar to the environment that a
web browser provides for Java or Tcl/Tk applets.
What if $str
doesn’t contain a valid Perl
expression? Perl then puts an error message in a special variable
called $@
(or
$EVAL_ERROR
, if you use the English module). Since
eval
compiles the string before actually executing
it, this can be either a compilation or a run-time error.
$@
is guaranteed to be undef
if
$str
contains error-free code (well, I should say
free of syntax errors, because it can’t really protect you
against flawed logic).
Since eval
is used by the Perl interpreter itself
to parse and execute a given script, the error strings (in
$@
) are exactly those you see on the standard
error output when processing a flawed
script.
There is one subtle, yet important, point that needs to be mentioned.
eval
treats the string as a block, which is why it
is able to process a number of statements (not just expressions) and
return the value of the last statement. This also means that you
don’t see the changes to localized or lexical variables present
in the eval
‘ed string.
[22] On a related note, see the section Section 2.6 in Section 2.6 for other Perl constructs that set Perl apart from systems programming languages.