Mengapa program ini valid? Saya mencoba membuat kesalahan sintaksis

489

Saya sedang menjalankan ActiveState 32 bit ActivePerl 5.14.2 di Windows 7. Saya ingin main-main dengan hook pre-commit Git untuk mendeteksi program yang diperiksa dengan kesalahan sintaks. (Entah bagaimana saya hanya berhasil melakukan komit buruk.) Jadi sebagai program pengujian saya secara acak menuliskan ini:

use strict;
use warnings;

Syntax error!

exit 0;

Namun, itu mengkompilasi dan mengeksekusi tanpa peringatan, dan tingkat kesalahan adalah nol saat keluar. Bagaimana sintaksis yang valid ini?

Bill Ruppert
sumber
121
Apakah Anda baru saja membuktikan bahwa mengetik kata-kata acak ke dalam perl menghasilkan program yang berfungsi ??!?!?!?!
Peter M
10
@PeterM Kata-kata yang hampir tidak acak. Saya membuktikan saya tidak cukup tahu tentang sintaks Perl. Sekarang saya tahu lebih banyak.
Bill Ruppert
10
Anda mungkin ingin no indirectmenghentikan hal-hal tersebut agar tidak terjadi
LeoNerd
@LeoNerd Terima kasih atas tipnya!
Bill Ruppert
1
Ini adalah pertanyaan perl paling terkenal yang pernah ada. Bahkan lebih baik dari cuplikan Schwartz :whatever / 25 ; # / ; die "this dies!";
jm666

Jawaban:

540

Perl memiliki sintaksis yang disebut "notasi metode tidak langsung". Itu memungkinkan

Foo->new($bar)

ditulis sebagai

new Foo $bar

Jadi itu berarti

Syntax error ! exit 0;

sama dengan

error->Syntax(! exit 0);

atau

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

Tidak hanya sintaks yang valid, itu tidak menghasilkan kesalahan run-time karena hal pertama yang dilakukan adalah exit(0).

ikegami
sumber
1
@Hassan, Kenapa? Itu diikuti oleh ekspresi.
ikegami
3
Sejauh ini saya membacanya sebagai "Sintaks kesalahan! Keluar 0;", tetapi saya tidak memikirkan permohonan tidak langsung. Menghabiskan banyak waktu melupakan itu!
Bill Ruppert
6
@Hassan, Pikirkan seperti ini, !exit(0)tidak bisa lebih dari kesalahan ketik !$xkarena tidak ada yang diketik.
ikegami
11
@Hassan, Bahasa ini memiliki tipe. Secara khusus, nilai memiliki tipe. Operator dan kapal selam tidak dibatasi untuk mengembalikan jenis nilai tertentu. Ini ternyata sangat berguna dengan sedikit biaya (terima kasih atas peringatan).
ikegami
6
@Nawaz, Ini sebenarnya cukup populer. Ini digunakan oleh semua orang yang membangun objek di Java dan C ++, dan sejumlah besar programmer Perl yang menggunakan new Classdan print $fh ...bukannya Class->new(...)dan $fh->print(...). Saya akan memberi Anda bahwa itu menyebabkan pesan kesalahan aneh, meskipun
ikegami
112

Saya tidak tahu mengapa, tapi inilah yang dibuat Perl:

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

Tampaknya pengurai mengira Anda memanggil metode Syntaxpada- errorobjek ... Aneh memang!

pavel
sumber
3
Itu sintaks metode panggilan tidak langsung. Ini (semacam) bekerja di sini karena exit(0)dievaluasi terlebih dahulu, membuat program keluar sebelum mencoba meneruskan hasilnya 'error'->Syntax().
duskwuff -inactive-
6
Perl tampaknya menganggap "sintaks tidak langsung (objek)", biasanya digunakan seperti new ClassbukanClass->new() . Untuk memanggil metode Syntax, exitfungsi dijalankan, sehingga kesalahan run-time tidak pernah terjadi.
amon
118
Selamat. Anda menemukan sebuah program di mana Anda perlu menambahkan semi-kolon agar kompilasi gagal.
massa
use strict; use warnings; error->Syntax(! print "hi"); Hasil: Sintaks Ok pada perl -MO = Deparse juga, tetapi dengan use warnings itu mungkin harus mengatakan sesuatu karena dapat mengetahui bahwa itu tidak dimuat. Alih-alih itu melempar kesalahan runtime "Tidak dapat menemukan metode objek ..".
53

Alasan Anda tidak mendapatkan kesalahan adalah karena kode yang dieksekusi pertama kali adalah

exit(0);

Karena Anda tidak memiliki titik koma di baris pertama:

Syntax error!

Compiler akan menebak (secara tidak benar) bahwa ini adalah panggilan subrutin dengan notoperator !dilemparkan. Ia kemudian akan menjalankan argumen untuk subrutin ini, yang kebetulan merupakanexit(0) , di mana titik keluar program dan errorlevel set ke 0. Tidak ada lagi dijalankan , jadi tidak ada lagi kesalahan runtime yang dilaporkan.

Anda akan melihat bahwa jika Anda mengubah exit(0)sesuatu seperti print "Hello world!"Anda memang mendapatkan kesalahan:

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

dan tingkat kesalahan Anda akan diatur:

> echo %errorlevel%
255
TLP
sumber
7
>The compiler will guess (incorrectly) Kompiler tidak dapat melakukan hal yang salah.
Liam Laverty
14
@LiamLaverty Ya, itu bisa. Dapat menebak dengan salah apa yang dimaksud manusia.
TLP
4
Manusia adalah yang salah dalam persamaan. Kompilator hanya bisa "benar" atau "rusak". Itu tidak mendapatkan pendapat tentang definisi bahasa atau niat pengguna.
Liam Laverty
4
@LiamLaverty Ini akan menjadi kompiler yang cukup rapi jika bisa menebak niat pengguna dalam kasus ini, ya. Oleh karena itu, kompiler tidak dapat menebak dengan benar. Anda mungkin melakukan beberapa analisis jargon teknis dari pernyataan saya, yang, saya dapat tambahkan, cara yang salah untuk membacanya.
TLP
Bukankah itu penerjemah? ;-)
Rikki
33

Seperti disebutkan di atas ini disebabkan oleh notasi pemanggilan metode tidak langsung. Anda dapat memperingatkan ini:

use strict;
use warnings;
no indirect;

Syntax error!

exit 0;

Menghasilkan:

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

Ini membutuhkan modul CPAN tidak langsung .

Anda juga dapat menggunakan no indirect "fatal";untuk menyebabkan program mati (inilah yang saya lakukan)

Mark Fowler
sumber
8

Coba Perl 6 , tampaknya memenuhi harapan Anda lebih mudah:

===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
sumber
1

Dalam hal ini kertas , kami bertujuan untuk menjawab masalah terbuka lama di komunitas bahasa pemrograman: apakah mungkin untuk cat smear di dinding tanpa menciptakan Perl valid?

TLDR; Hampir tidak

Holli
sumber
Saya suka itu. Saya mungkin harus memindai dalam beberapa gambar.
Bill Ruppert