Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Please help me understand string eval better

by perltux (Monk)
on Jun 13, 2017 at 01:43 UTC ( [id://1192639] : perlquestion . print w/replies, xml ) Need Help??

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

I'm a bit confused about eval and what gets done at run-time and what at compile time, therefore I need to understand the following:

What is the difference (if there is any) between the following three string eval usages:

eval "$$var$transf"

eval $$var.$transf

eval '$$var'.$transf

Thanks very much in advance!

Replies are listed 'Best First'.
Re: Please help me understand string eval better
by AnomalousMonk (Archbishop) on Jun 13, 2017 at 02:32 UTC
        eval "$$var$transf"

        eval $$var.$transf

    These two expresions are essentially the same.  $$var.$transf is simply an explicit string concatenation that would be done implicitly in a  "$$var$transf" double-quotish interpolation. (Update: The concatenation/interpolation is done at run time, and then the resulting string is eval-ed.)

        eval '$$var'.$transf

    This is a bit different. A literal string  '$$var' (the dollar signs are part of the literal string) is explicitly concatenated with the stringization of whatever is in the  $transf scalar. (Update: Again, the concatenation is done at run time and the resulting string is eval-ed.)

    Try experimenting with these string expressions — without the eval part until you're sure what's going on!

    Update 1: Note that  $$var is the dereference of a reference to a scalar:  $var holds a reference to some other scalar.

    Update 2: Some examples:

    c:\@Work\Perl\monks>perl -wMstrict -le "my $foo = 'rm '; my $var = \$foo; my $transf = '-R *'; ;; print qq{A: >$$var$transf<}; print 'B: >', $$var.$transf, '<'; print 'C: >', '$$var'.$transf, '<'; " A: >rm -R *< B: >rm -R *< C: >$$var-R *<
    Once again, experiment — it's fun and easy!

    Update 3:

    ... what gets done ... at compile time ...
    That's the  eval BLOCK form. It's compiled at... well, compile time, and must be syntactically correct. (The  eval EXPR form need not be.) Please see the docs for more info, get back to us with any specific questions.


    Give a man a fish:  <%-{-{-{-<

      You say '$$var' is a literal but in my experiments the value stored in $$var is actually being used during eval so it's not a literal string at run-time when eval runs it.
      That's why I'm wondering what differences there are between option 1 and option 3.

      I got to this because $$var can contain a number with a leading zero, which with option 1 gets interpreted as octal by eval while with option 3 the leading zero is ignored, so I know these options aren't the same, I just don't know what other effects using option 3 instead of option 1 can have.

        That's why I'm wondering if between option 1 and option 3 there is a difference.

        use strict; use warnings; my $foo='$ans='; my $var=\$foo; my $transf='5;' ; my $r5='$ans="hithere";'; my $var5=\$r5; testit("$$var$transf"); testit("${$var}$transf"); testit($$var.$transf); testit('$$var'.$transf); sub testit { my $ans; my $string=shift; $ans='???'; my $eret=eval $string; printf 'String:%-20s',$string; printf ' $ans:%-10s',$ans; printf ' $eret:%-10s',$eret; print "\n"; } # testit
        Result
        String:$ans=5; $ans:5 $eret:5 String:$ans=5; $ans:5 $eret:5 String:$ans=5; $ans:5 $eret:5 String:$$var5; $ans:??? $eret:$ans="hithere";
        Yea i think there is a difference.

        And i dont think you would be talking about "run time" if you understood the multiple compile and run times involved. First your script containing the eval is compiled, and then run. At the this "run time", the '$$var' is just a literal, two dollar signs and then 3 letters. when this run time reaches an eval command it then "restarts?" the perl compiler, to compile whatever string is handed to it, this is when the literal '$$var' is considered code. And then there is a run time for that compiled eval string. It runs that compiled code and returns the "last thing".

Re: Please help me understand string eval better
by haukex (Archbishop) on Jun 13, 2017 at 08:37 UTC

    In terms of the behavior of how Perl parses and runs the code, evaling a string is essentially the same as putting that string in a file and having perl run it. So, except for the scoping and character encoding issues explained in eval, it all comes down to what the string contains. So I think the key to understanding this question, as well as debugging many eval issues, is to print1 the strings that you will be passing to eval.

    my $foo; my $bar = '043'; my $s1 = '$foo = $bar'; print "s1: ", $s1, "\n"; print "eval=", eval($s1)//$@, ", \$foo=$foo\n"; my $s2 = "\$foo = $bar"; # same as '$foo = '.$bar print "s2: ", $s2, "\n"; print "eval=", eval($s2)//$@, ", \$foo=$foo\n"; __END__ s1: $foo = $bar eval=043, $foo=043 s2: $foo = 043 eval=35, $foo=35

    So you see that in the first case (s1), the piece of Perl code that is being executed is $foo = $bar, a simple assignment of one variable to another, regardless of what they contain. However, in the second case (s2), the variable $bar has been interpolated into the string that eval will execute, and the piece of Perl code that is executed is $foo = 043. As you may know, 043 in Perl source code is an octal literal representing the number 35, so that is how it is interpreted here. This is different from the string "043", which does not get converted automatically, but more on this below.

    Whenever there is a use of eval, possible security implications must be considered. Where are you getting your variables from? If any of this is from user input, note that you may be opening yourself to possible security holes. When in doubt, avoid eval. <update> Just one example from a quick search: Math::Expression::Evaluator. </update>

    Elsewhere in this thread you say that your variables may or may not contain octal values. One trick to convert a string variable containing octal, hex or binary notation into a Perl numeric value (which doesn't happen automatically) is the following, so perhaps you won't have to eval at all. See oct.

    use Data::Dump; my $val = "034"; $val = oct $val if $val=~/^0/; dd $val; # 28 # or, for a list of values: my @vals = qw/ 10 20 0b1101 0xF 023 /; /^0/ and $_=oct for @vals; dd @vals; # (10, 20, 13, 15, 19)

    1 Footnote: I have explicitly recommended print here instead of the usual Data::Dumper or Data::Dump because I think that in the case of eval, the extra quoting and escaping these modules do would confuse the issue more. However, when in doubt, i.e. if there might be whitespace or other special character issues hidden by a regular print, it's best to additionally use one of the aforementioned modules to look at the variable.

Re: Please help me understand string eval better
by BillKSmith (Monsignor) on Jun 13, 2017 at 14:26 UTC
    Your main problem is that perl does not consider '09' to be a number. The function 'eval' will fail no matter what you do. Why not explicitly remove the leading zero?
    use strict; use warnings; use Scalar::Util qw(looks_like_number isdual); my $transf = '/3+6'; my $constant = '09'; my $var = \$constant; #if (looks_like_number($$var)) { if (!looks_like_number($$var)) { #correction per [haukex] print "$$var is not a number\n"; } else { print "$$var looks like a number\n"; } if (!isdual($$var)) { print "$$var does not have a hidden numeric value\n"; } $$var =~ s/^0*//; # make it into a valid number my$expr = "$$var$transf"; my $ans = eval "$$var$transf"; print "$expr yields: "; if (!$@) { print $ans, "\n"; } else { print $@, "\n"; }
    Bill
      perl does not consider '09' to be a number

      Perl does consider the string "09" to be a number, 0 + "09" == 9. The only question is, does the OP want this to be interpreted as an octal, which in this case would be invalid, or not.

      $ perl -wMstrict -le 'print 77' 77 $ perl -wMstrict -le 'print 077 ' 63 $ perl -wMstrict -le 'print 0 + "077"' 77 $ perl -wMstrict -le 'print oct "077"' 63 $ perl -wMstrict -le 'print eval " 77"' 77 $ perl -wMstrict -le 'print eval "077"' 63
      if (looks_like_number($$var)) { print "$$var is not a number\n";

      You've got mistake in your logic there. Also note that looks_like_number tells you whether the string will properly auto-convert to a number (0+$str), but does not cover all of the numeric literals that Perl accepts in its source code, i.e. what eval will parse, such as 0b1001 or 1234_56; strings like "0b1001" won't automagically convert to numbers. I wrote about this a bit more at Re^3: why are hex values not numbers?

Re: Please help me understand string eval better
by Anonymous Monk on Jun 13, 2017 at 07:12 UTC

    Its the same as if you wrote 3 different files and used perl to run each file, exactly as if you wrote this

    use Path::Tiny qw/ path /; path( 'first')->spew( "$$var$transf" ); do 'first'; path( 'second')->spew( $$var.$transf ); do 'second'; path( 'third')->spew( '$$var'.$transf ); do 'third';

    If your purpose is calculator, see dont use eval for calculator , and see safest undumper

      exactly as if you wrote this

      I get the point you're trying to make and I made a very similar one in my post, but note that this part isn't accurate, since do is different from eval in that code run by do can't see lexicals in the enclosing scope. Specifically:

      use warnings; use strict; use Path::Tiny 'path'; my $var = \"09"; my $transf = "/3+6"; print eval('$$var'.$transf),"\n"; # prints 9 path('third')->spew('$$var'.$transf); print do('third'),"\n"; # prints 6

      Because in the file third, neither warnings or strict are in effect and it can't see my $var, it's the same as undef()/3+6. (Of course if you s/my/our/g, it does work.)

A reply falls below the community's threshold of quality. You may see it by logging in.