Почему эта программа действительна? Я пытался создать синтаксическую ошибку

489

Я использую ActiveState 32-битный ActivePerl 5.14.2 в Windows 7. Я хотел возиться с ловушкой Git pre-commit, чтобы обнаружить программы, проверенные на наличие синтаксических ошибок. (Каким-то образом мне просто удалось сделать такой неудачный коммит.) Так что в качестве тестовой программы я случайно набросал это:

use strict;
use warnings;

Syntax error!

exit 0;

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

Билл Рупперт
источник
121
Вы только что доказали, что ввод случайных слов в perl создает работающие программы ??!?!?!?!
Питер М
10
@PeterM Вряд ли случайные слова. Я доказал, что не знаю достаточно о синтаксисе Perl. Теперь я знаю немного больше.
Билл Рупперт
10
Вы, вероятно, хотите, no indirectчтобы те не происходили
LeoNerd
@LeoNerd Спасибо за совет!
Билл Рупперт
1
Это самый известный Perl вопрос когда-либо. Еще лучше, чем фрагмент Шварца :whatever / 25 ; # / ; die "this dies!";
jm666

Ответы:

540

Perl имеет синтаксис, называемый «косвенная запись метода». Это позволяет

Foo->new($bar)

быть написанным как

new Foo $bar

Так что значит

Syntax error ! exit 0;

такой же как

error->Syntax(! exit 0);

или

error->Syntax(!exit(0));

Это не только допустимый синтаксис, он не приводит к ошибке во время выполнения, потому что первое, что выполняется, это exit(0).

Ikegami
источник
1
@ Хасан, почему? За этим следует выражение.
Икегами
3
Я дошел до того, что прочитал его как «Синтаксическая ошибка! Выход 0;», но я не думал о косвенном вызове. Потратил много времени, забыв об этом!
Билл Рупперт
6
@ Хассан, думай об этом так, не !exit(0)может быть ошибкой типа, !$xпоскольку ни один из них не напечатан.
Икегами
11
@Hassan, у языка есть типы. В частности, значения имеют типы. Операторы и подпрограммы просто не ограничиваются возвратом определенных типов значений. Это оказывается очень полезным при небольших затратах (благодаря предупреждениям).
икегами
6
@Nawaz, это на самом деле довольно популярно. Он используется всеми, кто создает объекты на Java и C ++, и большим количеством программистов на Perl, которые используют new Classи print $fh ...вместо Class->new(...)и $fh->print(...). Я предоставлю вам, что это вызывает странные сообщения об ошибках, хотя
ikegami
112

Я не знаю почему, но вот что делает Perl:

perl -MO=Deparse -w yuck
BEGIN { $^W = 1; }
use warnings;
use strict 'refs';
'error'->Syntax(!exit(0));
yuck syntax OK

Кажется, что парсер думает, что вы вызываете метод Syntaxдля error-объекта ... Странно!

павел
источник
3
Это косвенный синтаксис вызова метода. Это (вроде) работает здесь, потому что exit(0)сначала выполняется оценка, заставляя программу завершиться, прежде чем она попытается передать результат 'error'->Syntax().
duskwuff -неактивно-
6
Perl, похоже, предполагает «косвенный (объектный) синтаксис», обычно используемый new Classвместо «как» Class->new(). Для вызова метода Syntaxвыполняется exitфункция, поэтому ошибка времени выполнения никогда не возникает.
am
118
Поздравляю. Вы нашли программу, в которой нужно добавить точку с запятой, чтобы компиляция не удалась.
моб
use strict; use warnings; error->Syntax(! print "hi"); Выход: синтаксис Ok для perl -MO = Deparse также, но с use warningsэтим, вероятно, следует что-то сказать, поскольку он может выяснить, что он не загружается. Вместо этого он выдает ошибку времени выполнения "Не удается найти метод объекта ..".
53

Причина, по которой вы не получаете ошибку, состоит в том, что первый исполняемый код

exit(0);

Потому что у вас не было точки с запятой в первой строке:

Syntax error!

Компилятор догадывается (неверно), что это вызов подпрограммы с добавленным notоператором !. Затем он выполнит аргументы этой подпрограммы, что и происходит exit(0), после чего программа завершает работу и устанавливает уровень ошибки равным 0. Ничего больше не выполняется. , так что больше никаких ошибок времени выполнения не сообщается.

Вы заметите, что если вы перейдете на что- exit(0)то подобное, print "Hello world!"вы получите ошибку:

Can't locate object method "Syntax" via package "error" ...

и ваш уровень ошибки будет установлен:

> echo %errorlevel%
255
TLP
источник
7
>The compiler will guess (incorrectly) Компилятор не может ничего сделать неправильно.
Лиам Лаверти
14
@LiamLaarity Да, это возможно. Он может неправильно угадать, что имел в виду человек.
TLP
4
Человек является неправильным в уравнении. Компилятор может быть только «правильным» или «сломанным». Он не получает мнения относительно определения языка или намерений пользователя.
Лиам Лаверти
4
@LiamLaarity Было бы довольно аккуратным компилятором, если бы он мог угадать намерение пользователя в этом случае, да. Следовательно, компилятор не может правильно угадать. Возможно, вы делаете некоторый технический жаргонный анализ моего утверждения, который, я бы добавил, является неправильным способом его прочтения.
TLP
Разве это не переводчик? ;-)
Рикки
33

Как отмечено выше, это вызвано косвенной нотацией вызова метода. Вы можете предупредить об этом:

use strict;
use warnings;
no indirect;

Syntax error!

exit 0;

Производит:

Indirect call of method "Syntax" on object "error" at - line 5.

Это требует косвенного модуля CPAN .

Вы также можете использовать, no indirect "fatal";чтобы заставить программу умереть (это то, что я делаю)

Марк Фаулер
источник
8

Попробуйте Perl 6 , кажется, он с большей готовностью оправдает ваши ожидания:

===SORRY!=== Error while compiling synerror.p6
Negation metaoperator not followed by valid infix
at synerror.p6:1
------> Syntax error!⏏<EOL>
    expecting any of:
        infix
        infix stopper
Moritz
источник
1

В этой статье мы стремимся ответить на давнюю открытую проблему в сообществе языков программирования: возможно ли намазать краску на стене, не создавая действительный Perl?

TLDR; Едва

Holli
источник
Я люблю это. Возможно, мне придется сканировать на некоторых фотографиях.
Билл Рупперт