Trong thiết kế trình biên dịch, tại sao phải loại bỏ đệ quy trong ngữ pháp? Tôi đang đọc rằng đó là bởi vì nó có thể gây ra một đệ quy vô hạn, nhưng nó có đúng với một ngữ pháp đệ quy đúng không?
Trong thiết kế trình biên dịch, tại sao phải loại bỏ đệ quy trong ngữ pháp? Tôi đang đọc rằng đó là bởi vì nó có thể gây ra một đệ quy vô hạn, nhưng nó có đúng với một ngữ pháp đệ quy đúng không?
Câu trả lời:
Ngữ pháp đệ quy còn lại không nhất thiết là một điều xấu. Các ngữ pháp này dễ dàng được phân tích cú pháp bằng cách sử dụng ngăn xếp để theo dõi các cụm từ đã được phân tích cú pháp, vì đó là trường hợp trong trình phân tích cú pháp LR .
Nhớ lại rằng một quy tắc đệ quy trái của một CF ngữ pháp có dạng:
Bất cứ khi nào một thiết bị đầu cuối mới được nhận bởi trình phân tích ngữ pháp (từ lexer), thiết bị đầu cuối này được đẩy lên trên ngăn xếp: thao tác này được gọi là dịch chuyển .
Mỗi khi phía bên phải của quy tắc được khớp bởi một nhóm các phần tử liên tiếp ở đầu ngăn xếp, nhóm này được thay thế bằng một phần tử duy nhất đại diện cho cụm từ mới được khớp. Sự thay thế này được gọi là giảm .
Với các ngữ pháp đệ quy đúng, ngăn xếp có thể phát triển vô hạn cho đến khi giảm bớt, do đó hạn chế đáng kể các khả năng phân tích cú pháp. Tuy nhiên, những cái đệ quy còn lại sẽ cho phép trình biên dịch tạo ra các mức giảm sớm hơn (thực tế là càng sớm càng tốt). Xem mục wikipedia để biết thêm thông tin.
Hãy xem xét quy tắc này:
example : 'a' | example 'b' ;
Bây giờ hãy xem xét một trình phân tích cú pháp LL đang cố gắng khớp một chuỗi không khớp như 'b'
quy tắc này. Vì 'a'
không khớp, nên nó sẽ cố khớp example 'b'
. Nhưng để làm như vậy, nó phải phù hợp example
... đó là những gì nó đã cố gắng làm ở nơi đầu tiên. Nó có thể bị kẹt khi cố gắng mãi mãi để xem liệu nó có thể khớp hay không, bởi vì nó luôn cố gắng khớp cùng một luồng mã thông báo theo cùng một quy tắc.
Để ngăn chặn điều đó, bạn sẽ phải phân tích cú pháp từ bên phải (điều này khá hiếm, theo như tôi đã thấy, và sẽ thực hiện đệ quy đúng vấn đề), hạn chế một cách giả tạo số lượng lồng cho phép hoặc khớp mã thông báo trước khi đệ quy bắt đầu để luôn có trường hợp cơ bản (cụ thể là, nơi tất cả các mã thông báo đã được sử dụng và vẫn không có kết quả khớp hoàn chỉnh). Vì quy tắc đệ quy đúng đã thực hiện quy tắc thứ ba, nên nó không có cùng một vấn đề.
(Bây giờ tôi biết câu hỏi này khá cũ, nhưng trong trường hợp những người khác có cùng câu hỏi ...)
Bạn đang hỏi trong bối cảnh của trình phân tích cú pháp gốc đệ quy? Ví dụ, đối với ngữ pháp expr:: = expr + term | term
, tại sao một cái gì đó như thế này (đệ quy trái):
// expr:: = expr + term
expr() {
expr();
if (token == '+') {
getNextToken();
}
term();
}
là có vấn đề, nhưng không phải điều này (phải đệ quy)?
// expr:: = term + expr
expr() {
term();
if (token == '+') {
getNextToken();
expr();
}
}
Có vẻ như cả hai phiên bản của expr()
cuộc gọi mình. Nhưng sự khác biệt quan trọng là bối cảnh - tức là mã thông báo hiện tại khi cuộc gọi đệ quy đó được thực hiện.
Trong trường hợp đệ quy bên trái, expr()
liên tục gọi chính nó với cùng một mã thông báo và không có tiến triển nào được thực hiện. Trong trường hợp đệ quy đúng, nó tiêu thụ một số đầu vào trong lệnh gọi term()
và mã thông báo PLUS trước khi thực hiện cuộc gọi đến expr()
. Vì vậy, tại thời điểm này, cuộc gọi đệ quy có thể gọi hạn và sau đó kết thúc trước khi đạt lại kiểm tra if.
Ví dụ: hãy xem xét phân tích cú pháp 2 + 3 + 4. Trình phân tích cú pháp đệ quy bên trái gọi expr()
vô hạn trong khi bị mắc kẹt trên mã thông báo đầu tiên, trong khi trình phân tích đệ quy bên phải tiêu thụ "2 +" trước khi gọi expr()
lại. Cuộc gọi thứ hai expr()
khớp với "3 +" và các cuộc gọi expr()
chỉ còn 4 cuộc gọi . Cả 4 khớp với một thuật ngữ và phân tích cú pháp chấm dứt mà không có thêm bất kỳ cuộc gọi nào expr()
.
Từ hướng dẫn sử dụng Bison:
"Bất kỳ loại trình tự nào cũng có thể được xác định bằng cách sử dụng đệ quy bên trái hoặc đệ quy bên phải, nhưng bạn phải luôn luôn sử dụng đệ quy bên trái , bởi vì nó có thể phân tích một chuỗi bất kỳ số phần tử nào có không gian ngăn xếp giới hạn. Đệ quy bên phải sử dụng không gian trên ngăn xếp Bison trong tỷ lệ với số phần tử trong chuỗi, bởi vì tất cả các phần tử phải được chuyển lên ngăn xếp trước khi quy tắc có thể được áp dụng ngay cả một lần. Xem Thuật toán Bison Parser, để giải thích thêm về điều này. "
http://www.gnu.org/software/bison/manual/html_node/Recursion.html
Vì vậy, nó phụ thuộc vào thuật toán của trình phân tích cú pháp, nhưng như đã nêu trong các câu trả lời khác, một số trình phân tích cú pháp có thể đơn giản không hoạt động với đệ quy trái