Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

How to get a reference from an eval() string?

by exilepanda (Pilgrim)
on Sep 06, 2016 at 09:26 UTC ( #1171237=perlquestion: print w/replies, xml ) Need Help??

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

Code comes first:
use Data::Dumper; use strict; no warnings; no autovivification; my $return_from_sub = '{X}{Y}' ; # a result return from some sub my $to_process = '$x->' . $return_from_sub; # my local def my $x = { Y => { Z => 10, } }; # after some regex work on the $to_process my $lastPart = 'Y' ; # the last part from $to_process my $base = '$x->{X}' ; # before the $lastPart my $lastPartIs = "HASH" ; my $newData = [qw/a b c/]; my $ref = eval $base || eval "$base = {}"; # whatever it is, just make + it referencable if not defined. if ( $lastPartIs eq "HASH" ) { $ref->{"$lastPart"} = $newData; # Remark 1 } elsif ( $lastPartIs eq "ARRAY" ) { $ref->["$lastPart"] = $newData; # Remark 1 } print Dumper $x;

Above code demonstrated some sort what I want (but not entirely correct), however I have to make it one level up from the last "part".

I know that I cannot directly get that reference via eval. like :

$ref = eval '$base$return_from_sub'; $ref = [/whatever/];

This just make $ref pointed to something else so this won't work. But I wonder is there any trick can do something like :

$ref = eval '\$refString'; deference( $ref ) = $whatever ;# [] {} ""

Provided that, I don't know what will be returned from the sub, and I don't know what's gonna be stored. both (return_from_sub and to write) can be array ref, hash ref, code ref, whatever ref or simple scalar.

Replies are listed 'Best First'.
Re: How to get a reference from an eval() string?
by choroba (Archbishop) on Sep 06, 2016 at 09:36 UTC
    Why don't you put the whole assignment to eval?
    eval "$base\{$lastPart} = \$newData"

    But you can return a reference to the value to be able to change it:

    my $ref = eval "\\$base\{$lastPart}" || eval "$base = {}"; $$ref = $newData;

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      Oh! Stupid me... eval "$base\{$lastPart} = \$newData" This works!! BIG THANKS!!

      Seems my focus really draw to know how to get the reference deep in my heart.... rather than how to get this work done...

      But for the $$ref part, because I don't know what exactly the data type will be on the sub return side, either the assignment side, so that can be a %$ref, @$ref or $$ref etc. If I don't take care of it per case, I probably get sort of Not a XXXXX reference error.

        Beware the XY Problem. It's a common one in software fora.

Re: How to get a reference from an eval() string?
by haukex (Bishop) on Sep 06, 2016 at 12:24 UTC

    Hi exilepanda,

    Where are the strings you're putting into eval coming from, are any of them based on user input? If so, you'll have to be very careful to sanitize the user input, otherwise you may be exposed to serious security issues. String eval also has some other disadvantages (syntax errors not detected until runtime, and it's not particularly fast), which is why it's usually discouraged and there are other ways to write the code you're writing. Only if you're sure none of the aforementioned issues are a concern in this case, string eval could be used.

    Although it's possible to write some data-walking code without string eval by hand (in which it'd be helpful if you could show some more test cases of what you're trying to to do), maybe in this case the module Data::Diver could be useful to you?

    Hope this helps,
    -- Hauke D

      Where are the strings you're putting into eval coming from...

      It's from another program's exported plain text data in a file, and what's inside is a list of flatten object attributes and their value(~400 lines), which looks like:

      object.prop.subprop.[0].name = "foo" object.prop.subprop.[1].name = "bar" obj.prop.[0] = "blah" obj.prop.[1] = 1000 ... ...
      The vendor of that program exported this for me to do my part with perl implementation. And then my script will rewrite this text file, and the program will import the text file back.

      I considered about the security issue, anyway the read part seem clean enough for me, that's why I use eval. Although my implementation do deal with user input, but since my data are constrained to use /^[\w\d\., ]+$/i only, so the write back data should be clean as well... unless it's not;).

      I just read into Data::Diver and found that my $ref= DiveRef( $root, qw( top 9 new sub ) ); would be useful for me, so Thanks a lot!! Only I thought that would be some build in perl func or syntactical trick can do the job. Seem there should be no simple direct way to do that now.

        Hi exilepanda,

        Only I thought that would be some build in perl func or syntactical trick can do the job. Seem there should be no simple direct way to do that now.

        Well sure there is :-)

        my $data = {}; while (<DATA>) { chomp; my @fields = split /\s*[=\.]\s*/; build( $data, \@fields, pop @fields ); } sub build { my ($ref, $path, $val) = @_; return $val unless @$path; my $el = shift @$path; if ($el=~/^\[(\d+)\]$/) { $ref->[$1] = build( $ref->[$1], $path, $val ) } else { $ref->{$el} = build( $ref->{$el}, $path, $val ) } return $ref; } __DATA__ object.prop.subprop.[0].name = foo object.prop.subprop.[1].name = bar obj.prop.[0] = blah obj.prop.[1] = 1000

        $data then looks like so:

        { obj => { prop => ["blah", 1000] }, object => { prop => { subprop => [{ name => "foo" }, { name => "bar" + }] } }, }

        And then to walk the data structure:

        sub dive { my ($ref,@path) = @_; return $ref unless @path; my $el = shift @path; if ($el=~/^\[(\d+)\]$/) { return dive( $ref->[$1], @path ) } else { return dive( $ref->{$el}, @path ) } } print dive( $data, qw/ obj prop [0] / ), "\n"; # prints "blah" print dive( $data, qw/ object prop subprop [1] name / ), "\n"; # prints "bar"

        Hope this helps,
        -- Hauke D

Re: How to get a reference from an eval() string?
by ikegami (Pope) on Sep 06, 2016 at 18:13 UTC

    Instead, could you make the sub return an expression Data::Diver understands?

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1171237]
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (6)
As of 2020-09-26 08:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    If at first I donít succeed, I Ö










    Results (141 votes). Check out past polls.

    Notices?