Điều gì khiến Java dễ phân tích cú pháp hơn C?


90

Tôi quen với thực tế là ngữ pháp của C và C ++ là ngữ cảnh nhạy cảm , và đặc biệt là bạn cần "hack lexer" trong C. Mặt khác, tôi có ấn tượng rằng bạn có thể phân tích cú pháp Java chỉ với 2 mã thông báo nhìn trước, mặc dù có sự tương đồng đáng kể giữa hai ngôn ngữ.

Bạn sẽ phải thay đổi điều gì về C để phân tích cú pháp dễ hiểu hơn?

Tôi hỏi vì tất cả các ví dụ tôi đã thấy về độ nhạy ngữ cảnh của C đều được phép về mặt kỹ thuật nhưng thật kỳ lạ. Ví dụ,

foo (a);

có thể là gọi hàm void foovới đối số a. Hoặc, nó có thể được khai abáo là một đối tượng kiểu foo, nhưng bạn có thể dễ dàng loại bỏ các parantheses. Một phần, sự kỳ lạ này xảy ra do quy tắc sản xuất "bộ khai báo trực tiếp" cho ngữ pháp C đáp ứng mục đích kép là khai báo cả hàm và biến.

Mặt khác, ngữ pháp Java có các quy tắc sản xuất riêng biệt cho khai báo biến và khai báo hàm. Nếu bạn viết

foo a;

thì bạn biết đó là một khai báo biến và foorõ ràng có thể được phân tích cú pháp thành tên kiểu. Đây có thể không phải là mã hợp lệ nếu lớp foochưa được xác định ở đâu đó trong phạm vi hiện tại, nhưng đó là công việc phân tích ngữ nghĩa có thể được thực hiện trong lần vượt qua trình biên dịch sau này.

Tôi đã thấy nó nói rằng C khó phân tích cú pháp vì typedef, nhưng bạn cũng có thể khai báo các kiểu của riêng mình trong Java. Ngoài ra, quy tắc ngữ pháp C nào direct_declaratorbị lỗi?


7
Câu hỏi hay. Có lẽ là cách quá rộng hoặc chủ yếu cố chấp.
asteri

37
Đây là một câu hỏi hợp lệ về trình phân tích cú pháp và điều duy nhất bao quát hoặc quan điểm dựa trên nó là vài câu cuối cùng (có thể nên bỏ hoặc thay đổi). Bỏ phiếu kín.
R .. GitHub NGỪNG TRỢ GIÚP ICE

1
Tôi đã chỉnh sửa câu hỏi cho phù hợp, cảm ơn @R .. đã phản hồi.
korrok

3
Hầu như mọi ngôn ngữ máy tính (tiêu chuẩn) đều nhạy cảm với ngữ cảnh ; bạn không thể khai báo một biến của một loại, và lạm dụng nó nhất langauges . Điều đó khác với "tất cả các ngữ pháp cho ngôn ngữ" là ngữ cảnh nhạy cảm; hầu hết mọi người xây dựng trình phân tích cú pháp xây dựng trình phân tích cú pháp không có ngữ cảnh (hoặc thậm chí hạn chế hơn), sau đó sử dụng các bản hack bên ngoài trình phân tích cú pháp để kiểm tra các thuộc tính không có ngữ cảnh.
Ira Baxter

1
@IraBaxter Tôi sẽ không gọi đó là "hack". Chia vấn đề thành hai có vẻ là một điều hợp lý để làm, vì việc phân tích các ngôn ngữ nhạy cảm theo ngữ cảnh không thể được thực hiện một cách hiệu quả (và trên thực tế, ngay cả việc phân tích các ngôn ngữ không có ngữ cảnh cũng không hiệu quả và đó là lý do tại sao chúng tôi thường hạn chế ở các tập con không có ngữ cảnh) . Một phân tích cú pháp không theo ngữ cảnh + phân tích tĩnh để chỉ kiểm tra các thuộc tính nhạy cảm với ngữ cảnh trên AST là điều hợp lý nên làm.
Bakuriu

Câu trả lời:


76

Phân tích cú pháp C ++ đang trở nên khó khăn. Phân tích cú pháp Java ngày càng trở nên khó khăn.

Xem này câu trả lời SO thảo luận tại sao C (và C ++) "khó" để phân tích cú pháp . Tóm tắt ngắn gọn là ngữ pháp C và C ++ vốn rất mơ hồ; chúng sẽ cung cấp cho bạn nhiều phân đoạn và bạn phải sử dụng ngữ cảnh để giải quyết những điều không rõ ràng. Sau đó, mọi người mắc sai lầm khi cho rằng bạn phải giải quyết những điều mơ hồ khi bạn phân tích cú pháp; không phải như vậy, hãy xem bên dưới. Nếu bạn nhấn mạnh vào việc giải quyết những điều mơ hồ khi bạn phân tích cú pháp, trình phân tích cú pháp của bạn sẽ phức tạp hơn và khó xây dựng hơn nhiều; nhưng sự phức tạp đó là vết thương lòng tự gây ra.

IIRC, ngữ pháp LALR (1) "rõ ràng" của Java 1.4 không mơ hồ, vì vậy nó "dễ dàng" để phân tích cú pháp. Tôi không chắc rằng Java hiện đại không có ít nhất những mơ hồ cục bộ đường dài; luôn có vấn đề là quyết định xem "... >>" đóng hai mẫu hay là "toán tử dịch chuyển phải". Tôi nghi ngờ rằng Java hiện đại không phân tích cú pháp với LALR (1) nữa .

Nhưng người ta có thể vượt qua vấn đề phân tích cú pháp bằng cách sử dụng trình phân tích cú pháp mạnh (hoặc trình phân tích cú pháp yếu và hack thu thập ngữ cảnh như giao diện người dùng C và C ++ hiện nay chủ yếu làm), cho cả hai ngôn ngữ. C và C ++ có thêm sự phức tạp là có một bộ tiền xử lý; chúng phức tạp hơn trong thực tế so với vẻ ngoài của chúng. Một khẳng định là các trình phân tích cú pháp C và C ++ rất khó để chúng phải được viết bằng tay. Nó không đúng sự thật; bạn có thể xây dựng trình phân tích cú pháp Java và C ++ tốt với trình tạo trình phân tích cú pháp GLR.

Nhưng phân tích cú pháp không thực sự là vấn đề.

Sau khi bạn phân tích cú pháp, bạn sẽ muốn làm điều gì đó với AST / cây phân tích cú pháp. Trong thực tế, bạn cần biết, đối với mỗi mã định danh, định nghĩa của nó là gì và nó được sử dụng ở đâu ("độ phân giải tên và kiểu", cẩu thả, xây dựng bảng ký hiệu). Điều này hóa ra là một công việc nhiều hơn rất nhiều so với việc làm cho đúng trình phân tích cú pháp, kết hợp bởi tính kế thừa, giao diện, quá tải và các mẫu, và gây bối rối bởi thực tế là ngữ nghĩa của tất cả những điều này được viết bằng ngôn ngữ tự nhiên không chính thức trải rộng trên hàng chục đến hàng trăm trang của tiêu chuẩn ngôn ngữ. C ++ thực sự tệ ở đây. Từ quan điểm này, Java 7 và 8 trở nên khá tệ hại. (Và các bảng biểu tượng không phải là tất cả những gì bạn cần; hãy xem tiểu sử của tôi để có một bài luận dài hơn về "Life After Parsing").

Hầu hết mọi người phải vật lộn với phần phân tích cú pháp thuần túy (thường không bao giờ hoàn thành; hãy tự kiểm tra SO để biết rất nhiều câu hỏi về cách tạo trình phân tích cú pháp hoạt động cho các ngôn ngữ thực), vì vậy họ không bao giờ thấy cuộc sống sau khi phân tích cú pháp. Và sau đó chúng ta nhận được các định lý dân gian về những gì khó phân tích cú pháp và không có tín hiệu nào về những gì xảy ra sau giai đoạn đó.

Sửa cú pháp C ++ sẽ không đưa bạn đến được đâu.

Về việc thay đổi cú pháp C ++: bạn sẽ thấy mình cần phải vá rất nhiều chỗ để xử lý sự đa dạng của các điểm mơ hồ cục bộ và thực tế trong bất kỳ ngữ pháp C ++ nào. Nếu bạn nhấn mạnh, danh sách sau đây có thể là một nơi khởi đầu tốt . Tôi cho rằng không có ích gì khi làm điều này nếu bạn không phải là ủy ban tiêu chuẩn C ++; nếu bạn đã làm như vậy và xây dựng một trình biên dịch bằng cách sử dụng nó, sẽ không ai có thể sử dụng nó. Đã đầu tư quá nhiều vào các ứng dụng C ++ hiện có để chuyển đổi để thuận tiện cho những người xây dựng trình phân tích cú pháp; ngoài ra, nỗi đau của họ đã qua và các trình phân tích cú pháp hiện có hoạt động tốt.

Bạn có thể muốn viết trình phân tích cú pháp của riêng mình. Được rồi, ổn thôi; chỉ không mong đợi phần còn lại của cộng đồng cho phép bạn thay đổi ngôn ngữ mà họ phải sử dụng để giúp bạn dễ dàng hơn. Tất cả họ đều muốn nó dễ dàng hơn cho họ và đó là sử dụng ngôn ngữ như được tài liệu hóa và triển khai.


Câu trả lời tốt. Xem thêm D và C +, cố gắng giải quyết một số vấn đề này. s / content / tranh /
david.pfx

3
Tôi đã đọc Life After Parsing trước đây và thấy nó là một thứ mở mang tầm mắt thực sự; nó đã nói rõ với tôi rằng có nhiều công việc trong phân tích ngữ nghĩa (phân giải tên / kiểu, ...) hơn là phân tích cú pháp. Tôi không cố gắng thay đổi cú pháp của bất kỳ ngôn ngữ nào. Tôi làm muốn hiểu những gì các tính chất là của một ngôn ngữ mà bạn có thể thực hiện phân tích cú pháp đầu tiên và sau đó là phân tích ngữ nghĩa. C không phải là một ngôn ngữ như vậy (cần hack lexer); Tôi luôn nghĩ rằng Java là như vậy và tôi muốn biết tại sao.
korrok

1
@Korrok: đọc câu trả lời của tôi về cách xây dựng Java / C ++ với bộ phân tích cú pháp GLR. Bạn không cần bất kỳ hack lexer nào . Vì vậy, sự phân biệt nằm trong tâm trí của những người đang sử dụng công nghệ phân tích cú pháp sai. ... Được cho là, việc xây dựng giao diện người dùng C ++ đầy đủ (đặc biệt là C ++ 14, mà chúng tôi đã thực hiện) khó hơn làm Java8, nhưng cả hai đều khó (về nỗ lực và chú ý đến chi tiết) và phân tích cú pháp là mảnh dễ dàng nhất.
Ira Baxter

1
Tôi đồng ý về "Cuộc sống sau khi phân tích cú pháp" của bạn: ví dụ: giải quyết quá tải trong C # có thể mã hóa bất kỳ vấn đề 3-SAT nào và do đó khó NP.
Jörg W Mittag,

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.