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 foo
dengan argumen a
. Atau, bisa juga mendeklarasikan a
sebagai 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 foo
dapat diurai dengan jelas sebagai nama jenis. Ini mungkin bukan kode yang valid jika kelas foo
belum 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_declarator
yang salah?
Jawaban:
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.
sumber