2009-03-18 42 views
16

Tôi đang cố gắng sử dụng flex và bò rừng để tạo ra một bộ lọc, bởi vì tôi muốn nhận được một số yếu tố ngữ pháp từ một ngôn ngữ phức tạp. Kế hoạch của tôi là sử dụng flex + bison để nhận ra ngữ pháp và loại bỏ vị trí của các yếu tố quan tâm. (Sau đó sử dụng một kịch bản để lấy văn bản theo các địa điểm đổ.)Flex hỗ trợ chính xác vị trí bison?

tôi thấy flex có thể hỗ trợ một tính năng gọi là bò rừng bison-địa điểm, nhưng làm thế nào nó hoạt động chính xác. Tôi đã thử các ví dụ trong tài liệu flex, có vẻ như yylloc không được thiết lập tự động bởi flex, tôi luôn luôn nhận được (1,0)-(1,0). Có thể tự động tính toán vị trí của từng mã thông báo không? Nếu không, chức năng giao diện nào được định nghĩa để tôi thực hiện? Có ví dụ nào không?

Bất kỳ giải pháp nào tốt hơn về công cụ?

Best Regards, Kevin

Edit:

Bây giờ giao diện đến lượt yylex tới:

tay bò rừng bizon
int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param); 

không xác định như thế nào lexer nên thực hiện để thiết lập một cách chính xác yylloc_param. Đối với tôi rất khó để theo dõi số cột của mỗi mã thông báo theo cách thủ công.

Trả lời

6

Hãy xem phần 3.6 of the Bison manual - mà dường như để trang trải các địa điểm ở một số chi tiết. Kết hợp với những gì bạn tìm thấy trong hướng dẫn sử dụng Flex, có thể là đủ.

+0

I figured rằng chỉ số dòng là nhập khẩu đối với tôi. –

15

Việc kê khai yylex lẽ thay đổi vì bạn sử dụng một lõm hoặc thuần phân tích cú pháp. Có vẻ như nhiều tài liệu trên web đề xuất bạn bắt buộc phải làm việc nếu bạn muốn địa điểm sinh sống làm việc nhưng không bắt buộc.

tôi cần số dòng quá và tìm thấy các tài liệu Bison bối rối trong vấn đề đó. Giải pháp đơn giản (sử dụng var yylloc toàn cầu): Trong file Bison của bạn chỉ cần thêm% địa điểm chỉ thị:

%{ 
... 
%} 
%locations 
... 
%% 
... 

trong lexer của bạn:

%{ 
... 
#include "yourprser.tab.h" /* This is where it gets the definition for yylloc from */ 
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; 
%} 
%option yylineno 
... 
%% 
... 

vĩ mô YY_USER_ACTION được "gọi là" trước mỗi hành động mã thông báo của bạn và cập nhật yylloc. Bây giờ bạn có thể sử dụng @N/@ $ quy tắc như thế này:

statement : error ';' { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); } 

, hoặc sử dụng các yylloc var toàn cầu:

void yyerror(char *s) 
{ 
    fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s); 
} 
+0

Tôi không nghĩ điều này là đủ. Tôi đã thử điều này và luôn gặp lỗi 'yylloc 'không khai báo khi tôi cố gắng xây dựng. Phải có một cái gì đó khác mà bạn phải làm để kích hoạt yylloc. – Mike

+1

Bạn có thêm chỉ thị% vị trí không? Bạn có bao gồm bạn đã tạo tệp .tab.h trong lexer không? Có thể bạn đang sử dụng phiên bản cũ của bison + flex? Nó hoạt động với tôi với Bison 2.4.1 và Flex 2.5.35. –

11

Tôi thích câu trả lời của Shlomi.

Bên cạnh đó tôi đang tìm kiếm cập nhật vị trí cột là tốt. Tìm thấy http://oreilly.com/linux/excerpts/9780596155971/error-reporting-recovery.html có ý nghĩa hơn sau khi đọc câu trả lời của Shlomi.

Đáng tiếc là có một lỗi đánh máy trên trang đó cho yylloc. Tôi đã đơn giản hóa nó dưới đây một chút.

Trong phân tích cú pháp của bạn thêm:

%locations 

trong lexer của bạn:

%{ 

#include "parser.tab.h" 

int yycolumn = 1; 

#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; \ 
    yylloc.first_column = yycolumn; yylloc.last_column = yycolumn + yyleng - 1; \ 
    yycolumn += yyleng; \ 
    yylval.str = strdup(yytext); 

%} 

%option yylineno 

Có thể có một cái gì đó đang xảy ra với vị trí cột mà không giữ đúng dõi các cột mà chỉ tiếp tục tăng . Đó chỉ là sự thiếu hiểu biết của tôi và có tính ứng dụng nếu nó làm lẫn lộn bất cứ ai. Tôi hiện đang sử dụng cột để giữ một số ký tự tập tin mà trong trường hợp của tôi là có lợi hơn vị trí cột.

Hy vọng điều đó sẽ hữu ích.

+0

Cảm ơn bạn rất nhiều vì đã đăng bài này. – Dilawar

+1

Lý do số cột chỉ tiếp tục tăng là bởi vì bạn không bao giờ đặt nó trở lại 1 trên một dòng mới và không Flex, vì nó thậm chí không biết về biến 'yycolumn' của bạn. Rõ ràng những gì bạn cần làm là tự theo dõi các dòng mới thay vì dựa vào '% option yylineno'. – hugomg

+0

Không 'yylval.str = strdup (yytext)' đặt nội dung của mã thông báo Bison? Bạn sẽ chỉ muốn đây là hành động mặc định trong quy tắc nếu mọi mã thông báo là một chuỗi, phải không? –

4

Vì vậy, tôi có này để "làm việc", nhưng với một số bước mở rộng (tôi có thể đã bỏ qua chúng ở đây ... xin lỗi trong trường hợp đó):

  1. Trong parser.y, tôi đã phải nói:

    #define YYLEX_PARAM &yylval, &yylloc 
    

    ngay cả với %locationsbison --locations, để làm cho nó thông qua các dữ liệu.

  2. Trong lexer.l tôi đã phải sử dụng -> thay vì . cho yylloc

  3. Cũng trong lexer.l, tôi thiết lập lại các cột trong hành động:

    [\n] { yycolumn = 1; } 
    

Rõ ràng y phức tạp hơn một chút, cho \r v.v. nhưng ít nhất tôi đã làm việc đó.

+0

Thêm - các vị trí vào dòng lệnh HOẶC% vị trí sau ngữ pháp sẽ có nghĩa là yylloc nằm trong phạm vi của tệp .y, miễn là bạn tham chiếu đến nó từ mã trong phần "%%" cuối cùng. –

6

Câu trả lời của Shomi là giải pháp đơn giản nhất nếu bạn chỉ quan tâm đến việc giữ số dòng. Tuy nhiên, nếu bạn cũng muốn số cột thì bạn cần phải theo dõi chúng.

Một cách để làm điều đó là thêm yycolumn = 1 quy tắc ở mọi nơi một dòng mới hiển thị (như được đề xuất trong câu trả lời của David Elson) nhưng nếu bạn không muốn theo dõi tất cả các địa điểm mà dòng mới có thể hiển thị (khoảng trắng, nhận xét, v.v. ...) một sự thay thế được kiểm tra bộ đệm yytext vào lúc bắt đầu của mọi hành động:

static void update_loc(){ 
    static int curr_line = 1; 
    static int curr_col = 1; 

    yylloc.first_line = curr_line; 
    yylloc.first_column = curr_col; 

    {char * s; for(s = yytext; *s != '\0'; s++){ 
    if(*s == '\n'){ 
     curr_line++; 
     curr_col = 1; 
    }else{ 
     curr_col++; 
    } 
    }} 

    yylloc.last_line = curr_line; 
    yylloc.last_column = curr_col-1; 
} 

#define YY_USER_ACTION update_loc(); 

Cuối cùng, có một điều cần lưu ý là một khi bạn bắt đầu theo dõi các số cột bằng tay bạn cũng có thể cũng theo dõi của các số dòng trong cùng một vị trí và không bận tâm với việc sử dụng tùy chọn yylineno của Flex.

9

Không phải bison cũng không tự động flex cập nhật yylloc, nhưng thực sự không khó để tự thực hiện — nếu bạn biết thủ thuật.

Bí quyết triển khai yylloc hỗ trợ là, mặc dù yyparse() tuyên bố yylloc, nó không bao giờ thay đổi.Điều đó có nghĩa là nếu bạn sửa đổi yylloc trong một cuộc gọi đến lexer, bạn sẽ tìm thấy các giá trị giống nhau trong cuộc gọi tiếp theo. Do đó, yylloc sẽ chứa vị trí của mã thông báo cuối cùng. Vì cuối cùng của mã thông báo cuối cùng giống như bắt đầu của mã thông báo hiện tại, bạn có thể sử dụng giá trị yylloc cũ để giúp bạn xác định giá trị mới.

Nói cách khác, yylex() không được tínhyylloc; cần cập nhậtyylloc.

Để cập nhật yylloc, trước tiên chúng ta phải sao chép các giá trị last_ thành first_ và sau đó cập nhật giá trị last_ để phản ánh độ dài của mã thông báo vừa khớp. (Đây không phải là strlen() của mã thông báo; đó là chiều dài dòng và cột.) Chúng tôi có thể thực hiện việc này trong macro YY_USER_ACTION, được gọi ngay trước khi thực hiện bất kỳ thao tác lexer nào; đảm bảo rằng nếu một quy tắc phù hợp nhưng nó không trả về một giá trị (ví dụ, quy tắc bỏ qua khoảng trống hoặc nhận xét), vị trí của mã không bị bỏ qua đó thay vì được bao gồm ở đầu mã thông báo thực hoặc bị mất theo cách khiến việc theo dõi vị trí không chính xác.

Đây là phiên bản dành cho trình phân tích cú pháp reentrant; bạn có thể sửa đổi nó cho một cú pháp không reentrant bằng cách trao đổi các -> nhà khai thác cho .:

#define YY_USER_ACTION \ 
    yylloc->first_line = yylloc->last_line; \ 
    yylloc->first_column = yylloc->last_column; \ 
    for(int i = 0; yytext[i] != '\0'; i++) { \ 
     if(yytext[i] == '\n') { \ 
      yylloc->last_line++; \ 
      yylloc->last_column = 0; \ 
     } \ 
     else { \ 
      yylloc->last_column++; \ 
     } \ 
    } 

Nếu bạn muốn, bạn có thể thay vì đặt mã trong một hàm và thực hiện cuộc gọi vĩ mô chức năng, nhưng hai kỹ thuật tương đương nhau.

+0

Hữu ích hơn nhiều so với các câu trả lời khác, mặc dù tôi muốn đề xuất tạo một hàm và chỉ cần gọi hàm đó trong macro - 'static void update_loc (YYLTYPE * loc, char * txt) {loc-> first_line = ...}'/'#define YY_USER_ACTION update_loc (yylloc, yytext);'. – Kevin

+0

Đừng bận tâm về điều vị trí, hóa ra tôi có một quy tắc khớp với toàn bộ dòng và 'REJECT'. – Kevin

+0

Đây là câu trả lời hữu ích nhất, và IMO phải là câu trả lời được chấp nhận – cqcallaw

1

Tôi nghĩ rằng tôi đã quản lý để làm cho nó hoạt động (tín dụng đi đến các nhà văn của hướng dẫn bison ltcalc lexical analyzer). Theo mặc định, bò rừng bizon tạo yylloc chứa

{ first_line, first_column , last_line , last_column } 

Chúng tôi chỉ cần cập nhật những giá trị trong phân tích từ vựng của chúng tôi. Ví dụ:

[ \t]  { ++yylloc.last_column; } 
[\n]  { yyloc.last_column = 0; return EOL; } 
[a-zA-Z]+ { 
      yylloc.last_column += strlen(yytext); 
      return IDENTIFIER; 
      } 

Bây giờ trong bò rừng, để lấy những lĩnh vực:

statement : IDENTIFIER '=' expression 
      { printf("%d - %d\n", @1.last_line, @1.last_column); } 

Theo mặc định các trường này được khởi tạo một, chúng ta nên khởi tạo các lĩnh vực cột bằng không nếu không họ sẽ báo cáo cột sai.

0

Một sự thêm vào Shlomi của câu trả lời:

Nếu bạn đang sử dụng% xác định api.pure ở bò rừng để tạo ra một phân tích cú pháp lõm, bạn cũng cần phải xác định% tùy chọn bò rừng bizon-địa điểm trong flex. Điều này là do trong một trình phân tích cú pháp yylloc reentrant không phải là một biến toàn cầu, và cần phải được chuyển vào lexer.

Vì vậy, trong phân tích cú pháp:

%define api.pure 
%locations 

trong lexer:

#include "yourprser.tab.h" 
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; 
%option bison-locations 
%option yylineno 
Các vấn đề liên quan