Захват переменных назначений в Perl eval - PullRequest
5 голосов
/ 24 ноября 2011

Я бы хотел получить возможность присваивать переменные из Perl eval.То есть, чтобы определить, какие имена переменных были назначены в коде, и извлечь их значение.

Например, если я запускаю:

eval '$foo=42; $bar=3.14;'

Результат вычисления равен 3,14 (последнее значение), но я также хотел бы иметь возможность определять имена «$ foo» и «$ bar» и их значения (не зная заранее их имен).

Я прочиталпара способов вставки переменных в блок eval с помощью Safe и Eval :: Context, но еще нет способа их извлечения.Я больше знаком с Python eval / exec, который имеет встроенную поддержку для этого.

Ответы [ 3 ]

7 голосов
/ 24 ноября 2011

Любые лексические переменные, объявленные в eval, будут потеряны после его окончания.Чтобы захватить и изолировать глобальные переменные, которые установлены в eval, вы можете использовать модуль Safe для создания нового глобального пространства имен.Что-то вроде следующего:

use Safe;

my $vars = Safe->new->reval(qq{
    $code_to_eval; 
    $code_to_search_the_symbol_table_for_declared_variables
});

Где код поиска определяется как нечто, обходящее вложенную таблицу символов %main::, ищущую любые переменные, представляющие интерес.Вы можете сделать так, чтобы он возвращал структуру данных, содержащую информацию, и затем вы можете делать с ней то, что вам нравится.

Если вы беспокоитесь только о переменных, определенных на корневом уровне, вы можете написать что-то вроде:

use strict;
use warnings;

my $eval_code = '$foo=42; $bar=3.14;';

use Safe;
my $vars = Safe->new->reval(
    $eval_code . q{;
    my %vars;
    for my $name (keys %main::) {
        next if $name =~ /::$/    # exclude packages
         or not $name =~ /[a-z]/; # and names without lc letters

        my $glob = $main::{$name};
        for (qw($SCALAR @ARRAY %HASH)) {
            my ($sigil, $type) = /(.)(.+)/;
            if (my $ref = *$glob{$type}) {
                $vars{$sigil.$name} = /\$/ ? $$ref : $ref
            }
        }
    }
    \%vars
});

print "$_: $$vars{$_}\n" for keys %$vars;
# $foo: 42
# $bar: 3.14

В поисковом коде также может использоваться Padwalker для поиска в текущей лексической области любых заданных переменных с использованием функции peek_my.

1 голос
/ 24 ноября 2011

Вот моя попытка найти решение, основанное на Safe , как предлагает Эрик Стром.

package main;
use warnings; use strict;
use Safe;

my $cpt = new Safe;

$cpt->permit_only(qw(sassign lineseq padany const rv2sv leaveeval));
my $name_space = $cpt->root;

my $no_strict = 0;
#
# populate the clean symbol table
#
$cpt->reval('0');
die "safe compartment initialisation error: $@" if $@;
my %symtab_clean = do {no strict 'refs'; %{$name_space.'::'} }; 

my $result = $cpt->reval('$foo=42; $bar=3.14;', $no_strict);

if ($@) {
    warn "eval error: $@";
}
else {
    #
    # symbol table additions
    #
    my %symtab_dirty = do {no strict 'refs'; %{$name_space.'::'} }; 

    my @updated_variables = grep { ! exists $symtab_clean{$_} } (sort keys %symtab_dirty);

    foreach my $variable (@updated_variables) {
        my $value = do{ no strict 'refs'; ${$name_space.'::'.$variable} };
       print "variable $variable was set to: $value\n"
    }
}

Примечания:

  1. Вышеуказанное позволяетминимальный ограниченный набор безопасных кодов операций.См. коды операций perl
  2. Я решил искать различия до и после выполнения команды
0 голосов
/ 24 ноября 2011

Что ж, значение $ foo будет равно 42 после запуска eval.Нет никаких изменений в области видимости, которые делают $ foo невидимым.

Но это, конечно, предполагает предвидение существования $ foo.

В любом случае, это будет работать только без установленного use strict,что почти наверняка плохая идея.С use strict сценарий не будет компилироваться.Я предполагаю, что ваш пример более сложный, и вы ищете изменения в существующем хеше, выполненном вашим eval.Но неясно, как ваш eval не мог знать, что он меняет, если только не происходит какое-то динамическое внедрение кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...