Saya membaca tentang parser dan generator parser dan menemukan pernyataan ini di halaman parsing LR wikipedia:
Banyak bahasa pemrograman dapat diuraikan menggunakan beberapa variasi parser LR. Satu pengecualian penting adalah C ++.
Kenapa gitu? Apa properti khusus dari C ++ yang menyebabkannya tidak mungkin diurai dengan parser LR?
Menggunakan google, saya hanya menemukan bahwa C dapat diurai sempurna dengan LR (1) tetapi C ++ membutuhkan LR (∞).
c++
parsing
grammar
formal-languages
Riang
sumber
sumber
Jawaban:
Ada utas menarik tentang Lambda the Ultimate yang membahas tata bahasa LALR untuk C ++ .
Ini termasuk tautan ke tesis PhD yang mencakup diskusi tentang penguraian C ++, yang menyatakan bahwa:
Selanjutnya memberikan sejumlah contoh (lihat halaman 147 dari pdf).
Contohnya adalah:
berarti
Dibandingkan dengan:
berarti
(ekspresi yang dipisahkan koma).
Dua urutan token memiliki urutan awal yang sama tetapi pohon parse yang berbeda, yang tergantung pada elemen terakhir. Mungkin ada banyak token yang sewenang-wenang sebelum yang ambigu.
sumber
Parser LR tidak dapat menangani aturan tata bahasa yang ambigu, berdasarkan desain. (Membuat teori lebih mudah di tahun 1970-an ketika ide sedang dikerjakan).
C dan C ++ keduanya memungkinkan pernyataan berikut:
Ini memiliki dua parse yang berbeda:
Sekarang, Anda mungkin berpikir yang terakhir itu bodoh dan harus diabaikan. Sebagian besar akan setuju dengan Anda; Namun, ada kasus-kasus di mana ia mungkin memiliki efek samping (misalnya, jika multiply kelebihan beban). tapi bukan itu intinya. Intinya adalah ada yang dua mem-parsing yang berbeda, dan karena itu program dapat berarti hal yang berbeda tergantung pada bagaimana ini harus telah dipecah.
Kompiler harus menerima yang sesuai dalam keadaan yang sesuai, dan jika tidak ada informasi lain (misalnya, pengetahuan tentang tipe x) harus mengumpulkan keduanya untuk memutuskan nanti apa yang harus dilakukan. Jadi tata bahasa harus memungkinkan ini. Dan itu membuat tata bahasa ambigu.
Jadi parsing LR murni tidak bisa menangani ini. Juga tidak banyak generator parser lain yang tersedia secara luas, seperti Antlr, JavaCC, YACC, atau Bison tradisional, atau bahkan parser gaya PEG, yang digunakan dengan cara "murni".
Ada banyak kasus yang lebih rumit (sintaks templat parsing memerlukan tampilan acak, sedangkan LALR (k) dapat melihat ke depan di sebagian besar token k), tetapi hanya dibutuhkan satu sampel balik untuk menembak parsing LR murni (atau yang lain).
Kebanyakan parser C / C ++ nyata menangani contoh ini dengan menggunakan beberapa jenis parser deterministik dengan hack tambahan: mereka parsing terjalin dengan koleksi tabel simbol ... sehingga pada saat "x" ditemui, parser tahu jika x adalah tipe atau tidak, dan dengan demikian dapat memilih antara dua parse potensial. Tetapi parser yang melakukan ini tidak bebas konteks, dan parser LR (yang murni, dll.) Bebas konteks.
Satu dapat menipu, dan menambahkan pemeriksaan semantik waktu pengurangan aturan untuk parser LR untuk melakukan disambiguasi ini. (Kode ini seringkali tidak sederhana). Sebagian besar tipe parser lain memiliki beberapa cara untuk menambahkan pemeriksaan semantik di berbagai titik di parsing, yang dapat digunakan untuk melakukan ini.
Dan jika Anda cukup curang, Anda dapat membuat parser LR bekerja untuk C dan C ++. Orang-orang GCC melakukannya untuk sementara waktu, tetapi menyerahkannya untuk parsing kode tangan, saya pikir karena mereka menginginkan diagnostik kesalahan yang lebih baik.
Ada pendekatan lain, yang bagus dan bersih dan mem-parsing C dan C ++ dengan baik tanpa peretasan tabel simbol: parser GLR . Ini adalah parser bebas konteks penuh (memiliki lookahead efektif tak terbatas). Pengurai GLR hanya menerima kedua pengurai, menghasilkan "pohon" (sebenarnya grafik asiklik terarah yang sebagian besar seperti pohon) yang mewakili penguraian ambigu. Sebuah pascarsing pas dapat menyelesaikan ambiguitas.
Kami menggunakan teknik ini di ujung depan C dan C ++ untuk Perangkat Lunak DMS Reengineering Tookit kami (per Juni 2017 ini menangani C ++ 17 penuh dalam dialek MS dan GNU). Mereka telah digunakan untuk memproses jutaan baris sistem C dan C ++ besar, dengan parse lengkap, presisi yang menghasilkan AST dengan rincian lengkap dari kode sumber. (Lihat AST untuk parse yang paling menjengkelkan C ++. )
sumber
x * y
dan terkekeh - Sungguh menakjubkan betapa sedikit orang berpikir tentang ambiguitas kecil yang bagus seperti ini.Masalahnya tidak pernah didefinisikan seperti ini, padahal seharusnya menarik:
apa set modifikasi terkecil untuk tata bahasa C ++ yang diperlukan agar tata bahasa baru ini dapat diurai sempurna oleh parser yacc "non-konteks-gratis"? (hanya menggunakan satu 'retasan': disambiguasi ketik nama / pengidentifikasi, pengurai yang menginformasikan lexer dari setiap typedef / class / struct)
Saya melihat beberapa:
Type Type;
terlarang. Identifier yang dideklarasikan sebagai typename tidak bisa menjadi non-typename identifier (note yangstruct Type Type
tidak ambigu dan masih bisa diizinkan).Ada 3 jenis
names tokens
:types
: tipe builtin atau karena typedef / class / structMempertimbangkan fungsi templat sebagai token yang berbeda memecahkan
func<
ambiguitas. Jikafunc
nama fungsi templat, maka<
harus menjadi awal dari daftar parameter templat, jika tidak,func
adalah penunjuk fungsi dan<
operator pembanding.Type a(2);
adalah instantiasi objek.Type a();
danType a(int)
merupakan prototipe fungsi.int (k);
benar-benar dilarang, harus ditulisint k;
typedef int func_type();
dantypedef int (func_type)();
dilarang.Fungsi typedef harus berupa function pointer typedef:
typedef int (*func_ptr_type)();
rekursi templat terbatas pada 1024, jika tidak, maksimum yang ditingkatkan dapat diteruskan sebagai opsi ke kompiler.
int a,b,c[9],*d,(*f)(), (*g)()[9], h(char);
bisa juga dilarang, diganti denganint a,b,c[9],*d;
int (*f)();
int (*g)()[9];
int h(char);
satu baris per fungsi prototipe atau deklarasi fungsi pointer.
Alternatif yang sangat disukai adalah mengubah sintaks pointer fungsi yang mengerikan,
int (MyClass::*MethodPtr)(char*);
disinkronisasi ulang sebagai:
int (MyClass::*)(char*) MethodPtr;
ini koheren dengan operator pemeran
(int (MyClass::*)(char*))
typedef int type, *type_ptr;
bisa juga dilarang: satu baris per typedef. Dengan demikian akan menjaditypedef int type;
typedef int *type_ptr;
sizeof int
,sizeof char
,sizeof long long
Dan co. dapat dideklarasikan di setiap file sumber. Jadi, setiap file sumber yang menggunakan tipe iniint
harus dimulai dengan#type int : signed_integer(4)
dan
unsigned_integer(4)
akan dilarang di luar#type
arahan itu, ini akan menjadi langkah besar ke dalamsizeof int
ambiguitas bodoh yang ada di begitu banyak header C ++Kompiler yang mengimplementasikan C ++ yang disinkronisasi ulang akan, jika menemukan sumber C ++ yang menggunakan sintaks ambigu, memindahkan
source.cpp
jugaambiguous_syntax
folder, dan akan secara otomatis membuat terjemahan yang tidak ambigusource.cpp
sebelum mengkompilasinya.Silakan tambahkan sintaks C ++ ambigu Anda jika Anda tahu beberapa!
sumber
Seperti yang Anda lihat dalam jawaban saya di sini , C ++ berisi sintaksis yang tidak dapat diuraikan secara parser oleh parser LL atau LR karena tahap resolusi tipe (biasanya pasca parsing) mengubah urutan operasi , dan oleh karena itu bentuk dasar AST ( biasanya diharapkan disediakan oleh parse tahap pertama).
sumber
Saya pikir Anda cukup dekat dengan jawabannya.
LR (1) berarti penguraian dari kiri ke kanan hanya membutuhkan satu token untuk melihat-depan untuk konteks, sedangkan LR (∞) berarti pandangan ke depan yang tak terbatas. Artinya, parser harus mengetahui segala sesuatu yang datang untuk mengetahui di mana tempatnya sekarang.
sumber
Masalah "typedef" di C ++ dapat diuraikan dengan parser LALR (1) yang membuat tabel simbol saat parsing (bukan parser LALR murni). Masalah "templat" mungkin tidak dapat diselesaikan dengan metode ini. Keuntungan dari jenis LALR (1) parser ini adalah bahwa tata bahasa (ditunjukkan di bawah) adalah tata bahasa LALR (1) (tidak ada ambiguitas).
Input berikut dapat diuraikan tanpa masalah:
The LRSTAR generator parser membaca notasi tata bahasa di atas dan menghasilkan parser yang menangani para "typedef" masalah tanpa ambiguitas dalam pohon parse atau AST. (Pengungkapan: Saya orang yang menciptakan LRSTAR.)
sumber