fightliteracy has asked for the wisdom of the Perl Monks concerning the following question:

Briefly,

I am using regular expressions to first get a block of code from a string, then I want to use an eval to evaluate and then replace what evaluate would return in the $@ variable

example

text <# { if (a == b) { print "a=b"; } } #> and more stuff <# { print 4*3; } #> and more stuff and so on....

and i want to replace the code blocks with what the evaluate would get from them

so far i have
$data =~ s/<# (\{.*?\}) #>/eval($1)/eg;


which will evaluate the code, but only replace a 1 where things should go, because i can't get a $@ in there.

Incidentally, it will prematurely write headers when i do it this way to a webpage.

is it possible to do this... to make it harder, there are multiple instances of the blocks of code. i think this is pretty much what HTML Mason does, but i am no perl god. ---- FightLiteracy http://www.fightliteracy.com

Replies are listed 'Best First'.
Re: Evaluate in a substitute
by Abigail-II (Bishop) on Oct 17, 2003 at 09:30 UTC
    If the eval succeeds, that is, no compilation errors, and no fatal errors or dies, $@ will be *empty*. Furthermore, what you print in an eval, you can't capture easily. By default, it'll go to stdout. What you probably want is something like:
    #!/usr/bin/perl use strict; use warnings; while (<DATA>) { s/<# \{(.*?)\} #>/$1/eeg; print; } __DATA__ <# { $a = 3; $b = 3; if ($a == $b) { "a=b"; } } #> <# { 4*3; } #>
    Which prints:
    a=b 12

    Note the differences:

    • In the pattern, the parens are inside the braces.
    • The replacement part is just $1.
    • There are two /e modifiers.
    • The data doesn't have prints.

    Abigail

Re: Evaluate in a substitute
by liz (Monsignor) on Oct 17, 2003 at 09:48 UTC
    Although the "eeg" trick of Abigail-II is nice, I don't think it gives you enough flexibility. I would use a sub, like so:

    sub process { my $src = shift; # possibly do some pre-processing on $src here my $value = eval $src; # possibly extra external logging if something went wrong return $@ ? "<B>Error in '$src': $@</B>" : $value; }

    and then have:

    $data =~ s/<# (\{.*?\}) #>/process($1)/eg;

    Liz

Re: Evaluate in a substitute
by nashdj (Friar) on Oct 17, 2003 at 11:12 UTC
    IO::String is useful for this kind of task.
    use IO::String; ... $data =~ s/<# (\{.*?\}) #>/evalCap($1)/eg; sub evalCap { my $code = shift; my $str; my $str_fh = IO::String->new($str); my $old_fh = select $str_fh; eval $code; select $old_fh; $str; }
    Update:
    The regular expression can be simplified: s/<# {(.*?)} #>/....
    As Abigail implies, the braces aren't needed in the eval. Nor in this case do they need to be escaped.

    - nashdj

Re: Evaluate in a substitute
by Cody Pendant (Prior) on Oct 21, 2003 at 07:14 UTC
    I just want to say "security problem". The implications are a bit scary, aren't they?


    ($_='kkvvttuubbooppuuiiffssqqffssmmiibbddllffss') =~y~b-v~a-z~s; print