2010-11-10 27 views
14

Tôi đang cố gắng để phân tích bình luận C-style đa dòng trong flex của tôi (.l) file:Tại sao các nhận xét nhiều dòng trong flex/bison lại dễ dàng như vậy?

%s ML_COMMENT 
%% 

... 

<INITIAL>"/*"     BEGIN(ML_COMMENT); 
<ML_COMMENT>"*/"    BEGIN(INITIAL); 
<ML_COMMENT>[.\n]+    { } 

Tôi không trả lại bất kỳ dấu hiệu và ngữ pháp của tôi (.y) không đề cập đến ý kiến bằng mọi cách.

Khi tôi chạy thực thi của tôi, tôi nhận được một lỗi phân tích cú pháp:

$ ./a.out 
/* 
abc 
def 
Parse error: parse error 
$ echo "/* foo */" | ./a.out 
Parse error: parse error 

(chức năng yyerror My hiện một printf ("Parse error:% s \ n"), là nơi mà các nửa đầu thông báo lỗi dư thừa xuất phát từ).

Tôi có thể thấy lý do tại sao ví dụ thứ hai không thành công do toàn bộ dữ liệu nhập là nhận xét và vì các chú thích bị bỏ qua bởi ngữ pháp, không có câu lệnh nào. Do đó đầu vào không phải là một chương trình hợp lệ. Nhưng phần đầu tiên ném lỗi phân tích cú pháp trước khi tôi hoàn thành nhận xét.

Cũng khó hiểu:

$ ./a.out 
/* foo */ 
a = b; 
Parse error: parse error 

Trong trường hợp này, những nhận xét được đóng trước khi đầu vào có giá trị thực tế (trong đó, nếu không có sự bình luận, phân tích tốt). Sự thất bại thực sự xảy ra sau khi phân tích cú pháp "a", không phải sau khi cố gắng phân tích cú pháp nhiệm vụ "a = b;". Nếu tôi nhập "a" trên dòng riêng của nó, nó vẫn ném một lỗi.

Cho rằng thông báo lỗi là lỗi phân tích cú pháp và không phải là lỗi máy quét, có điều gì đó quan trọng mà tôi bị thiếu trong tệp .y của mình không? Hoặc tôi đang làm điều gì đó sai trái trong các quy tắc của tôi quét mà truyền bá sang phía phân tích cú pháp?

EDIT: mỗi @ đề nghị Rudi, tôi bật gỡ lỗi và tìm thấy:

$ ./a.out 
Starting parse 
Entering state 0 
Reading a token: /* 
foo 
Next token is 44 (IDENTIFER) 
Shifting token 44 (IDENTIFER), Entering state 4 
Reducing via rule 5 (line 130), IDENTIFER -> identifier 
state stack now 0 
Entering state 5 

Tôi tắt gỡ lỗi và phát hiện ra rằng /* foo */ = bar; thực sự phân tích giống như foo = bar;. Tôi đang sử dụng flex 2.5.4; nó không cho tôi bất kỳ cảnh báo nào về các quy tắc nhà nước mà tôi đang cố gắng sử dụng.

+1

Tôi đã gắn lại flex thành gnu-flex. Quy tắc máy quét của bạn trông ổn. Lỗi phân tích cú pháp cho biết đầu vào mã thông báo không hợp lệ cho trình phân tích cú pháp. Bạn có thể muốn đăng một số quy tắc Bison tương ứng. Ngoài ra, bạn nên đặt các câu lệnh printf() bên trong các quy tắc bison của bạn, theo cách này bạn có thể thấy những gì mà trình phân tích cú pháp đang thử trong quá trình quét mã thông báo. – Kizaru

+2

Bạn cũng nên tạo một bộ kiểm tra riêng cho máy quét của mình. Bằng cách đó bạn có thể tách biệt các lỗi máy quét khỏi các lỗi phân tích cú pháp. Bất kỳ hệ thống trình phân tích cú pháp nào cũng đủ phức tạp đến mức bạn không cần phải chèn thêm độ phức tạp bằng cách thực hiện kiểm tra tích hợp khi bạn thực sự muốn thực hiện kiểm tra đơn vị ... – bstpierre

+1

Khi bạn thêm cờ '--debug' vào bison của bạn gọi và đặt 'yydebug = 1' trước cuộc gọi' yyparse() ', khi đó trình phân tích cú pháp phát ra thông tin gỡ lỗi cho mỗi mã thông báo mà nó thấy từ lexer. – Rudi

Trả lời

5

Tôi nghĩ rằng bạn cần phải khai báo ML_COMMENT của bạn bắt đầu tình trạng như một độc quyền bắt đầu điều kiện để chỉ các quy tắc ML_COMMENT được kích hoạt.%x ML_COMMENT thay vì %s ML_COMMENT

Quy tắc khác không có điều kiện bắt đầu cũng hoạt động.

+0

Ah! Điều này dường như đã thực hiện các trick. Câu hỏi duy nhất của tôi bây giờ là: tại sao nội dung của các bình luận nhiều dòng của tôi lại lặp lại? Khi tôi gõ '/ * foo bar * /' vào STDIN, tôi nhận 'foo bar' được in ra STDOUT. – adelarge

+2

[. \ N] hiện không làm những gì bạn nghĩ. thay thế nó bằng 2 quy tắc một. và một cho \ n. Flex theo đầu vào echos mặc định không khớp với bất kỳ quy tắc nào. Đó là lý do tại sao nhiều bộ quy tắc lex kết thúc bằng "." vì vậy mọi đầu vào đều khớp với thứ gì đó. – Craig

+0

Điều đó đã làm được. Cảm ơn! – adelarge

5

Phân tích bình luận theo cách này có thể dẫn đến sai lầm vì:

  • bạn cần phải thêm điều kiện cho tất cả các quy tắc lex bạn
  • nó trở nên phức tạp hơn nếu bạn cũng muốn xử lý // bình luận
  • bạn vẫn có rủi ro yacc/bison hợp nhất hai nhận xét bao gồm mọi thứ ở giữa

Trong trình phân tích cú pháp, tôi xử lý các nhận xét như thế này. Trước tiên hãy xác định các quy tắc lex để bắt đầu nhận xét, như sau:

\/\*  { 
     if (!SkipComment()) 
      return(-1); 
     } 

\/\/  { 
     if (!SkipLine()) 
      return(-1); 
     } 

sau đó viết chức năng Bỏ qua và bỏ qua. Họ cần phải tiêu thụ tất cả các đầu vào cho đến cuối những nhận xét được tìm thấy (đây là thay vì mã cũ nên tha thứ cho tôi những công trình xây dựng có phần cổ xưa):

bool SkipComment (void) 
{ 
int Key; 

Key=!EOF; 
while (true) 
    { 
    if (Key==EOF) 
     { 
     /* yyerror("Unexpected EOF within comment."); */ 
     break; 
     } 
    switch ((char)Key) 
     { 
     case '*' : 
     Key=input(); 
     if (char)Key=='/') return true; 
     else    continue; 
     break; 
     case '\n' : 
     ++LineNr; 
     break; 
     } 
    Key=input(); 
    } 

return false; 
} 

bool SkipLine (void) 
{ 
int Key; 

Key=!EOF; 
while (true) 
    { 
    if (Key==EOF) 
     return true; 
    switch ((char)Key) 
     { 
     case '\n' : 
     unput('\n'); 
     return true; 
     break; 
     } 
    Key=input(); 
    } 

return false; 
} 
+1

Thao tác này có xử lý chuỗi ký tự bắt đầu/kết thúc nhận xét nếu nó xuất hiện trong văn bản được trích dẫn không? (ví dụ: 'foo =" điều này không chứa/* comment */"') –

+0

Tôi đã không đề cập rõ ràng điều này, nhưng bạn phải phân tích cú pháp chính xác theo cùng một cách. Bạn đặc biệt phải làm điều này nếu bạn muốn hỗ trợ thoát các dấu gạch chéo ngược như trong C/C++. – Patrick

+1

Điều này phức tạp hơn, dễ bị lỗi hơn, chi tiết hơn và khó thực hiện hơn là chỉ sử dụng trạng thái bắt đầu flex đúng cách. Về cơ bản nó chỉ là một phần viết tay của lexer của bạn - nếu bạn không thích flex, tại sao không chỉ viết tay toàn bộ? –

1

Bên cạnh những vấn đề với %x vs %s, bạn cũng có vấn đề mà các . trong [.\n] trận đấu (chỉ) một literal . và không 'bất kỳ nhân vật khác hơn là newline' giống như một trần . làm. Bạn muốn có một quy tắc như

<ML_COMMENT>.|"\n"  { /* do nothing */ } 

thay

1

tôi thấy mô tả này của ngữ pháp ngôn ngữ C (trên thực tế chỉ là lexer) rất hữu ích. Tôi nghĩ rằng đó là chủ yếu giống như câu trả lời của Patrick, nhưng hơi khác nhau.

http://www.lysator.liu.se/c/ANSI-C-grammar-l.html

Các vấn đề liên quan