Вопрос по parsing, dynamic-languages, perl, runtime, interpreter – Может ли Perl анализироваться статически?

9

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

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

Edit:

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

Также из хакерских новостей:news.ycombinator.com/item?id=770072 draegtun
@ Пол Биггар: Часть этого похожа на традиционный переводчик. Та часть, в которой он переходит в исполнение до завершения интерпретации остальной части кода, не является. Robert P
Роберт П.: Модель исполнения Perl 5 определенно не совпадает с традиционным понятием интерпретатора. & Quot ;. Затем он продолжает описывать традиционного переводчика ... Paul Biggar

Ваш Ответ

5   ответов
5

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

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

2

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

Да, хорошо сказано. Та же идея, что и в моем посте, и много меньше слов :)
Это не очень похоже - для шаблонов C ++ все используемые значения также являются выражениями времени компиляции, и они явно отличаются от выражений времени выполнения. В Perl с примером, приведенным в связанной статье, функция может быть определена по-разному в зависимости, например, от пользовательский ввод строки, поэтому остальная часть программы будет передаваться иначе, чем с момента ввода. В C ++ нет ничего даже отдаленно похожего.
@Pavel Вы можете создать (почти) точный аналог примера в статье на C ++, используя шаблоны и неоднозначность объявления / инициализации. Тот факт, что Perl может использовать это во время выполнения, тогда как компилятор C ++ должен разрешать его во время компиляции, не имеет значения.
@Segfault: статический анализ выполняется до выполнения.
19

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

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

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

Чаще всего на компиляцию одного бита кода может влиять компиляция предыдущего фрагмента кода - особенно, является ли идентификатор именем пакета или подпрограммы.
11

которые запускают пользовательский код 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 ($) {} вместоsub {}, это даже повлияет на то, какfoo интерпретируются на протяжении всей остальной части программы, потенциально отбрасывая синтаксический анализ.)

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

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

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

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

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

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

Да, хотя конец начальной фазы компиляции особенный.
@ Пол, нет, имена отражают Большую Задачу каждого из этих этапов. Имена являются целенаправленными, описательными и точными.
Так почему же вы используете термины «запускать Perl-код во время компиляции»? а не «компилировать код Perl во время выполнения». В чем разница? Вот почему я спросил о терминологии. Paul Biggar
Так что это просто терминология сообщества Perl? Было бы также правильно сказать, что второй "compile" происходит во время выполнения блока BEGIN, как сказать, что первое выполнение происходит во время фазы компиляции основного кода? Paul Biggar
Это не просто терминология. Хотя Perl может выполнить некоторый код на этапе компиляции и, возможно, скомпилировать некоторый код на этапе выполнения, у каждого из них также есть ловушки для запуска в начале и конце фаз. Хотя они немного размыты изнутри, у них есть границы, где происходят другие вещи.
3

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

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;

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

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

perl -c myscript.pl

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

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

«Время выполнения» затем становится тем, что происходит после всехBEGIN блоки выполнены. (Это упрощение; есть еще много чего.perlmod для получения дополнительной информации.) Это все еще выполняется на Perl-коде, но это отдельная фаза выполнения, выполняемая после того, как все блоки с более высоким приоритетом были выполнены.

У chromatic есть несколько подробных сообщений в его блоге Modern :: Perl:

How a Perl 5 Program Works On Parsing Perl 5
Предположительно, вы можете заставить блок BEGIN проверять что-то в файловой системе или сети, что приводит к двум анализам идентичной программы с двумя различными значениями? Paul Biggar
Хорошо, но это просто игра с таблицей символов. Но строка не может изменить значение во время программы (в смысле цитируемой статьи), не так ли? Paul Biggar
@Paul Biggar: В статье не говорится о дереве разбора для небольшого изменения кода во время выполнения, речь идет о неспособности (в общем случае) определить дерево разбора для этого кода без выполнения чего-либо.
Абсолютно. Я видел (возможно, ошибочных) разработчиков Perl, использующих блок BEGIN для анализа аргументов в командной строке, а затем изменения переменных, доступных для выполнения, на основании этого. Фактически, вам даже не нужно делать это во время «компиляции»; фаза; приведенный выше код может быть выполнен несколько раз. Вы могли бы иметь это в функции и даже изменить поведение модуля после того, как он был "скомпилирован". Податливость perl-кода - это то, что действительно конкурирует только с другими динамическими языками; LISP-подобные языки приходят на ум в качестве яркого примера.

Похожие вопросы