Примерно проверка синтаксиса кода Perl, быстрее, чем perl -c
Есть ли способ синтаксической проверки Perl-программы без запуска perl? Хорошо известный ответ "нет". Без запуска полной среды выполнения Perl для оценки кода для импорта и т. Д. Вы не сможете определить, верен ли синтаксис программы.
Но что, если вы хотите приблизительный ответ? Проверка синтаксиса, которая скажет "плохо" или "возможно". Если 'плохо', то программа определенно не является допустимым кодом Perl (при условии, что интерпретатор Perl Vanilla). Если "возможно", то это выглядит нормально, но только сам Perl сможет сказать наверняка.
Программа, которая всегда печатает "возможно", явно является такой проверкой, но не очень полезной. Лучшая попытка - использовать PPI. Могут быть некоторые допустимые программы на Perl, которые отклоняются PPI, но если это происходит, это принимается как ошибка PPI (я думаю).
Отступление: Почему это полезно? Одним из применений может быть проверка квалитов. Чтобы поймать различные моменты "d'oh", система управления версиями в $WORK запускает весь код Perl через perl -c, прежде чем разрешить коммит. (Я не рекомендую это как общую практику, просто отмечаю, что это было полезно на нашем сайте.) Но perl -c небезопасен, так как выполняет код (как и должен). Вместо этого было бы безопаснее использовать консервативную проверку синтаксиса за счет некоторых случаев, когда проверка говорит "возможно", но на самом деле программа не является допустимым Perl.
Что я действительно хочу (конец отступления): Но на самом деле безопасность не является мотивирующим фактором для моего текущего применения. Я заинтересован в скорости. Есть ли способ грубо проверить и отклонить плохо сформированный код Perl, прежде чем тратить время на раскрутку всего интерпретатора Perl? PPI медленнее, чем сам Perl, поэтому не очень хороший кандидат. Вы можете написать приблизительную грамматику Perl и использовать генератор синтаксического анализатора для создания простой программы на C, которая принимает или отклоняет псевдо-Perl.
Мое приложение "дельта-отладка". Вы начинаете с большой программы, которая имеет определенное свойство (например, segfaulting) и выбиваете его разделы, сохраняя это свойство. Я использую http://delta.tigris.org/ который работает в простом стиле. Многие тестовые примеры, которые он генерирует, не будут действительным кодом Perl. Отладочная дельта пошла бы быстрее, если бы их можно было быстро устранить до запуска полного исполняемого файла Perl.
Поскольку затраты времени на запуск интерпретатора Perl, вероятно, составляют большую часть времени, вы могли бы реализовать какой-либо сервер, который прослушивает сокет, принимает текст программы и возвращает "плохой" или "возможно", пытаясь выполнить eval(). текст или запустить его через PPI.
Другой способ ускорить процесс - заставить Perl терпеть неудачу быстрее. Обычно он печатает все синтаксические ошибки и диагностику, которые он может найти. Если вместо этого он остановится на первом, какое-то время будет сохранено.
Но мне нравится идея грамматики для почти Perl, которую можно проверить с помощью простой программы на Си. Существует ли такая вещь?
(Связано: Perl неглубокая проверка синтаксиса? Т.е. не проверять синтаксис импорта, но мой вопрос больше касается скорости, и я рад получить грубую проверку, которая будет принимать некоторые недопустимые программы, если они не отклоняют допустимые.)
2 ответа
Учитывая исходные фильтры, прототипы и API ключевых слов Perl (5.14+), импорт может радикально изменить, какой синтаксис является действительным, а какой - нет. Если вы импортируете что-либо, то такая проверка будет очень мало полезной.
Если вы ничего не импортируете, то вы можете безопасно загрузить все свои внешние модули require
вместо use
, а также perl -c
станет молниеносно (потому что require
обрабатывается во время выполнения).
ИЦП здесь не особенно полезен, потому что он использует очень простую методику наилучшего предположения при разборе, поэтому примет очень некорректные входные данные без жалоб:
#!perl
use strict;
use warnings;
use PPI::Document;
use PPI::Dumper;
PPI::Dumper->new(
PPI::Document->new(\"foo q[}")
)->print;
Perl:: Lexer, возможно, может быть более полезным, хотя он будет обнаруживать только ошибки, настолько сломанные, что их даже нельзя будет маркировать. Мой предыдущий пример оказался одним из таких, так что вот что жалуется:
#!perl
use strict;
use warnings;
use Perl::Lexer;
print $_->inspect, $/
for @{ Perl::Lexer->new->scan_string("foo q[}") };
Тем не менее, такие вещи, как API ключевых слов Perl, Devel::Declare и исходные фильтры, применяются перед лексингом, поэтому, если вы импортируете какие-либо модули, использующие эти методы, Perl::Lexer застрянет. (Любой из этих методов может легко сделать foo q[}
действительный синтаксис.)
Compiler:: Lexer и Compiler::Parser могут быть полезны. Следующие дампы ядра:
#!perl
use strict;
use warnings;
use Compiler::Lexer;
use Compiler::Parser;
my $t = Compiler::Lexer->new("eg.pl")->tokenize("foo q[}");
my $a = Compiler::Parser->new->parse($t);
Хотя, если вы исправите несоответствующие кавычки в foo q[}
в foo q[]
больше не сбрасывает ядро. Это кажется результатом.;-)
В конечном счете, ответ зависит от того, какой код вы пишете и какой класс ошибок вы надеетесь обнаружить. perl -c
даст вам довольно строгую проверку синтаксиса. Perl::Lexer может быть быстрее, но есть большие классы ошибок, которые он не обнаружит. Compiler::Lexer/Compiler::Parser может быть полезен в будущем, но, похоже, прямо сейчас он ведет себя беспорядочно.
Лично я бы придерживался perl -c
и, если это слишком медленно, попробуйте сократить количество модулей, которые вы загружаете во время компиляции, в пользу загрузки во время выполнения.
TL; DR: если вам нужен статический анализ, не используйте Perl.
Если вам нужна только быстрая проверка на скомпилируемость, запустите perl-процесс, который будет проверять каждый файл за вас:
perl -MFile::Slurp -lne'print 0+!! eval "sub {" . read_file($_) . "}"'