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?
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:
Ở 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:
Ví dụ, đưa ra ngữ pháp này:
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:
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ư yacc
hoặ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 ANTLR
cô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.
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:
+ 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 .
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:
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.
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)