Việc phân tích cú pháp không quét có liên quan gì đến vấn đề khác của Dang Dangling Else?


13

Tôi không hiểu câu này từ bài viết trên Wikipedia về vấn đề Nguy hiểm khác :

[Vấn đề nguy hiểm khác] là một vấn đề thường xuất hiện trong quá trình xây dựng trình biên dịch, đặc biệt là phân tích cú pháp không quét.

Ai đó có thể giải thích cho tôi làm thế nào các kỹ thuật phân tích không quét có thể làm trầm trọng thêm vấn đề này không? Dường như với tôi rằng vấn đề là do ngữ pháp - vì nó mơ hồ - không phải do sự lựa chọn của kỹ thuật phân tích cú pháp. Tôi đang thiếu gì?


2
Điều duy nhất tôi có thể nghĩ là một trình phân tích cú pháp không có máy quét cần một ngữ pháp phức tạp hơn, khiến cho việc cung cấp các phương pháp phỏng đoán để giải quyết sự mơ hồ trở nên khó khăn hơn.
Giorgio

3
@Robert Harvey: Vấn đề là giả định này phải được phản ánh bởi cây cú pháp. Nếu một ngữ pháp cho phép lấy được hai cây cú pháp khác nhau cho chuỗi if a then if b then s1 else s2, thì ngữ pháp đó không rõ ràng.
Giorgio

1
@RobertHarvey một cách phổ biến để xác định ngôn ngữ là sử dụng ngữ pháp không ngữ cảnh, cộng với một loạt các quy tắc phân biệt ngữ pháp, nếu cần.

2
Không phải tất cả các trình phân tích cú pháp không quét được tạo ra bằng nhau. Đối với PEG hoặc GLR, một hành vi nguy hiểm khác luôn luôn có thể dự đoán được.
SK-logic

1
[Vấn đề nguy hiểm khác] không liên quan gì đến phân tích cú pháp quét. [Vấn đề Dangling Else] có liên quan đến hoạt động giảm ca của các trình phân tích cú pháp LR (từ dưới lên). AFAIK
ddur

Câu trả lời:


6

Dự đoán tốt nhất của tôi là câu trong bài viết Wikipedia là kết quả của sự hiểu lầm về công việc của E. Visser.

Các ngữ pháp dành cho trình phân tích cú pháp không quét (tức là các ngữ pháp mô tả một ngôn ngữ là tập hợp các chuỗi ký tự thay vì tập hợp các chuỗi mã thông báo được mô tả riêng biệt dưới dạng chuỗi ký tự) có xu hướng mơ hồ. E. Bộ lọc định hướng giấy Visser cho Bộ phân tích cú pháp tổng quát không quét (*) đề xuất một số cơ chế để giải quyết sự mơ hồ, một trong số đó hữu ích để giải quyết vấn đề nguy hiểm khác. Nhưng bài báo không nói rằng sự mơ hồ chính xác có tên là "vấn đề khác" có liên quan đến các trình phân tích cú pháp không quét (thậm chí cơ chế này đặc biệt hữu ích cho các trình phân tích cú pháp không quét).

Thực tế là nó đề xuất một cơ chế để giải quyết nó không phải là một tuyên bố ngầm vì một cơ chế giải quyết sự mơ hồ khác (ưu tiên và ưu tiên của nhà điều hành) dường như hoàn toàn không liên quan đến bản chất không quét của các trình phân tích cú pháp được xem xét (ví dụ như những sự mơ hồ đó không thể hiện diện trong các ngữ pháp thông thường khi chúng là kết quả của việc lồng nhau, trong khi các ngữ pháp được xử lý theo quy tắc khớp dài nhất có thể).


(*) Trong đó có lẽ là giấy phục vụ như là cơ sở của bài viết Wikipedia trên parsers scannerless ngay cả khi họ tham khảo lẫn nhau, cũng bởi E. Visser, Scannerless Generalized-LR Parsing .


13

Chỉ cần nêu vấn đề, Vấn đề Nguy hiểm khác là một sự mơ hồ trong đặc tả cú pháp mã trong đó có thể không rõ ràng, trong trường hợp ifs và elses tiếp theo, cái nào khác thuộc về if.

Ví dụ đơn giản và cổ điển nhất:

if(conditionA)
if(conditionB)
   doFoo();
else
   doBar();

Không rõ ràng, với những người không biết chi tiết cụ thể của đặc tả ngôn ngữ, điều này có ifđược else(và đoạn mã cụ thể này có giá trị trong nửa tá ngôn ngữ, nhưng có thể thực hiện khác nhau ở mỗi ngôn ngữ).

Cấu trúc Dangling Else đặt ra một vấn đề tiềm ẩn đối với việc triển khai trình phân tích cú pháp không quét, bởi vì chiến lược này sẽ làm xáo trộn luồng tệp một ký tự tại một thời điểm, cho đến khi trình phân tích cú pháp thấy rằng nó đủ để token hóa (tiêu hóa vào ngôn ngữ lắp ráp hoặc ngôn ngữ trung gian mà nó đang biên dịch) . Điều này cho phép trình phân tích cú pháp duy trì trạng thái tối thiểu; ngay khi nó nghĩ rằng nó có đủ thông tin để viết mã thông báo mà nó đã phân tích vào tệp, nó sẽ làm như vậy. Đó là mục tiêu cuối cùng của một trình phân tích cú pháp không quét; biên soạn nhanh, đơn giản, gọn nhẹ.

Giả sử dòng mới và khoảng trắng trước hoặc sau dấu chấm câu là vô nghĩa (vì chúng có trong hầu hết các ngôn ngữ kiểu C), câu lệnh này sẽ xuất hiện cho trình biên dịch dưới dạng:

if(conditionA)if(conditionB)doFoo();else doBar;

Phân tích cú pháp hoàn hảo cho máy tính, vì vậy hãy xem. Tôi nhận được một nhân vật tại một thời điểm cho đến khi tôi có:

if(conditionA)

Ồ, tôi biết điều đó có nghĩa là gì (trong C #), nó có nghĩa là " pushđiều kiệnA lên ngăn xếp eval và sau đó gọi brfalseđể chuyển đến câu lệnh sau dấu chấm phẩy tiếp theo nếu nó không đúng". Ngay bây giờ tôi không thấy dấu chấm phẩy, vì vậy bây giờ tôi sẽ đặt phần bù nhảy của mình sang khoảng trống tiếp theo sau hướng dẫn này và tôi sẽ tăng phần bù đó khi tôi chèn thêm hướng dẫn cho đến khi tôi thấy dấu chấm phẩy. Tiếp tục phân tích ...

if(conditionB)

OK, điều này phân tích ra một cặp thao tác IL tương tự, và nó diễn ra ngay sau lệnh tôi vừa phân tích. Tôi không thấy dấu chấm phẩy, vì vậy tôi sẽ tăng độ lệch của câu lệnh trước bằng độ dài của hai lệnh (một cho đẩy và một cho ngắt) và tiếp tục tìm kiếm.

doFoo();

Ok, thật dễ dàng. Đó là " calldoFoo". Và đó có phải là dấu chấm phẩy mà tôi thấy không? Chà, thật tuyệt, đó là kết thúc của dòng. Tôi sẽ tăng số lần nhảy của cả hai khối của mình theo độ dài của hai lệnh này và quên tôi đã từng quan tâm. OK, tiếp tục ...

else

... Uh-oh. Điều này không đơn giản như nó nhìn. OK, tôi đã quên những gì tôi vừa làm, nhưng elsecó nghĩa là có một tuyên bố phá vỡ có điều kiện ở đâu đó mà tôi đã thấy, vì vậy hãy để tôi nhìn lại ... vâng, đó là brfalse, ngay sau khi tôi nhấn một số "conditionB" ngăn xếp, bất cứ điều gì đã được. OK, bây giờ tôi cần một điều kiện vô điều kiện breaknhư tuyên bố tiếp theo. Tuyên bố sẽ được đưa ra sau đó chắc chắn là mục tiêu phá vỡ có điều kiện của tôi, vì vậy tôi sẽ đảm bảo rằng tôi có quyền và tôi sẽ tăng thời gian nghỉ vô điều kiện mà tôi đã đưa vào.

doBar();

Điều đó thật dễ dàng. " callDoBar". Và có một dấu chấm phẩy, và tôi chưa bao giờ thấy bất kỳ dấu ngoặc nhọn nào. Vì vậy, vô điều kiện breaknên chuyển sang tuyên bố tiếp theo, bất kể đó là gì, và tôi có thể quên tôi từng quan tâm.


Vì vậy, chúng ta có gì ... (lưu ý: bây giờ là 10:00 và tôi không cảm thấy muốn chuyển đổi các bit bit thành thập lục phân hoặc điền vào toàn bộ vỏ IL của một hàm bằng các lệnh này, vì vậy đây chỉ là giả IL sử dụng số dòng trong đó thường có các byte bù):

ldarg.1 //conditionA
brfalse <line 6> //jumps to "break"
ldarg.2 //conditionB
brfalse <line 7> //jumps to "call doBar"
call doFoo
break <line 8> //jumps beyond statement in scope
call doBar
<line 8 is here>

Chà, điều đó thực sự thực thi chính xác, NẾU quy tắc (như trong hầu hết các ngôn ngữ kiểu C) là elseđi với gần nhất if. Được thụt lề để theo dõi lồng nhau, nó sẽ thực thi như thế này, trong đó nếu điều kiệnA là sai, toàn bộ phần còn lại của đoạn mã sẽ bị bỏ qua:

if(conditionA)
    if(conditionB)
       doFoo();
    else
       doBar();

... nhưng nó làm như vậy bởi sự ngẫu nhiên, bởi vì ngắt kết hợp với ifcâu lệnh bên ngoài nhảy đến breakcâu lệnh ở cuối phần bên trong if , đưa con trỏ thực thi vượt ra ngoài toàn bộ câu lệnh. Đó là một bước nhảy không cần thiết thêm, và nếu ví dụ này phức tạp hơn nữa thì nó có thể không còn hoạt động nếu được phân tích cú pháp và mã hóa theo cách này.

Ngoài ra, điều gì sẽ xảy ra nếu đặc tả ngôn ngữ nói rằng sự lơ lửng elsethuộc về đầu tiên ifvà nếu điều kiệnA là sai thì doBar được thực thi, trong khi nếu điều kiệnA là đúng nhưng không phải điều kiệnB thì không có gì xảy ra, như vậy?

if(conditionA)
    if(conditionB)
       doFoo();
else
   doBar();

Trình phân tích cú pháp đã quên lần đầu tiên iftồn tại và vì vậy thuật toán trình phân tích cú pháp đơn giản này sẽ không tạo ra mã chính xác, không nói gì về mã hiệu quả.

Bây giờ, trình phân tích cú pháp có thể đủ thông minh để ghi nhớ các ifs và elsenó tồn tại trong một thời gian dài hơn, nhưng nếu thông số ngôn ngữ nói một elsesau hai ifs khớp với đầu tiên if, điều đó gây ra vấn đề với hai ifs với khớp elses:

if(conditionA)
    if(conditionB)
       doFoo();
    else
       doBar();
else
    doBaz();

Trình phân tích cú pháp sẽ thấy cái đầu tiên else, khớp với cái đầu tiên if, sau đó nhìn thấy cái thứ hai và rơi vào trạng thái hoảng loạn "cái quái gì tôi đang làm lại". Tại thời điểm này, các trình phân tích cú pháp đã nhận được khá nhiều mã ở trạng thái có thể thay đổi mà nó sẽ thay vào đó là đẩy ra đoạn phim đầu ra.

Có giải pháp cho tất cả những vấn đề này và what-ifs. Nhưng, hoặc mã cần thiết là thông minh đó làm tăng độ phức tạp của thuật toán trình phân tích cú pháp hoặc thông số ngôn ngữ cho phép trình phân tích cú pháp này làm tăng mức độ dài của mã nguồn ngôn ngữ, chẳng hạn như bằng cách yêu cầu các câu lệnh kết thúc như end if, hoặc dấu ngoặc chỉ ra lồng nhau chặn nếu ifcâu lệnh có một else(cả hai thường được thấy trong các kiểu ngôn ngữ khác).

Đây chỉ là một ví dụ đơn giản về một vài ifcâu lệnh và xem xét tất cả các quyết định mà trình biên dịch phải đưa ra, và dù sao nó cũng có thể rất dễ bị nhầm lẫn. Đây là chi tiết đằng sau câu nói vô thưởng vô phạt đó từ Wikipedia trong câu hỏi của bạn.


1
Thú vị nhưng tôi không chắc đó là những gì được dự định bởi bài viết Wikipedia. Nó tham chiếu (thông qua mục không quét) một báo cáo của Eelco Visser có nội dung ngay từ cái nhìn đầu tiên không tương thích với lời giải thích của bạn.
Lập trình viên

3
Cảm ơn đã phản hồi, nhưng nó không thực sự giải quyết được OP. Tôi không đồng ý với các giả định trong bài viết về mục tiêu của trình phân tích cú pháp không quét và cách thức thực hiện. Có nhiều cách để thực hiện trình phân tích cú pháp không quét và bài đăng này dường như chỉ đối phó với một tập hợp con giới hạn.
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.