Sự khác biệt giữa phân tích LL và LR là gì?


225

Bất cứ ai cũng có thể cho tôi một ví dụ đơn giản về phân tích LL so với phân tích cú pháp LR?

Câu trả lời:


483

Ở mức độ cao, sự khác biệt giữa phân tích cú pháp LL và phân tích cú pháp LR là các trình phân tích cú pháp LL bắt đầu ở biểu tượng bắt đầu và cố gắng áp dụng các sản phẩm để đến chuỗi đích, trong khi các trình phân tích cú pháp LR bắt đầu ở chuỗi đích và cố gắng quay lại từ đầu Biểu tượng.

Một phân tích LL là một dẫn xuất từ ​​trái sang phải, ngoài cùng bên trái. Đó là, chúng tôi xem xét các ký hiệu đầu vào từ trái sang phải và cố gắng xây dựng một đạo hàm ngoài cùng bên trái. Điều này được thực hiện bằng cách bắt đầu tại biểu tượng bắt đầu và liên tục mở rộng ra phần ngoài cùng bên trái cho đến khi chúng ta đến chuỗi mục tiêu. Phân tích cú pháp LR là một đạo hàm từ trái sang phải, phải, có nghĩa là chúng ta quét từ trái sang phải và cố gắng xây dựng một đạo hàm ngoài cùng bên phải. Trình phân tích cú pháp liên tục chọn một chuỗi con của đầu vào và cố gắng đảo ngược nó trở lại một nonterminal.

Trong quá trình phân tích LL, trình phân tích cú pháp liên tục chọn giữa hai hành động:

  1. Dự đoán : Dựa trên nonterminal ngoài cùng bên trái và một số mã thông báo tìm kiếm, chọn sản phẩm nào sẽ được áp dụng để đến gần hơn với chuỗi đầu vào.
  2. Khớp : Ghép biểu tượng đầu cuối đoán bên trái với biểu tượng đầu vào không nhìn thấy ngoài cùng bên trái.

Ví dụ, đưa ra ngữ pháp này:

  • S → E
  • E → T + E
  • E → T
  • T → int

Sau đó được cung cấp chuỗi int + int + int, trình phân tích cú pháp LL (2) (sử dụng hai mã thông báo của lookahead) sẽ phân tích chuỗi như sau:

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

Lưu ý rằng trong mỗi bước chúng ta nhìn vào biểu tượng ngoài cùng bên trái trong sản xuất của chúng tôi. Nếu đó là một thiết bị đầu cuối, chúng tôi khớp với nó và nếu đó là một thiết bị đầu cuối, chúng tôi dự đoán nó sẽ là gì bằng cách chọn một trong các quy tắc.

Trong trình phân tích cú pháp LR, có hai hành động:

  1. Shift : Thêm mã thông báo tiếp theo của đầu vào vào bộ đệm để xem xét.
  2. Giảm : Giảm một tập hợp các thiết bị đầu cuối và nonterminals trong bộ đệm này trở lại một số nonterminal bằng cách đảo ngược một sản xuất.

Ví dụ, một trình phân tích cú pháp LR (1) (với một mã thông báo của lookahead) có thể phân tích chuỗi tương tự như sau:

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

Hai thuật toán phân tích cú pháp mà bạn đề cập (LL và LR) được biết là có các đặc điểm khác nhau. Trình phân tích LL có xu hướng dễ viết bằng tay hơn, nhưng chúng kém mạnh hơn trình phân tích cú pháp LR và chấp nhận một bộ ngữ pháp nhỏ hơn nhiều so với trình phân tích cú pháp LR. Các trình phân tích cú pháp LR có nhiều hương vị (LR (0), SLR (1), LALR (1), LR (1), ClassifiedR (1), GLR (0), v.v.) và mạnh mẽ hơn nhiều. Chúng cũng có xu hướng phức tạp hơn nhiều và hầu như luôn được tạo ra bởi các công cụ như yacchoặc bison. Trình phân tích LL cũng có nhiều hương vị (bao gồm LL (*), được sử dụng bởi ANTLRcông cụ), mặc dù trong thực tế LL (1) được sử dụng rộng rãi nhất.

Là một người không biết xấu hổ, nếu bạn muốn tìm hiểu thêm về phân tích LL và LR, tôi vừa hoàn thành việc dạy một khóa biên dịch và có một số hướng dẫn và các bài giảng về phân tích cú pháp trên trang web của khóa học. Tôi rất vui lòng giải thích về bất kỳ ai trong số họ nếu bạn nghĩ rằng nó sẽ hữu ích.


40
Các slide bài giảng của bạn là phi thường, dễ dàng là lời giải thích thú vị nhất mà tôi đã thấy :) Đây là loại điều thực sự gây hứng thú.
kizzx2

1
Tôi phải bình luận về các slide là tốt! Đi qua tất cả chúng bây giờ. Giúp đỡ rất nhiều! Cảm ơn!
kornfridge

Thực sự thích các slide quá. Tôi không cho rằng bạn có thể đăng phiên bản không phải Windows của các tệp dự án (và tệp Scanner.l cho pp2)? :)
Erik P.

1
Một điều tôi có thể đóng góp cho câu trả lời tóm tắt tuyệt vời của Matt là bất kỳ ngữ pháp nào có thể được phân tích cú pháp bởi trình phân tích cú pháp LL (k) (nghĩa là nhìn về phía trước các đầu cuối "k" để quyết định hành động phân tích cú pháp tiếp theo) có thể được phân tích cú pháp bởi một LR ( 1) trình phân tích cú pháp. Điều này cho người ta một gợi ý về sức mạnh đáng kinh ngạc của việc phân tích cú pháp qua phân tích LL. Nguồn: khóa học trình biên dịch tại UCSC được giảng dạy bởi Tiến sĩ F. DeRemer, người tạo ra các trình phân tích cú pháp LALR ().
JoGusto 2/2/2015

1
Tài nguyên tuyệt vời! Cảm ơn đã cung cấp slide, bản phát, dự án là tốt.
P. Hinker

58

Josh Haberman trong bài viết của mình LL và LR Parsing Demystified tuyên bố rằng LL phân tích trực tiếp tương ứng với Ký hiệu Ba Lan , trong khi LR tương ứng với Ký hiệu đảo ngược Ba Lan . Sự khác biệt giữa PN và RPN là thứ tự đi qua cây nhị phân của phương trình:

cây nhị phân của một phương trình

+ 1 * 2 3  // Polish (prefix) expression; pre-order traversal.
1 2 3 * +  // Reverse Polish (postfix) expression; post-order traversal.

Theo Haberman, điều này minh họa sự khác biệt chính giữa các trình phân tích cú pháp LL và LR:

Sự khác biệt chính giữa cách trình phân tích cú pháp LL và LR hoạt động là trình phân tích cú pháp LL tạo ra một giao dịch đặt hàng trước của cây phân tích cú pháp và trình phân tích cú pháp LR đưa ra một giao dịch theo thứ tự sau.

Để được giải thích sâu hơn, các ví dụ và kết luận hãy xem bài viết của Haberman .


9

LL sử dụng từ trên xuống, trong khi LR sử dụng phương pháp từ dưới lên.

Nếu bạn phân tích một ngôn ngữ progamming:

  • LL thấy một mã nguồn, chứa các hàm, chứa biểu thức.
  • LR thấy biểu thức, thuộc về các hàm, kết quả là nguồn đầy đủ.

6

Phân tích LL bị khuyết tật, khi so sánh với LR. Đây là một ngữ pháp là một cơn ác mộng đối với trình tạo trình phân tích cú pháp 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 trông giống hệt như FunctionDecl cho đến khi ';' hoặc '{' là bắt gặp.

Trình phân tích cú pháp LL không thể xử lý hai quy tắc cùng một lúc, do đó, nó phải chọn FunctionDef hoặc FunctionDecl. Nhưng để biết cái nào đúng thì phải tìm một ';' hoặc là '{'. Tại thời điểm phân tích ngữ pháp, lookahead (k) dường như là vô hạn. Tại thời điểm phân tích cú pháp là hữu hạn, nhưng có thể lớn.

Một trình phân tích cú pháp LR không phải tìm kiếm bởi vì nó có thể xử lý hai quy tắc cùng một lúc. Vì vậy, trình tạo trình phân tích cú pháp LALR (1) có thể xử lý ngữ pháp này một cách dễ dàng.

Cho mã đầu vào:

int main (int na, char** arg); 

int main (int na, char** arg) 
{

}

Một trình phân tích cú pháp LR có thể phân tích cú pháp

int main (int na, char** arg)

mà không quan tâm quy tắc nào được công nhận cho đến khi nó gặp một ';' hoặc '{'.

Trình phân tích cú pháp LL bị treo ở 'int' vì nó cần biết quy tắc nào được công nhận. Vì vậy, nó phải tìm kiếm một ';' hoặc là '{'.

Cơn ác mộng khác đối với các trình phân tích cú pháp LL là đệ quy trái trong một ngữ pháp. Đệ quy trái là một điều bình thường trong ngữ pháp, không có vấn đề gì đối với trình tạo trình phân tích cú pháp LR, nhưng LL không thể xử lý nó.

Vì vậy, bạn phải viết ngữ pháp của bạn một cách không tự nhiên với LL.


0

Ví dụ dẫn xuất trái nhất: Một ngữ pháp G không có ngữ cảnh có các sản phẩm

z → xXY (Quy tắc: 1) X → Ybx (Quy tắc: 2) Y → bY (Quy tắc: 3) Y → c (Quy tắc: 4)

Tính chuỗi w = 'xcbxbc' với hầu hết các đạo hàm trái.

z ⇒ xXY (Quy tắc: 1) xYbxY (Quy tắc: 2) ⇒ xcbxY (Quy tắc: 4) xcbxbY (Quy tắc: 3) xcbxbc (Quy tắc: 4)


Hầu hết các đạo hàm phải Ví dụ: K → aKK (Quy tắc: 1) A → b (Quy tắc: 2)

Tính chuỗi w = 'aababbb' với đạo hàm đúng nhất.

K ⇒ aKK (Quy tắc: 1) aKb (Quy tắc: 2) ⇒ aaKKb (Quy tắc: 1) aaKaKKb (Quy tắc: 1) ⇒ aaKaKbb (Quy tắc: 2) ⇒ aaKabbb (Quy tắc: 2)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.