Может ли Perl быть" статически " проанализирован?

An статья под названием "Perl не может быть проанализирована, формальное доказательство" делает раундов. Итак, Perl решает значение своего проанализированного кода во время" выполнения "или"во время компиляции"?

в некоторых обсуждениях, которые я читал, у меня создается впечатление, что аргументы вытекают из неточной терминологии, поэтому, пожалуйста, попробуйте определить свои технические термины в своем ответе. Я намеренно не определил "время выполнения", " статически "или " проанализировал", чтобы я мог получить перспективы от людей, которые, возможно, определите эти термины по-другому для меня.

Edit:

речь идет не о статическом анализе. Это теоретический вопрос о поведении Perl.

5 ответов


Perl имеет четко определенную фазу "время компиляции", за которой следует четко определенная фаза" время выполнения". Однако есть способы перехода от одного к другому. Многие динамические языки имеют eval конструкции, которые позволяют компиляцию нового кода на этапе выполнения; в Perl возможно также обратное - и общее. BEGIN блоки (и неявное BEGIN блок причиненный use) вызовите временную фазу выполнения во время времени компиляции. А BEGIN блок выполняется, как только он скомпилирован, вместо ожидания остальной части блока компиляции (т. е. текущего файла или текущего eval) для компиляции. С BEGINs выполняется до компиляции следующего за ними кода, они могут влиять на компиляцию следующего кода практически любым способом (хотя на практике основные вещи, которые они делают, - это импорт или определение подпрограмм или включение строгости или предупреждений).

A use Foo; в основном эквивалентно BEGIN { require foo; foo->import(); }, С требуют (как eval STRING) один из способов вызова времени компиляции из среды выполнения, что означает, что мы теперь находимся во время компиляции во время выполнения во время компиляции, и все это рекурсивно.

во всяком случае, то, что он сводится к разрешимости разбора Perl, заключается в том, что, поскольку компиляция одного бита кода может зависеть от исполнение предыдущего фрагмента кода (который теоретически может сделать что-нибудь), у нас есть тип проблемы с остановкой ситуация; единственный способ правильно разобрать данный файл Perl в общем - запустить его.


Perl имеет блоки BEGIN, которые запускают пользовательский код Perl во время компиляции. Этот код может повлиять на значение другого компилируемого кода, что делает "невозможным" разбор Perl.

например, код:

sub foo { return "OH HAI" }

"действительно":

BEGIN {
    *{"${package}::foo"} = sub { return "OH HAI" };
}

это означает, что кто-то может написать Perl как:

BEGIN {
    print "Hi user, type the code for foo: ";
    my $code = <>;
    *{"${package}::foo"} = eval $code;
}

очевидно, что никакой инструмент статического анализа не может угадать, какой код пользователь собирается ввести здесь. (И если пользователь говорит из sub {}, это даже повлияет на то, как вызовы foo интерпретируются на протяжении всей остальной части программы, потенциально сбрасывая синтаксический анализ.)

хорошая новость заключается в том, что невозможные случаи очень угловой Кейси; технически возможно, но почти наверняка бесполезно в реальном коде. Поэтому, если вы пишете инструмент статического анализа, это, вероятно, не вызовет у вас никаких проблем.

честно говоря, каждый язык, который стоит своей соли имеет эту проблему, или что-то подобное. В качестве примера, бросьте свой любимый код walker на этот Lisp-код:

(iter (for i from 1 to 10) (collect i))

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

наконец, многие люди думают, что Perl не хватает статического анализа и инструментов рефакторинга, таких как Java, из-за относительной сложности его анализа. Я сомневаюсь, что это правда, я просто думаю, что в этом нет необходимости, и никто не потрудился написать это. (Людям нужна "корпия", поэтому есть Perl::Critic, например.)

любой статический анализ, который мне нужно сделать Perl для генерации кода (некоторые макросы emacs для обслуживания тестовых счетчиков и Makefile.PL) работал нормально. Могут ли странные угловые случаи сбить мой код? Конечно, но я не делаю все возможное, чтобы написать код, который невозможно поддерживать, хотя я мог бы.


люди использовали много слов, чтобы объяснить различные фазы, но это действительно простой вопрос. При компиляции источника Perl Perl intrepreter может запустить код, который изменяет способ анализа остальной части кода. Статический анализ, который не запускает код, пропустит это.

в этом сообщении Perlmonks Джеффри рассказывает о своих статьях в Обзор Perl которые идут в гораздо больше деталей, включая пример программы, которая не анализирует таким же образом каждый раз, когда вы запускаете он.


Perl имеет фазу компиляции, но она отличается от большинства обычных фаз компиляции, когда дело доходит до кода. Лексер Perl превращает код в токены, затем анализатор анализирует токены для формирования дерева op. Однако блоки BEGIN {} могут прервать этот процесс и разрешить выполнение кода. При выполнении use. Все!--3--> блоки выполняются прежде всего, что дает вам возможность настроить модули и пространства имен. Во время общей "компиляции" скрипта вы, скорее всего, будете использовать Perl для определения как должен выглядеть модуль Perl, когда это будет сделано. sub, bare, подразумевает добавление его в glob для пакета, но вам не нужно. Например, это (хотя и нечетный) способ настройки методов в модуле:

package Foo;

use strict;
use warnings;
use List::Util qw/shuffle/;

my @names = qw(foo bar baz bill barn);
my @subs = (
    sub { print "baz!" },
    sub { die; },
    sub { return sub { die } },
);
@names = shuffle @names;
foreach my $index (0..$#subs) {
   no strict 'refs';
   *{$names[$index]} = $subs[$index];
}

1;

вы есть интерпретировать это, чтобы знать что он делает! Это не очень полезно, но это не то, что вы можете определить заранее. Но это 100% действительный perl. Несмотря на то, что этой функцией можно злоупотреблять, она также может выполнять большие задачи, такие как build сложные подводные лодки, которые все выглядят очень похожими, программно. Это также затрудняет понимание того, что все делает.

Это не значит, что скрипт perl не может быть "скомпилирован" - в perl компиляция просто определяет, как должен выглядеть модуль. Вы можете сделать это с помощью

perl -c myscript.pl

и он скажет вам, может ли он добраться до точки, где он начнет выполнять основной модуль. Вы просто не можете просто знать, глядя на это "статически".

однако,PPI показано, мы можем приблизиться. Действительно близкий. Достаточно близко, чтобы делать очень интересные вещи, такие как (почти статический) анализ кода.

"время выполнения", затем становится тем, что происходит после того, как все BEGIN блоки были выполнены. (Это упрощение; это гораздо больше. См.perlmod дополнительные.) Это все еще perl-код, но это отдельная фаза выполнения, выполняемая после того, как все выше приоритетные блоки запущены.

хроматический имеет некоторые подробные сообщения на своем современном:: Perl блог:


C++ имеет аналогичную проблему в своей системе шаблонов, но это не мешает компиляторам компилировать ее. Они просто вырвутся или будут работать вечно на угловых случаях, где будет применяться такой аргумент.