Trình sinh parser cũng có thể tạo lỗi tốt.
(dalinaum.github.io)Bản dịch của bài viết do Russ Cox, nhà phát triển ngôn ngữ Go, đăng vào năm 2010.
-
Từng có một ngộ nhận lan truyền rằng các trình tạo cú pháp như yacc không thể tạo ra lỗi cú pháp tốt.
-
Nhưng vấn đề này đã được Clinton Jeffery giải quyết từ năm 2003, và không phải là vấn đề chỉ có thể xử lý bằng cách tự viết parser thủ công.
-
Nếu tìm nội dung khớp với trạng thái và token đầu vào trong bảng, thì có thể dùng thông báo lỗi tốt hơn thay vì chỉ báo lỗi cú pháp đơn thuần.
3 bình luận
(Dưới đây là phần tổng hợp nội dung tôi đã viết trên máy chủ Discord Rust Hàn Quốc.)
Vấn đề lớn nhất của bài viết này là: vậy hiện tại Go có dùng parser generator hay không? Không. Parser chính của Go vẫn được viết thủ công. Tôi không rõ lý do lắm, nhưng rất có thể việc rsc thấy ổn không có nghĩa là các nhà phát triển Go khác cũng thấy ổn.
Nếu xét theo trình biên dịch, ý tưởng của Jeffrey có thể so sánh với peephole optimizer. Trước khi xuất ra mã máy, nó giữ lại một số lượng cố định các lệnh mã máy vừa mới được xuất ra gần đây — vì thế mới có cái tên như vậy, do nhìn trộm qua một cửa sổ nhỏ (peep-hole) — rồi khi thấy một mẫu cụ thể thì thay thế ngay tại chỗ các lệnh đó bằng lệnh nhanh hơn. Peephole optimizer là một công cụ tiện lợi có thể dùng song song với các bước tối ưu hóa thông thường khác, nhưng chỉ với riêng peephole optimizer thì không thể thực hiện mọi loại tối ưu hóa mà một trình biên dịch cần. Tương tự, cách tiếp cận tạo lỗi từ các token gần đây cũng không thể bao phủ mọi lỗi phân tích cú pháp. Hơn nữa, đây là một cách tiếp cận độc lập có thể áp dụng tương tự ngay cả khi không dùng parser generator, nên không đúng khi cho rằng chỉ vì có cách tiếp cận này mà các vấn đề của parser generator biến mất.
Cá nhân tôi cho rằng không phải bản thân khái niệm parser generator là tệ, mà vấn đề nằm ở tư duy mà parser generator "áp đặt". Khi viết parser, dù là sinh tự động hay viết tay, vẫn phải cân nhắc left recursion và right recursion, và không thể đơn giản mã hóa nguyên xi một ngữ pháp "tự nhiên". Nhưng nếu đã viết tay thì đó là cái giá chấp nhận được; còn nếu dùng parser generator mà vẫn phải viết sao cho khớp với một tập con của "ngữ pháp" bị ràng buộc vào các thuật toán phân tích cú pháp cụ thể như LL/LR, thì ưu điểm của generator sẽ bị giảm đi đáng kể. (Các thuật toán như GLR có nới lỏng phần nào, nhưng vẫn có giới hạn.) Việc phần lớn các triển khai ngôn ngữ ở mức production hiện nay либо không dùng parser generator, либо đôi khi dùng những cách tiếp cận như PEG — tức là vẫn là parser generator nhưng hoàn toàn không tận dụng các ưu điểm thông thường của generator — đều có lý do của nó.
Tôi đồng cảm với việc không dùng trình tạo parser vì không thích bị ràng buộc bởi ngữ pháp mà nó giới hạn, nhưng vì thế mà nói rằng trình tạo parser đang ép người dùng theo một ngữ pháp cụ thể thì hơi kỳ.
Trình tạo parser ra đời để hỗ trợ việc xây dựng bảng phân tích cú pháp cho ngữ pháp LR, vốn quá khó dù có ưu điểm là phân tích trong thời gian tuyến tính; chứ việc sinh ra bộ phân tích cho ngữ pháp context-sensitive có tính bất định, hay parser đệ quy mà thậm chí không biết khi nào việc phân tích sẽ kết thúc, có lẽ không phải là mối quan tâm của các nhà nghiên cứu/phát triển trình tạo parser.
Vì vậy, tôi cho rằng trình tạo parser không phải là chương trình mù quáng tin vào và ép buộc ngữ pháp LR/LALR thiếu trực quan, mà đang làm đúng vai trò của mình như một công cụ giải quyết bài toán rất khó là phân tích trong thời gian tuyến tính và xây dựng bảng phân tích cú pháp cho mục tiêu đó.
Hầu như mọi ngôn ngữ có thể thấy trong thực tế đều có ngữ pháp LL(1), nên ngay cả khi không dùng parser LR thì vẫn có thể phân tích cú pháp trong thời gian tuyến tính. (Điều này không có nghĩa ngữ pháp được ưu tiên cho ngôn ngữ đó là LL(1).) Vì vậy, nói rằng phải dùng parser LR để phân tích cú pháp thời gian tuyến tính nhưng vì khó tạo bảng phân tích cú pháp nên cần trình tạo parser là đã đảo ngược quan hệ trước sau. Ngoài ra, bản thân quá trình tạo bảng phân tích cú pháp về mặt bản chất không hẳn là phức tạp (cái khó là chứng minh thuật toán).