Apa perbedaan antara penguraian LL dan LR?


Jawaban:


484

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:

  1. Prediksi : Berdasarkan nonterminal paling kiri dan sejumlah tokenead looken, pilih produksi mana yang harus diterapkan untuk mendekati string input.
  2. Cocokkan : Cocokkan simbol terminal yang ditebak paling kiri dengan simbol input yang tidak dikonsumsi paling kiri.

Sebagai contoh, diberikan tata bahasa ini:

  • S → E
  • E → T + E
  • E → T
  • T → 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:

  1. Shift : Tambahkan token input berikutnya ke buffer untuk dipertimbangkan.
  2. Mengurangi : Mengurangi koleksi terminal dan nonterminals dalam buffer ini kembali ke beberapa nonterminal dengan membalikkan produksi.

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 yaccatau bison. Parser LL juga memiliki banyak rasa (termasuk LL (*), yang digunakan oleh ANTLRalat 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.


40
Slide kuliah Anda sangat fenomenal, mudah penjelasan yang paling menyenangkan yang pernah saya lihat :) Ini adalah jenis hal yang benar-benar memicu minat.
kizzx2

1
Saya harus mengomentari slide juga! Mengalami mereka semua sekarang. Banyak membantu! Terima kasih!
kornfridge

Benar-benar menikmati slide juga. Saya kira Anda tidak dapat memposting versi non-Windows file proyek (dan file scanner.l, untuk pp2)? :)
Erik P.

1
Satu hal yang saya dapat berkontribusi untuk jawaban ringkasan Matt yang sangat baik adalah bahwa tata bahasa apa pun yang dapat diuraikan oleh parser LL (k) (yaitu, melihat ke depan terminal "k" untuk memutuskan tindakan parse berikutnya) dapat diurai oleh LR ( 1) pengurai. Ini memberikan satu petunjuk pada kekuatan luar biasa dari LR parsing atas parsing LL. Sumber: kursus kompiler di UCSC yang diajarkan oleh Dr. F. DeRemer, pencipta parser LALR ().
JoGusto

1
Sumber daya luar biasa! Terima kasih telah menyediakan slide, selebaran, proyek juga.
P. Hinker

58

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:

pohon biner dari suatu 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 .


9

LL menggunakan top-down, sedangkan LR menggunakan pendekatan bottom-up.

Jika Anda menguraikan bahasa pemrograman:

  • LL melihat kode sumber, yang berisi fungsi, yang berisi ekspresi.
  • LR melihat ekspresi, yang merupakan fungsi, yang menghasilkan sumber lengkap.

6

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.


0

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)

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.