Как я могу получить синтаксическое дерево из coderef в Perl? - PullRequest
2 голосов
/ 05 октября 2010

Я хотел бы проверять и манипулировать кодом произвольных процедур Perl (получаемых coderefs) в Perl.Есть ли инструмент / модуль / библиотека для этого?Нечто похожее на B :: Concise , за исключением того, что B :: Concise печатает код на выходе, но я хотел бы проверить его программно.

Я хотел бы использовать его какэтот.Дан кодовый номер F, который называется например.с 10 аргументами:

$ret = &$F(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10);

Я хотел бы создать функцию F1, st.

&$F(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) == 
  &$F1(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)*
  &$C(x2, x3, x4, x5, x6, x7, x8, x9, x10)

, которая должна «разложить» ее на две части, где втораяне зависит от x1, и первое настолько просто, насколько это возможно (я предполагаю, что F создан как огромный продукт).

Приложение, для которого я хочу это оптимизация Метрополисалгоритм - предположим, что я выбираю распределение p(x1 | x2 = X1, x3 = X3, ...) = f(x1, x2, x3, ...).Сам алгоритм инвариантен относительно.мультипликативные постоянные коэффициенты и другие переменные не меняются в алгоритмах, поэтому часть, не зависящая от x1 (т. е. $c сверху), вообще не нуждается в оценке).

Совместная вероятность можетесть напримерследующая форма:

  p(x1, x2, x3, x4, x5) = g1(x1, x2)*g2(x2, x3)*g3(x3, x4)*g4(x4, x5)*g5(x4, x1)*g6(x5, x1)

Я также рассматриваю конструирование p как объекта, состоящего из факторов с аннотациями, от которых зависит тот или иной фактор.Даже это извлекло бы пользу из интроспекции кода (автоматического определения переменных).

Ответы [ 3 ]

9 голосов
/ 05 октября 2010

Для самоанализа optrees обычно используется семейство модулей B.

Учитывая кодовую ссылку $cv, сначала создайте объект B для этого:

my $b_cv = B::svref_2object($cv);

Теперь вы можете вызывать различные методы, описанные в B, для извлечения различных вещей из optree.

Используя только самоанализ optree, вы уже можете достичь удивительных вещей.См. DBIx::Perlish для довольно продвинутого примера этого.

Также есть модуль B::Generate, который позволяет создавать новые optree, которые делают все, что выхочу или манипулировать существующими optrees.Тем не менее, B::Generate не настолько зрелый, как можно было бы надеяться, и в нем много отсутствующих функций и довольно много ошибок.

Фактическое создание и манипулирование optree обычно лучше всего делать с использованием C api perl, как описанов perlapi, perlguts и perlhack, среди прочих.Возможно, вам также придется изучить некоторые XS, чтобы раскрыть функции манипулирования optree, которые вы записали обратно в пространство perl, но на самом деле это самая простая часть.

Создание optrees (необязательно основанный на других существующих optree, которые анализируются) в последнее время, кажется, стал несколько популярным, особенно после того, как Syntax Plugins было добавлено в ядро ​​в perl 5.12.0.Вы можете найти различные примеры, такие как Scope::Escape::Sugar на cpan.

Тем не менее, работа с optrees в Perl все еще несколько сложна и не совсем удобна для начинающих.Это не должно быть необходимо ни для одной из самых загадочных вещей.Что-то вроде использования B::Deparse->new->coderef2text($cv), а затем, возможно, очень незначительное искажение с оцениваемым исходным кодом действительно так далеко, как я бы хотел перейти к самоанализу optree из пространства чистого perl.

Возможно, вы захотите сделать шаг назади объясните реальную проблему, которую вы пытаетесь решить.Может быть, есть гораздо более простое решение, которое вообще не предполагает работы с optrees.

1 голос
/ 05 октября 2010

Учитывая ваш вновь сформулированный вопрос - я думаю, что вы должны сделать здесь, вместо того, чтобы пытаться взломать coderefs, это отложить получение coderef как можно дольше.

  1. Создайте объект, представляющий экземпляр вашего вычисления.
  2. Напишите методы для этого объекта, необходимые для оценки стоимости вычислений. Нет кодагена, просто сделай это длинным медленным путем. Это просто для того, чтобы дать вам базовый код для следующих шагов, который легко проверить и, надеюсь, легко понять.
  3. Напишите тесты, чтобы убедиться в правильности того, что вы сделали на шаге 2. (Поменяйте местами до шага 2, если вы такой человек.)
  4. Реализуйте то, о чем вы спрашиваете в этом вопросе, написав методы для преобразования объекта вычисления в новый, который представляет более оптимизированную форму того же вычисления. Используйте свои тесты, чтобы гарантировать, что вычисления все еще дают правильный результат после оптимизации.
  5. Напишите код, который принимает вычислительный объект и генерирует подпрограмму (либо по строке eval, либо с использованием B), которая выполняет это вычисление. Используйте свои тесты, чтобы убедиться, что вычисления по-прежнему дают правильный результат после их компиляции.

Необязательный шаг для вставки между 2 и 5:

  • Напишите некоторый синтаксический сахар (возможно, с использованием overload, но возможны и другие инструменты), чтобы позволить вам создавать «вычислительные объекты», используя красивые выражения, которые напоминают само вычисление, вместо множества конструкторов объектов.
0 голосов
/ 05 октября 2010

Perl 5 не позволяет вам манипулировать байт-кодом на лету, но вы можете создавать анонимные функции.Если я правильно понимаю ваш пример и сомневаюсь в этом, у вас уже есть две функции, на которые ссылаются $f1 и $c, и вы хотите создать новую ссылку $f, которая содержит результаты первых двухумножаются друг на друга.Это просто:

my $f = sub { $f1->(@_) * $c->(@_[1 .. 9]) };

$f->(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Обратите внимание на использование оператора стрелки вместо & для разыменования кодовых ссылок.Этот стиль гораздо более распространен (и, на мой взгляд, более читабелен).

...