Adakah yang bisa memberi saya contoh sederhana parsing LL versus parsing LR?
Adakah yang bisa memberi saya contoh sederhana parsing LL versus parsing LR?
Jawaban:
Pada level tinggi, perbedaan antara parsing LL dan parsing LR adalah bahwa parser LL dimulai pada simbol awal dan mencoba menerapkan produksi untuk sampai pada string target, sedangkan parser LR mulai pada string target dan mencoba untuk kembali pada awal simbol.
LL parse adalah derivasi dari kiri ke kanan, paling kiri. Yaitu, kami mempertimbangkan simbol input dari kiri ke kanan dan berusaha membangun derivasi paling kiri. Ini dilakukan dengan mulai dari simbol awal dan berulang kali melebarkan nonterminal paling kiri sampai kita tiba di string target. LR parse adalah derivasi dari kiri ke kanan, paling kanan, artinya kita memindai dari kiri ke kanan dan berusaha membangun derivasi paling kanan. Pengurai secara terus menerus mengambil substring dari input dan mencoba untuk membalikkannya kembali ke nonterminal.
Selama parse LL, parser terus memilih antara dua tindakan:
Sebagai contoh, diberikan tata bahasa ini:
int
Kemudian diberi string int + int + int
, parser LL (2) (yang menggunakan dua token lookahead) akan menguraikan string sebagai berikut:
Production Input Action
---------------------------------------------------------
S int + int + int Predict S -> E
E int + int + int Predict E -> T + E
T + E int + int + int Predict T -> int
int + E int + int + int Match int
+ E + int + int Match +
E int + int Predict E -> T + E
T + E int + int Predict T -> int
int + E int + int Match int
+ E + int Match +
E int Predict E -> T
T int Predict T -> int
int int Match int
Accept
Perhatikan bahwa dalam setiap langkah kita melihat simbol paling kiri dalam produksi kita. Jika itu terminal, kami cocokkan, dan jika itu nonterminal, kami memperkirakan akan seperti apa dengan memilih salah satu aturan.
Dalam parser LR, ada dua tindakan:
Sebagai contoh, parser LR (1) (dengan satu token lookahead) mungkin menguraikan string yang sama sebagai berikut:
Workspace Input Action
---------------------------------------------------------
int + int + int Shift
int + int + int Reduce T -> int
T + int + int Shift
T + int + int Shift
T + int + int Reduce T -> int
T + T + int Shift
T + T + int Shift
T + T + int Reduce T -> int
T + T + T Reduce E -> T
T + T + E Reduce E -> T + E
T + E Reduce E -> T + E
E Reduce S -> E
S Accept
Dua algoritma penguraian yang Anda sebutkan (LL dan LR) diketahui memiliki karakteristik yang berbeda. Parser LL cenderung lebih mudah untuk menulis dengan tangan, tetapi mereka kurang kuat daripada parser LR dan menerima set tata bahasa yang jauh lebih kecil daripada parser LR. Parser LR memiliki banyak rasa (LR (0), SLR (1), LALR (1), LR (1), IELR (1), GLR (0), dll.) Dan jauh lebih kuat. Mereka juga cenderung memiliki jauh lebih kompleks dan hampir selalu dihasilkan oleh alat seperti yacc
atau bison
. Parser LL juga memiliki banyak rasa (termasuk LL (*), yang digunakan oleh ANTLR
alat ini), meskipun dalam praktiknya LL (1) adalah yang paling banyak digunakan.
Sebagai plug yang tidak tahu malu, jika Anda ingin mempelajari lebih lanjut tentang parsing LL dan LR, saya baru saja selesai mengajar kursus kompiler dan memiliki beberapa handout dan slide kuliah tentang penguraian pada situs web kursus. Saya akan dengan senang hati menguraikan salah satu dari mereka jika Anda pikir itu akan berguna.
Josh Haberman dalam artikelnya LL dan LR Parsing Demystified mengklaim bahwa LL parsing secara langsung sesuai dengan Notasi Polandia , sedangkan LR sesuai dengan Notasi Polandia Reverse . Perbedaan antara PN dan RPN adalah urutan melintasi pohon biner dari persamaan:
+ 1 * 2 3 // Polish (prefix) expression; pre-order traversal.
1 2 3 * + // Reverse Polish (postfix) expression; post-order traversal.
Menurut Haberman, ini menggambarkan perbedaan utama antara parser LL dan LR:
Perbedaan utama antara bagaimana parser LL dan LR beroperasi adalah bahwa parser LL menghasilkan traversal pre-order dari pohon parse dan parser LR menghasilkan traversal post-order.
Untuk penjelasan mendalam, contoh dan kesimpulan, periksa artikel Haberman .
LL menggunakan top-down, sedangkan LR menggunakan pendekatan bottom-up.
Jika Anda menguraikan bahasa pemrograman:
Parsing LL cacat, jika dibandingkan dengan LR. Berikut ini adalah tata bahasa yang merupakan mimpi buruk bagi generator parser LL:
Goal -> (FunctionDef | FunctionDecl)* <eof>
FunctionDef -> TypeSpec FuncName '(' [Arg/','+] ')' '{' '}'
FunctionDecl -> TypeSpec FuncName '(' [Arg/','+] ')' ';'
TypeSpec -> int
-> char '*' '*'
-> long
-> short
FuncName -> IDENTIFIER
Arg -> TypeSpec ArgName
ArgName -> IDENTIFIER
FunctionDef terlihat persis seperti FunctionDecl hingga tanda ';' atau '{' ditemukan.
LL parser tidak dapat menangani dua aturan sekaligus, jadi ia harus memilih FunctionDef atau FunctionDecl. Tetapi untuk mengetahui mana yang benar itu harus mencari ''; atau '{'. Pada waktu analisis tata bahasa, lookahead (k) tampaknya tidak terbatas. Pada waktu penguraian itu terbatas, tetapi bisa jadi besar.
Pengurai LR tidak harus melihat ke depan, karena dapat menangani dua aturan secara bersamaan. Jadi generator parser LALR (1) dapat menangani tata bahasa ini dengan mudah.
Diberikan kode input:
int main (int na, char** arg);
int main (int na, char** arg)
{
}
Pengurai LR dapat menguraikan
int main (int na, char** arg)
tanpa peduli aturan apa yang diakui sampai bertemu ';' atau a '{'.
Pengurai LL digantung di 'int' karena perlu mengetahui aturan mana yang diakui. Karena itu ia harus mencari 'a'; atau '{'.
Mimpi buruk lain untuk parser LL ditinggalkan rekursi dalam tata bahasa. Rekursi kiri adalah hal normal dalam tata bahasa, tidak ada masalah untuk generator parser LR, tetapi LL tidak bisa mengatasinya.
Jadi, Anda harus menulis tata bahasa Anda dengan cara yang tidak wajar dengan LL.
Left Most derivation Contoh: Grammar G yang bebas konteks memiliki produksinya
z → xXY (Aturan: 1) X → Ybx (Aturan: 2) Y → bY (Aturan: 3) Y → c (Aturan: 4)
Hitunglah String w = 'xcbxbc' dengan derivasi paling kiri.
z ⇒ xXY (Aturan: 1) ⇒ xYbxY (Aturan: 2) ⇒ xcbxY (Aturan: 4) ⇒ xcbxbY (Aturan: 3) ⇒ xcbxbc (Aturan: 4)
Contoh Derivasi Paling Benar: K → aKK (Aturan: 1) A → b (Aturan: 2)
Hitunglah String w = 'aababbb' dengan derivasi paling kanan.
K ⇒ aKK (Aturan: 1) ⇒ aKb (Aturan: 2) ⇒ aaKKb (Aturan: 1) ⇒ aaKaKKb (Aturan: 1) ⇒ aaKaKbb (Aturan: 2) ⇒ aaKabbb (Aturan: 2) ⇒ aababbb (Peraturan: 2)