Nếu bạn muốn một cách dễ dàng để viết mã trình phân tích cú pháp hoặc bạn hạn chế về dung lượng, bạn nên viết mã thủ công trình phân tích cú pháp gốc đệ quy; về cơ bản đây là các trình phân tích cú pháp LL (1). Điều này đặc biệt hiệu quả đối với các ngôn ngữ "đơn giản" như Basic. (Tôi đã làm một vài trong số này vào những năm 70!). Tin tốt là chúng không chứa bất kỳ mã thư viện nào; chỉ những gì bạn viết.
Chúng khá dễ viết mã, nếu bạn đã có ngữ pháp. Đầu tiên, bạn phải loại bỏ các quy tắc đệ quy bên trái (ví dụ: X = XY). Điều này nói chung là khá dễ thực hiện, vì vậy tôi để nó như một bài tập. (Bạn không cần phải làm điều này đối với các quy tắc tạo danh sách; xem thảo luận bên dưới).
Sau đó, nếu bạn có quy tắc BNF của biểu mẫu:
X = A B C ;
tạo một chương trình con cho mỗi mục trong quy tắc (X, A, B, C) trả về một boolean nói rằng "Tôi đã thấy cấu trúc cú pháp tương ứng". Đối với X, mã:
subroutine X()
if ~(A()) return false;
if ~(B()) { error(); return false; }
if ~(C()) { error(); return false; }
// insert semantic action here: generate code, do the work, ....
return true;
end X;
Tương tự cho A, B, C.
Nếu mã thông báo là thiết bị đầu cuối, hãy viết mã kiểm tra luồng đầu vào cho chuỗi ký tự tạo nên thiết bị đầu cuối. Ví dụ: đối với một Số, hãy kiểm tra xem luồng đầu vào có chứa các chữ số hay không và đưa con trỏ luồng đầu vào qua các chữ số. Điều này đặc biệt dễ dàng nếu bạn đang phân tích cú pháp ra khỏi bộ đệm (đối với BASIC, bạn có xu hướng nhận được một dòng tại một thời điểm) bằng cách chỉ cần tiến hoặc không tiến một con trỏ quét bộ đệm. Mã này về cơ bản là phần lexer của trình phân tích cú pháp.
Nếu quy tắc BNF của bạn là đệ quy ... đừng lo lắng. Chỉ cần mã lệnh gọi đệ quy. Điều này xử lý các quy tắc ngữ pháp như:
T = '(' T ')' ;
Điều này có thể được mã hóa là:
subroutine T()
if ~(left_paren()) return false;
if ~(T()) { error(); return false; }
if ~(right_paren()) { error(); return false; }
// insert semantic action here: generate code, do the work, ....
return true;
end T;
Nếu bạn có quy tắc BNF với một quy tắc thay thế:
P = Q | R ;
sau đó mã P với các lựa chọn thay thế:
subroutine P()
if ~(Q())
{if ~(R()) return false;
return true;
}
return true;
end P;
Đôi khi bạn sẽ gặp các quy tắc tạo danh sách. Chúng có xu hướng là đệ quy trái và trường hợp này có thể dễ dàng xử lý. Ý tưởng cơ bản là sử dụng lặp thay vì đệ quy, và điều đó tránh đệ quy vô hạn mà bạn sẽ làm điều này theo cách "hiển nhiên". Thí dụ:
L = A | L A ;
Bạn có thể viết mã này bằng cách sử dụng lặp lại như sau:
subroutine L()
if ~(A()) then return false;
while (A()) do { /* loop */ }
return true;
end L;
Bạn có thể viết mã hàng trăm quy tắc ngữ pháp trong một hoặc hai ngày theo cách này. Có nhiều chi tiết hơn để điền vào, nhưng những điều cơ bản ở đây là quá đủ.
Nếu bạn thực sự eo hẹp về không gian, bạn có thể xây dựng một máy ảo thực hiện những ý tưởng này. Đó là những gì tôi đã làm vào những năm 70, khi 8K từ 16 bit là những gì bạn có thể nhận được.
Nếu bạn không muốn viết mã này bằng tay, bạn có thể tự động hóa nó bằng một siêu trình biên dịch ( Meta II ) về cơ bản tạo ra cùng một thứ. Đây là những điều thú vị về kỹ thuật và thực sự giúp bạn thực hiện điều này, ngay cả đối với những nhà ngữ pháp lớn.
Tháng 8 năm 2014:
Tôi nhận được rất nhiều yêu cầu về "cách tạo AST bằng trình phân tích cú pháp". Để biết thông tin chi tiết về vấn đề này, về cơ bản tạo ra câu trả lời này, hãy xem câu trả lời SO khác của tôi https://stackoverflow.com/a/25106688/120163
Tháng 7 năm 2015:
Có rất nhiều người muốn viết một trình đánh giá biểu thức đơn giản. Bạn có thể làm điều này bằng cách thực hiện những việc tương tự như liên kết "Trình tạo AST" ở trên gợi ý; chỉ cần làm số học thay vì xây dựng các nút cây. Đây là một trình đánh giá biểu thức được thực hiện theo cách này .