Why this perl code about eval not work

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”;
Chunhua DuAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

wilcoxonCommented:
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

Chunhua DuAuthor 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
wilcoxonCommented:
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.
Rowby Goren Makes an Impact on Screen and Online

Learn about longtime user Rowby Goren and his great contributions to the site. We explore his method for posing questions that are likely to yield a solution, and take a look at how his career transformed from a Hollywood writer to a website entrepreneur.

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

thanks
wilcoxonCommented:
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.
Chunhua DuAuthor 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";
wilcoxonCommented:
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
Chunhua DuAuthor 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 EngineerCommented:
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.
Chunhua DuAuthor Commented:
Thanks, I would try it
Chunhua DuAuthor Commented:
Do you mean \\\$->\\+\\+\\\$
Chunhua DuAuthor Commented:
How can I understand 'double', I am not sure double what
nociSoftware EngineerCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
wilcoxonCommented:
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
wilcoxonCommented:
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

Chunhua DuAuthor 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 EngineerCommented:
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.
Chunhua DuAuthor Commented:
In my code:

print $$varName;

at this line, I hope to get value of $a1, why getting nothing? Must it use eval?
Chunhua DuAuthor Commented:
By my understanding, if I write:

$$varName

in level N of eval, $varName must be defined and get value in level N-1
Chunhua DuAuthor 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 EngineerCommented:
$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....
Chunhua DuAuthor Commented:
But why this works:

$str = m{$pattern};
for($xx = 1; $xx <= 2; $xx++)
{
      print $$xx;
}
nociSoftware EngineerCommented:
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...
wilcoxonCommented:
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).
Chunhua DuAuthor 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";
}
Chunhua DuAuthor Commented:
I have try your code with:

no strict 'refs';

I get empty output.
wilcoxonCommented:
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...
wilcoxonCommented:
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").
Chunhua DuAuthor 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!
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Perl

From novice to tech pro — start learning today.