Why this perl code about eval not work

Chunhua Du
Chunhua Du used Ask the Experts™
on
Code is here:

my $a1 = ‘a1_v’;
my $c1;
my $varName;

my $code = qq(
      \$varName = ‘a1’;
      eval(‘\$c1 = \$\$varName;’);
);

eval($code);

# I hope this line print a1_v, but print nothing
print “$c1\n”;
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
You have a few escapes wrong.  This works...
use strict;
use warnings;
my $a1 = 'a1_v';
my $c1;
my $varName;

my $code = qq(
      \$varName = 'a1';
      eval("\\\$c1 = \\\$\$varName;");
);

eval($code);

print "$c1\n";

Open in new window

Author

Commented:
could I understand in this way: every layer of eval function add in code string, add \\ to begin of $ in parameter of added eval function?

thanks
Yes.  Generally, for every level of eval, add \ per level (which means \\ for beyond the first).

Anytime you have an eval that doesn't work, the first thing to check on is the escapes and quotes and try debugging if they are what you want for the level of eval nesting you are using.
Exploring ASP.NET Core: Fundamentals

Learn to build web apps and services, IoT apps, and mobile backends by covering the fundamentals of ASP.NET Core and  exploring the core foundations for app libraries.

Author

Commented:
In the nesting eval function, if I want to use single quote on the parameter of eval, how should I code

thanks
I don't think you can in this case (not positive) as you have variables at different nesting.  Why do you want to use single quotes?

Let me start even more basic - what are you trying to do with the nested evals?  You can probably do something else that would be simpler and much more efficient.

Author

Commented:
I want to know why this works:

my $code = qq(
   \$str =~ m"$pattern";
   for(\$xx = 1; \$xx <=2; $xx++)
   {
      eval('\$backRef = \$\$xx;');
      push(\@\$backRefArr, \$backRef);
   }
);

eval($code);

print "@$backRRefArr";
It works because it is a single level of eval (not nested) so all variables are at the same level of escape (no double-escapes necessary).  The only exception is $pattern which is defined outside of $code.  I don't understand why $xx++ is not escaped (is that a typo?).

To step through the steps:
  • When $code is defined, $pattern is evaluated so it becomes $str =~ m"somestring";.
  • When $code is defined, the eval becomes eval('$backRef = $$xx;').
  • When the first eval is run, it does pretty much as expected.
  • When the second eval is run, it does $backRef = $1 and $backRef = $2 over the loop

Author

Commented:
In my last code, variable $refBack and @refBackArr are defined out of $code

$xx++ in my last post is mistake, in my code it is \$xx++, it is my mistake sorry for this
nociSoftware Engineer
Distinguished Expert 2018

Commented:
not need to add a \  you need to escape everythin double

so \ -> \\   $ needs \$  etc.      $var -> \$var
Each needs to be escaped again...:   \$var -> \\\$var
Next level:                                              \\\$var -> \\\\\\\$var
etc.

Author

Commented:
Thanks, I would try it

Author

Commented:
Do you mean \\\$->\\+\\+\\\$

Author

Commented:
How can I understand 'double', I am not sure double what
Software Engineer
Distinguished Expert 2018
Commented:
All escapes need to be done a 2nd time...

a \ escaped = \\
a $ escaped = \$
a " escaped = \"

so $val is intended  for eval $ needs escape see below a working example:

#!/usr/bin/perl

$val = "xxxx\n";
print $val;
                 eval (   "print \$val");
        eval (  "eval (  \"print \\\$val\") " );
eval ( "eval ( \"eval (\\\"print \\\\\\\$val\\\") \" )" );

Open in new window


This should print 4 lines with xxxx
To rephrase what noci (and I) are saying...

For each nesting of eval that something should be evaluated at, you need to add an escape.  qq(...) is equivalent to double-quoting.

Given the example:
my $code = qq(
   \$str =~ m"$pattern";
   for(\$xx = 1; \$xx <=2; $xx++)
   {
      eval('\$backRef = \$\$xx;');
      push(\@\$backRefArr, \$backRef);
   }
);

eval($code);

print "@$backRRefArr";

Open in new window


$pattern will be evaluated when $code is defined
everything with \x becomes x when $code is defined
everything that had \x is evaluated when the first eval occurs (except the things in the second eval because they are enclosed in single quotes)
the single quoted string is then eval'd by the second eval
For your second example (quoted above), I'm curious why you need eval at all (and especially nested eval).  Unless you are doing something non-obvious from your example snippet, this code should work fine:
$str =~ m"$pattern";
{ no strict 'refs';
   for(my $xx = 1; $xx <=2; $xx++) {
      my $backRef = $$xx;
      push(@$backRefArr, $backRef);
   }
}

print "@$backRefArr\n";

Open in new window

Author

Commented:
Why this not work:

my $a1 = 'a1_v';
my $varName;

my $code = qq(
      \$varName = 'a1';
      print \$\$varName;
);

eval($code);

# This do not work, either
$varName = 'a1';
print "$$varName"
nociSoftware Engineer
Distinguished Expert 2018

Commented:
Why should it:
I translated your $code to the equivalent $code2...
my $a1 = 'a1_v';
my $varName;

my $code = qq(
      $varName = 'a1';
      print \$\$varName;
);
my $code2 = "
    $varName = 'a1';
    print \$\$varName;
    ";

print "code=$code\n";
print "code2=$code2\n";
#(That should explain the first part... qq is realy the same as "..., if you have an ebcdic system qq( ) is simpler than just "...

# This do not work, either
$varName = 'a1';
print "\$$varName"
#This does what you told it to do print $ and the contents of $varName:   result: $a1    (without line feed)...

Open in new window



You may meant it to do:

$varName = 'a1';
eval( "print \$$varName; " );
print "\n";
eval( "print \"\${$varName}\"; " );
print "\n";
eval( "\$x = \"\${$varName}\"; print \$x;" );
print "\n";

Open in new window


again look at the intermediate results:
$varName = 'a1';
print( "print \$$varName; " );
print "\n";
print( "print \"\${$varName}\"; " );
print "\n";
print( "\$x = \"\${$varName}\"; print \$x;" );
print "\n";

Open in new window


Which requires proper quoting, including the " getting quoted, if needed.

Author

Commented:
In my code:

print $$varName;

at this line, I hope to get value of $a1, why getting nothing? Must it use eval?

Author

Commented:
By my understanding, if I write:

$$varName

in level N of eval, $varName must be defined and get value in level N-1

Author

Commented:
I mean that get value of $a1 by $$varName, $varName has string value 'a1'.

For example: $a1 has value 'v1', I want $$varName return value 'v1'. I am not sure whether it must use eval here
nociSoftware Engineer
Distinguished Expert 2018
Commented:
$varName has value a1......
The parse has no concept of recursive resolution of variables.
For that you need eval, and eval requires to receive a syntactically correct "perl program"....
hence you need to escapd special characters to prevent replacement by the "higher level " parser that needs to construct the string for eval.

Perl has a very simplistic engine.. It Parses (reads, compiles)  the program ONCE and then executes it...., so the First reading should produce a string that can then be "evaluated" again for Parsing & Execution....

Author

Commented:
But why this works:

$str = m{$pattern};
for($xx = 1; $xx <= 2; $xx++)
{
      print $$xx;
}
nociSoftware Engineer
Distinguished Expert 2018

Commented:
They work at printing nothing?.... as there is quite some missing context. Syntax is correct though.
Anyway what is $pattern...
The pattern isn't matched to anything using =~ ....

Please try the code fragments you wish us to evaluate First as such and describe what you get and what you expect...
Doing something like this:
my $a1 = 'v_1';
my $varName = 'a1';
{ # begin closure
no strict 'refs';
print $$varName, "\n";
} # end closure

Open in new window

does not require an eval.  That is a symbolic ref and works fine.

You are doing "use strict; use warnings;" in your code, right?  If not, you ALWAYS should be as it will catch a lot of errors (and I assume you are which is why I put "no strict 'refs'" since strict disallows symbolic references and you should only allow them where expected).

Author

Commented:
Full code in my last post:

my $s1 = 'xxa1xxb2xx';
$s1 =~ m{(a\d+).*?(b\d+)};

for($xx = 1; $xx <= 2; $xx++)
{
      # why this line get value 'a1' and 'b2' without eval
      print "$$xx";
      print "\n";
}

Author

Commented:
I have try your code with:

no strict 'refs';

I get empty output.
Your code works fine because it uses symbolic refs.  $$xx evaluates to $1 and then $2.

Apparently I shouldn't do symbolic refs or evals from memory (my code doesn't work as posted and I don't see why).  I'll double-check it later and report a snippet that works...
There appears to be some very weird interaction between "my" and symbolic references (in short, they don't work).  The below code is what I used to get both example sym refs working...
use strict;
use warnings;
my $pattern = '(whatever).*(this)';
my $str = 'this is whatever this is';
my $backRefArr = [];
$str =~ m"$pattern";
{ no strict 'refs';
   for(my $xx = 1; $xx <=2; $xx++) {
      my $backRef = $$xx;
      push(@$backRefArr, $backRef);
   }
}

print "@$backRefArr\n";

our $a1 = 'v1';
my $varName = "a1";
{
    no strict 'refs';
    print $varName, "\t", $a1, "\t", $$varName, "\n";
}

Open in new window


Note the "our" definition for $a1 (instead of "my").

Author

Commented:
Thanks to all your help these days. I have understood and got ways to do on those issues by your posts.

I think it is time to stop.

Thanks everyone who help.

Happy New Year!

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial