Прим. пер.: Встретил сегодня в твиттере очень забавный, на первый взгляд, тред. А потом пригляделся и понял, что он не только забавный, но и занятный. А раз уж так сложилось, что сегодня пятница, то решил, что стоит поделиться обнаруженным и с товарищами:)
Сохраните следующую программу в /tmp/quine.pl
Запустите её командой
и она выведет свой собственный код.
«Квайны-обманки» довольно просто сочинять на многих языках программирования, где ошибка синтаксиса в исходнике провоцирует парсер на вывод ошибки, которая бы совпадала с исходным текстом программы. Я опубликовал несколько подобных «обманок» у себя в Twitter, включая следующую:
Но перловый квайн в начале этой заметки — это обманка совершенно другого рода — программа разбирается корректно. И недолго работает, пока не спотыкается об ошибку деления на ноль. Этот квайн очень чувствителен к именованию файла — например, запуск через ./quine.pl не сработает.
Так это сообщение об ошибке — на самом деле целая программа?!
В этой программе используется многое из перлового делай-что-я-имею-ввиду-парсера.
Символ / очень зависим от контекста применения и может быть расценен как символ деления, либо как начало регулярного выражения. И даже небольшие изменения в коде этой программы приводят к ошибке разбора регулярки, а не к выполнению кода. В данном случае оба символа / появляются в контексте оператора.
Другие несловарные части этой программы это
Тогда что же значат слова?
Слова в Perl могут быть именами подпрограмм, методов, пакетов или классов. Или же (в нестрогом режиме) строками без разделителя или может даже чем-то ещё, о чём я позабыл!
В Perl также применяется необычный синтаксис вызова методов, называемый "непрямым синтаксисом объекта", который выглядит следующим образом:
чаще всего можно видеть как
хотя для Perl более предпочтителен следующий синтаксис:
В документации perlobj говорится:
Начиная с правой стороны,
разбирается как вызов метода
где line — это имя пакета (класса), а pl — это метод.
В середине «at», «tmp» и «quine» разбираются как простые слова, т.е. строки. Выражение разбирается следующим образом:
Слева находятся два сложенных непрямых вызова метода,
внутреннее выражение, выполняющееся первым, это:
И это мгновенно вызывает исключение деления на ноль.
Сохраните следующую программу в /tmp/quine.pl
Illegal division by zero at /tmp/quine.pl line 1.
Запустите её командой
perl /tmp/quine.pl
и она выведет свой собственный код.
«Квайны-обманки» довольно просто сочинять на многих языках программирования, где ошибка синтаксиса в исходнике провоцирует парсер на вывод ошибки, которая бы совпадала с исходным текстом программы. Я опубликовал несколько подобных «обманок» у себя в Twitter, включая следующую:
File "quine.py", line 1
File "quine.py", line 1
^
IndentationError: unexpected indent
Но перловый квайн в начале этой заметки — это обманка совершенно другого рода — программа разбирается корректно. И недолго работает, пока не спотыкается об ошибку деления на ноль. Этот квайн очень чувствителен к именованию файла — например, запуск через ./quine.pl не сработает.
Так это сообщение об ошибке — на самом деле целая программа?!
В этой программе используется многое из перлового делай-что-я-имею-ввиду-парсера.
Символ / очень зависим от контекста применения и может быть расценен как символ деления, либо как начало регулярного выражения. И даже небольшие изменения в коде этой программы приводят к ошибке разбора регулярки, а не к выполнению кода. В данном случае оба символа / появляются в контексте оператора.
Другие несловарные части этой программы это
1.
, который интерпретируется просто как число и .
которая является оператором конкатенации.Тогда что же значат слова?
Слова в Perl могут быть именами подпрограмм, методов, пакетов или классов. Или же (в нестрогом режиме) строками без разделителя или может даже чем-то ещё, о чём я позабыл!
В Perl также применяется необычный синтаксис вызова методов, называемый "непрямым синтаксисом объекта", который выглядит следующим образом:
метод объект аргументы
чаще всего можно видеть как
print $filehandle "message";
my $instance = new Class(args);
хотя для Perl более предпочтителен следующий синтаксис:
$filehandle->print("message");
my $instance = Class->new(args);
В документации perlobj говорится:
Для разбора этого кода Perl использует эвристики, основанные на том, какие имена пакетов ему известны, какие в текущем пакете существуют подпрограммы, какие слова он до этого встречал и анализируя другие вводные данные. Излишне говорить, что эвристика может давать очень неожиданные результаты!Как он разбирает этот код?
Начиная с правой стороны,
pl line 1.
разбирается как вызов метода
line->pl(1.)
где line — это имя пакета (класса), а pl — это метод.
В середине «at», «tmp» и «quine» разбираются как простые слова, т.е. строки. Выражение разбирается следующим образом:
(("at" / "tmp") / "quine") . line->pl(1.)
Слева находятся два сложенных непрямых вызова метода,
division->Illegal(zero->by( ... ))
внутреннее выражение, выполняющееся первым, это:
"at" / "tmp"
И это мгновенно вызывает исключение деления на ноль.
Комментарии (3)
codesign
06.04.2019 13:23+2Всё-таки неправильно разбирать начинать с правой стороны, т.к. парсится программа слева направо. Хотя итоговый разбор — верный.
Кстати, смотреть подобное можно с помощью Deparse:
$ perl -MO=Deparse -E 'Illegal division by zero at /tmp/quine.pl line 1.' use feature 'current_sub', 'evalbytes', 'fc', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval'; 'division'->Illegal('zero'->by('at' / 'tmp' / 'quine' . 'line'->pl(1))); -e syntax OK
Habazlam
Ой, всё.
Провёл сегодняшний день, пиля тупой конечный автомат под FasrCGI.
В Иркутске хоть омуля в перерыве поедят, а я как сирота
4umak Автор
С омулем сложновато нынче, официально промышленный вылов пока прикрыли:(