2013-12-08 52 views
4

我有一臺掃描儀,解析器和主從創建通過的Flex /野牛EOF傳播VS文件

bison -d parser.y; flex scanner.l; gcc main.c parer.tab.c lex.yy.c

可執行當我運行./a.out是我想要做什麼:如果Ctrl+D被按下EOF被檢測到並且main可以相應地起作用。這意味着:如果yyinstdin那麼命中Return就結束該行的解析並且主循環等待下一個輸入行。按Ctrl+D結束主循環中break的解析輸入並退出。如果輸入來自文件,則e,g,testFile該文件可以包含1個要解析的表達式,直到EOF。在文件場景中,新行應該像空格和製表符一樣被吃掉。當輸入來自stdin時,所有這些行爲應該像解釋器一樣,並且當輸入來自文件時,就像腳本評估器一樣。這種測試文件的示例內容是:test\n。這裏沒有檢測到EOF。而且我很難理解爲什麼會這樣。換句話說,我想這個問題here的一個擴展,另外與輸入文件的工作

parser.y:

%{ 
#include <stdio.h> 
#include <stdlib.h> 
#include <stdbool.h> 

/* stuff from flex that bison needs to know about: */ 
int yylex(); 
int yyparse(); 
FILE *yyin; 

static int parseValue; 

void yyerror(const char *s); 
%} 

%token TWORD 
%token TEOF 
%token TJUNK 

%start input 

%% 
input: word       { printf("W"); parseValue = 1; } 
    | eof       { printf("eof"); parseValue = -11;} 
    | /* empty */     { printf("_"); parseValue = -1; } 
    | error       { printf("E"); parseValue = -2; } 
    ; 

eof: TEOF 
    ; 

word: TWORD 
    ; 
%% 

void yyerror(const char *s) { 
    printf("nope..."); 
} 

int getWord(FILE *file) { 
    int err; 

    if (file) { 
     yyin = file; 
    } else /* error */ { 
     printf("file not valid"); 
     return -3; 
    } 

    err = yyparse(); 
    if (!err) { 
     return parseValue; 
    } else /* error */ { 
     printf("parse error"); 
     return -4; 
    } 
} 

scanner.l:

%{ 
#include <stdio.h> 
#include "parser.tab.h" 
#define YYSTYPE int 

int yylex(); 
%} 

/* avoid: implicit declaration of function ‘fileno’ */ 
/*%option always-interactive*/ 

%option noyywrap 
/* to avoid warning: ‘yyunput’ defined but not used */ 
%option nounput 
/* to avoid warning: ‘input’ defined but not used */ 
%option noinput 

%% 
<<EOF>>      { return TEOF; } 
[ \t]      { } 
[\n]      { if (yyin == stdin) return 0; } 
[a-zA-Z][a-zA-Z0-9]*  { return TWORD; } 
.       { return TJUNK; } 
%% 

的main.c:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdarg.h> 
#include <stdbool.h> 

int main(int argc, char *argv[]) { 

    int result = 0; 
    FILE *fOut = stdout, *fIn = stdin; 

    /* skip over program name */ 
    ++argv, --argc; 
    if (argc > 0) { 
     fIn = fopen(argv[0], "r"); 
    } 

    while (true) { 
     fprintf(fOut, "\nTEST : ", result); 

     result = getWord(fIn); 

     if (result == -11) { 
      printf(" %i ", result); printf("--> EOF"); 
      break; 
     } 
     if (result < 0) { 
      printf(" %i ", result); printf("--> <0"); 
      /*continue;*/ 
      break; 
     } 

     fprintf(fOut, " => %i", result); 
    } 

    fprintf(fOut, "\n\n done \n "); 
    exit(EXIT_SUCCESS); 
} 

我試圖根據建議重寫解析herehere,沒有太大的成功。從文件讀取輸入時,主要知道EOF的正確方法是什麼?

更新: 一項建議是,該問題可能是由於return 0;\n。作爲一個快速測試,我只返回0,如果yyin == stin但叫./a.out testFile仍然沒有趕上EOF更新2: 我這通過使用yywrap工作。我擺脫了所有的TEOF東西。該掃描儀具有部分:

extern int eof; 

,並在年底:

int yywrap() { 
    eof = 1; 
    return 1; 
} 

在解析器有一個:

int eof = 0; 

,並在文件中進一步下跌:

err = yyparse(); 
if (err != 0) return -4; 
else if (eof) return -11; 
else return parseValue; 

如果有人能給我一個更優雅的解決方案,我會直到欣賞。 This可能是製作乾淨版本的好方法。

回答

2

如在鏈接中所指出的,具有flex語法用於識別一個輸入文件或流的末尾(例如,從一個字符串的輸入)。

事實上,flex實際上有一直操作這樣的規則。默認情況下,該規則調用yywrap。你關閉了這個功能(使用%noyywrap)。這很好,除了...

上遇到「EOF令牌」的默認行爲是返回0

野牛生成解析器(和byacc)需要看到這個零令牌。見this answerEND OF FILE token with flex and bison (only works without it)

您的詞法分析器在遇到換行符時返回0標記。那會造成各種麻煩。毫無疑問會導致您從文件中讀取時觀察到的內容。


編輯:好的,用這種方式和更新應用,讓我們考慮你的語法。

請記住,野牛增加了一個特殊的產品,尋找零令牌。我們用$來表示(通常人們會這樣做,或者有時候是$end)。所以,你的整個語法(無行動和「錯誤」去掉,因爲它也是特殊的)是:

$all : input $; 

input: word | eof | /* empty */; 

word: TWORD; 

eof: TEOF; 

,這意味着你的語法接受的唯一的句子是:

TWORD $ 

或:

TEOF $ 

或:

$ 

因此,當您撥打yyparse()時,yyparse()中的循環將從詞法分析器中讀取一個標記,並在標記爲零值文件結尾$時接受(並返回)結果。如果不是,則令牌需要是TWORDTEOF之一(其他任何情況都會導致致電yyerror()並嘗試重新同步)。如果令牌是兩個有效令牌之一,則yyparse()將再次調用詞法分析器以驗證下一個令牌是零值文件結束$令牌。

如果全部成功,yyparse()將返回成功。

重新添加動作,您應該看到printf的輸出,並根據用於識別(至多一個)標記的減少規則獲取存儲在parseValue中的值。

+0

非常感謝,非常感謝。我會嘗試一下,看看它是如何工作的。我希望upvote你,但沒有足夠的聲譽...... – oops

+0

我已經刪除了返回0和''\ n''但文件解析不起作用;它仍然循環。不知道如何繼續。 – oops

+0

它應該循環(在'yyparse()'內部),直到詞法分析器返回'0'來表示輸入結束。這已經有一段時間了,但是我記得flex在返回'TEOF'標記(一次)後實際上會返回'0'。 (有幾個宏放棄解析或早期接受,但你沒有使用它們...) – torek