Apa yang membuat Java lebih mudah diurai daripada C?

90

Saya mengetahui fakta bahwa tata bahasa C dan C ++ peka konteks , dan khususnya Anda memerlukan "retasan lexer" di C. Di sisi lain, saya mendapat kesan bahwa Anda dapat mengurai Java hanya dengan 2 token prospek, meskipun terdapat banyak kesamaan antara kedua bahasa.

Apa yang harus Anda ubah tentang C agar lebih mudah untuk diurai?

Saya bertanya karena semua contoh yang pernah saya lihat tentang sensitivitas konteks C secara teknis diperbolehkan tetapi sangat aneh. Sebagai contoh,

foo (a);

bisa memanggil fungsi void foodengan argumen a. Atau, bisa juga mendeklarasikan asebagai objek bertipe foo, tetapi Anda bisa dengan mudah menyingkirkan tanda kurung. Sebagian, keanehan ini terjadi karena aturan produksi "deklarator langsung" untuk tata bahasa C memenuhi tujuan ganda mendeklarasikan fungsi dan variabel.

Di sisi lain, tata bahasa Java memiliki aturan produksi terpisah untuk deklarasi variabel dan deklarasi fungsi. Jika Anda menulis

foo a;

maka Anda tahu itu adalah deklarasi variabel dan foodapat diurai dengan jelas sebagai nama jenis. Ini mungkin bukan kode yang valid jika kelas foobelum ditentukan di suatu tempat dalam cakupan saat ini, tetapi itu adalah tugas untuk analisis semantik yang dapat dilakukan di jalur kompilator nanti.

Saya pernah melihat yang mengatakan bahwa C sulit diurai karena typedef, tetapi Anda juga dapat mendeklarasikan tipe Anda sendiri di Java. Selain itu, aturan tata bahasa C mana direct_declaratoryang salah?

korrok
sumber
7
Pertanyaan keren. Mungkin terlalu luas atau terutama beropini.
asteri
37
Ini adalah pertanyaan yang valid tentang parser dan satu-satunya hal yang luas atau berdasarkan opini tentang itu adalah beberapa kalimat terakhir (yang mungkin harus dihilangkan atau diubah). Berhenti dengan suara dekat.
R .. GitHub STOP HELPING ICE
1
Saya mengedit pertanyaan sesuai, terima kasih untuk @R .. atas umpan baliknya.
korrok
3
Hampir setiap bahasa komputer (standar) peka konteks ; Anda tidak dapat mendeklarasikan variabel dengan satu jenis, dan menyalahgunakannya dalam sebagian besar bahasa . Itu berbeda dengan "semua tata bahasa untuk bahasa" adalah peka konteks; kebanyakan orang membangun parser membuat parser bebas konteks (atau bahkan lebih ketat), dan kemudian menggunakan hacks di luar parser untuk memeriksa properti bebas konteks.
Ira Baxter
1
@IraBaxter Saya tidak akan menyebutnya "hacks". Memisahkan masalah menjadi dua tampaknya hal yang masuk akal untuk dilakukan, karena penguraian bahasa peka konteks tidak dapat dilakukan secara efisien (dan bahkan penguraian bahasa bebas konteks tidak efisien, dan itulah mengapa kami umumnya membatasi pada subset bahasa bebas konteks) . Analisis parse + statis bebas konteks untuk memeriksa hanya properti peka konteks melalui AST, itu adalah hal yang wajar untuk dilakukan.
Bakuriu

Jawaban:

76

Parsing C ++ semakin sulit. Parsing Java menjadi sama sulitnya.

Lihat jawaban SO ini membahas mengapa C (dan C ++) "sulit" untuk diurai . Ringkasan singkatnya adalah bahwa tata bahasa C dan C ++ secara inheren ambigu; mereka akan memberi Anda beberapa parsing dan Anda harus menggunakan konteks untuk menyelesaikan ambiguitas. Orang-orang kemudian membuat kesalahan dengan menganggap Anda harus menyelesaikan ambiguitas saat Anda mengurai; tidak demikian, lihat di bawah. Jika Anda bersikeras untuk menyelesaikan ambiguitas saat Anda mengurai, parser Anda menjadi lebih rumit dan lebih sulit untuk dibuat; tetapi kerumitan itu adalah luka yang ditimbulkan sendiri.

IIRC, tata bahasa LALR (1) Java 1.4 yang "jelas" tidak ambigu, jadi "mudah" untuk diurai. Saya tidak begitu yakin bahwa Jawa modern tidak memiliki setidaknya ambiguitas lokal jarak jauh; selalu ada masalah dalam memutuskan apakah "... >>" menutup dua templat atau merupakan "operator shift kanan". Saya menduga Java modern tidak lagi mengurai dengan LALR (1) .

Tapi kita bisa melewati masalah penguraian dengan menggunakan pengurai yang kuat (atau pengurai lemah dan peretasan kumpulan konteks seperti yang kebanyakan dilakukan oleh C dan C ++ sekarang), untuk kedua bahasa. C dan C ++ memiliki komplikasi tambahan karena memiliki preprocessor; ini lebih rumit dalam praktiknya daripada yang terlihat. Salah satu klaimnya adalah bahwa pengurai C dan C ++ sangat sulit sehingga harus ditulis dengan tangan. Itu tidak benar; Anda bisa membuat parser Java dan C ++ dengan baik dengan generator parser GLR.

Tetapi penguraian bukanlah masalah yang sebenarnya.

Setelah Anda mengurai, Anda akan ingin melakukan sesuatu dengan pohon AST / parse. Dalam praktiknya, Anda perlu tahu, untuk setiap pengenal, apa definisinya dan di mana ia digunakan ("resolusi nama dan jenis", sembarangan, membuat tabel simbol). Ini ternyata menjadi pekerjaan BANYAK lebih banyak daripada mendapatkan hak parser, diperparah oleh pewarisan, antarmuka, overloading dan template, dan dibingungkan oleh fakta bahwa semantik untuk semua ini ditulis dalam bahasa alami informal yang tersebar di puluhan hingga ratusan halaman dari standar bahasa. C ++ sangat buruk di sini. Java 7 dan 8 menjadi sangat buruk dari sudut pandang ini. (Dan tabel simbol bukanlah semua yang Anda butuhkan; lihat bio saya untuk esai yang lebih panjang tentang "Life After Parsing").

Kebanyakan orang berjuang dengan bagian parsing murni (seringkali tidak pernah menyelesaikan; periksa SO itu sendiri untuk banyak, banyak pertanyaan tentang bagaimana membangun pengurai yang berfungsi untuk bahasa yang sebenarnya), sehingga mereka tidak pernah melihat kehidupan setelah penguraian. Dan kemudian kita mendapatkan teorema rakyat tentang apa yang sulit diuraikan dan tidak ada sinyal tentang apa yang terjadi setelah tahap itu.

Memperbaiki sintaks C ++ tidak akan membawa Anda kemana-mana.

Mengenai mengubah sintaks C ++: Anda akan merasa perlu menambal banyak tempat untuk menangani keragaman lokal dan ambiguitas nyata dalam tata bahasa C ++ apa pun. Jika Anda bersikeras, daftar berikut mungkin bisa menjadi tempat awal yang baik . Saya berpendapat tidak ada gunanya melakukan ini jika Anda bukan komite standar C ++; jika Anda melakukannya, dan membangun kompiler menggunakan itu, tidak ada orang waras yang akan menggunakannya. Ada terlalu banyak investasi dalam aplikasi C ++ yang ada untuk ditukar demi kenyamanan orang yang membangun parser; selain itu, rasa sakit mereka telah berakhir dan pengurai yang ada berfungsi dengan baik.

Anda mungkin ingin menulis pengurai Anda sendiri. Baiklah tidak apa apa; hanya saja, jangan berharap seluruh komunitas mengizinkan Anda mengubah bahasa yang harus mereka gunakan untuk memudahkan Anda. Mereka semua ingin lebih mudah bagi mereka, dan itu menggunakan bahasa yang didokumentasikan dan diterapkan.

Ira Baxter
sumber
Jawaban yang bagus. Lihat juga D dan C +, yang mencoba menyelesaikan beberapa masalah ini. s / content / contend /
david.pfx
3
Saya telah membaca Life After Parsing sebelumnya dan menemukan bahwa itu benar-benar membuka mata; ini menjelaskan kepada saya bahwa ada lebih banyak pekerjaan dalam analisis semantik (resolusi nama / jenis, ...) daripada yang ada dalam penguraian. Saya tidak mencoba mengubah sintaks bahasa apa pun. Saya benar- benar ingin memahami apa saja properti dari suatu bahasa di mana Anda dapat melakukan analisis sintaksis terlebih dahulu, baru kemudian analisis semantik. C bukanlah bahasa seperti itu (membutuhkan lexer hack); Saya selalu berpikir bahwa Java dulu dan saya ingin tahu mengapa.
korrok
1
@Korrok: baca jawaban saya tentang membangun Java / C ++ dengan parser GLR. Anda tidak memerlukan retasan lexer apa pun . Jadi, perbedaannya ada pada pikiran orang yang menggunakan teknologi parsing yang salah. ... Memang, membuat front end C ++ lengkap (esp. C ++ 14, yang telah kami lakukan) lebih sulit daripada menggunakan Java8, tetapi keduanya sulit (dalam hal upaya dan perhatian pada detail) dan penguraian adalah bagian termudah.
Ira Baxter
1
Saya setuju tentang "Life after Parsing" Anda: misalnya resolusi overload di C # dapat menyandikan masalah 3-SAT dan dengan demikian NP-hard.
Jörg W Mittag