2012-03-24 34 views
1

我試圖在Pascal語言中創建一個非常簡單的YACC解析器,它只包含整數聲明,一些基本表達式和if-else語句。然而,我無法找到幾個小時的錯誤,我很快就會瘋狂。終端說Error at line:0但它是不可能的!我使用flex和byacc解析器。如果你能幫助我,我會很高興。你可以看到這是我的lex文件;使用YACC解析時找不到一個簡單的錯誤

%{ 
#include <stdio.h> 
#include <string.h> 
#include "y.tab.h" 
extern int yylval; 
int linenum=0; 
%} 

digit [0-9] 
letter [A-Za-z] 

%% 
if    return IF; 
then    return THEN; 
else    return ELSE; 
for    return FOR; 
while    return WHILE; 
PROGRAM    return PROGRAM_SYM; 
BEGIN    return BEGIN_SYM; 
VAR    return VAR_SYM; 
END    return END_SYM; 
INTEGER    return INTEGER_SYM; 
{letter}({letter}|{digit})* return identifier; 
[0-9]+    return NUMBER; 
[\<][\=]   return CON_LE; 
[\>][\=]   return CON_GE; 
[\=]    return CON_EQ;   
[\:][\=]   return ASSIGNOP; 
;    return semiColon; 
,    return comma; 
\n    {linenum++;} 
.    return (int) yytext[0]; 
%% 

,這是我的Yacc文件

%{ 
#include <stdio.h> 
#include <string.h> 
#include "y.tab.h" 
extern FILE *yyin; 
extern int linenum; 
%} 

%token PROGRAM_SYM VAR_SYM BEGIN_SYM END_SYM INTEGER_SYM NUMBER 
%token identifier INTEGER ASSIGNOP semiColon comma THEN 
%token IF ELSE FOR WHILE 
%token CON_EQ CON_LE CON_GE GE LE 

%left '*' '/' 
%left '+' '-' 

%start program 

%% 

program: PROGRAM_SYM identifier semiColon VAR_SYM dec_block BEGIN_SYM statement_list END_SYM '.' 
    ; 

dec_block: 
     dec_list semiColon; 

dec_list: 
     dec_list dec 
     | 
     dec 
     ; 

dec: 
     int_dec_list 
     ; 

int_dec_list: 
     int_dec_list int_dec ':' type 
     | 
     int_dec ':' type 
     ; 

int_dec: 
     int_dec comma identifier 
     | 
     identifier 
     ; 

type: 
    INTEGER_SYM 
    ; 

statement_list: 
     statement_list statement 
     | 
     statement 
     ; 

statement: 
     assignment_list 
     | 
     expression_list 
     | 
     selection_list 
     ; 

assignment_list: 
     assignment_list assignment 
     | 
     assignment  
     ; 

assignment: 

     identifier ASSIGNOP expression_list 
     ; 

expression_list: 
     expression_list expression semiColon 
     | 
     expression semiColon 
     ; 


expression: 
     '(' expression ')' 
     | 
     expression '*' expression 
     | 
     expression '/' expression 
     | 
     expression '+' expression 
     | 
     expression '-' expression 
     | 
     factor 
     ; 

factor:  
     identifier 
     | 
     NUMBER 
     ; 


selection_list: 
     selection_list selection 
     | 
     selection 
     ; 

selection: 
     IF '(' logical_expression ')' THEN statement_list ELSE statement_list 
     ; 


logical_expression: 
     logical_expression '=' expression 
     | 
     logical_expression '>' expression 
     | 
     logical_expression '<' expression 
     ; 


%% 
void yyerror(char *s){ 
    fprintf(stderr,"Error at line: %d\n",linenum); 
} 
int yywrap(){ 
    return 1; 
} 
int main(int argc, char *argv[]) 
{ 
    /* Call the lexer, then quit. */ 
    yyin=fopen(argv[1],"r"); 
    yyparse(); 
    fclose(yyin); 
    return 0; 
} 

最後我採取了錯誤的第一線時,我給的輸入;

PROGRAM myprogram; 

VAR 

i:INTEGER; 

i3:INTEGER; 

j:INTEGER; 

BEGIN 

i := 3; 

j := 5; 

i3 := i+j*2; 

i := j*20; 

if(i>j) 

then i3 := i+50+(45*i+(40*j)); 

else i3 := i+50+(45*i+(40*j))+i+50+(45*i+(30*j)); 

END. 
+0

的哪個版本Yacc你在用嗎?當我用'bison'編譯你的代碼時,我得到'grammar.y:warning:3個無用的非終結符和7個無用的規則'和'grammar.y:衝突:8個shift/reduce'的警告(以及其間的特定錯誤消息) 。您是否打算在運行該程序之前解決其中的任何問題? (FWIW:當我運行程序時,儘管有警告,但我收到了同樣的錯誤信息) – 2012-03-24 22:38:51

+0

它應該是'byacc',即使我得到13次轉換/減少衝突,它們不應該是一個問題原因,因爲我說它包含非常簡單的語法,並且它說'在第0行錯誤'。我甚至試圖寫樹形式,但無法實現的問題 – quartaela 2012-03-24 22:44:29

+0

它是如此愚蠢,它給出了第0行的錯誤!我試圖解決這個小時,但沒有改善... – quartaela 2012-03-24 22:48:42

回答

0

您的詞法分析器返回空白和製表符作爲標記,但語法不能識別它們。

添加解析器規則:

[ \t\r] { } 

,它會給你6行,而不是線0您遇到錯誤之前。你得到這個錯誤,因爲你不允許分號聲明之間:

dec_block: 
     dec_list semiColon; 

dec_list: 
     dec_list dec 
     | 
     dec 
     ; 

dec: 
     int_dec_list 
     ; 

這也許應該是:

dec_block: 
     dec_block dec 
     | 
     dec 
     ; 

dec: 
     int_dec_list semiColon 
     ; 

這樣做可以讓你在輸入線14。

順便說一下,第一件事我做一個是確保詞法分析器告訴我它是什麼做的,通過修改規則是這樣的:

if    { printf("IF\n"); return IF; } 

在長期的代碼,我會做該診斷輸出在運行時可選。


您對所期望的分號有一個普遍的問題。也不清楚你應該允許在statement的規則中使用expression_list(或者,也許,'尚未'—可能適用於有函數調用時,但允許3 + 2/4作爲'語句'不是很有幫助)。


此語法獲取到輸入的結束:

%{ 
#include <stdio.h> 
#include <string.h> 
#include "y.tab.h" 
extern FILE *yyin; 
extern int linenum; 
%} 

%token PROGRAM_SYM VAR_SYM BEGIN_SYM END_SYM INTEGER_SYM NUMBER 
%token identifier INTEGER ASSIGNOP semiColon comma THEN 
%token IF ELSE FOR WHILE 
%token CON_EQ CON_LE CON_GE GE LE 

%left '*' '/' 
%left '+' '-' 

%start program 

%% 

program: PROGRAM_SYM identifier semiColon VAR_SYM dec_block BEGIN_SYM statement_list END_SYM '.' 
    ; 

dec_block: 
     dec_block dec 
     | 
     dec 
     ; 

dec: 
     int_dec_list semiColon 
     ; 

int_dec_list: 
     int_dec_list int_dec ':' type 
     | 
     int_dec ':' type 
     ; 

int_dec: 
     int_dec comma identifier 
     | 
     identifier 
     ; 

type: 
    INTEGER_SYM 
    ; 

statement_list: 
     statement_list statement 
     | 
     statement 
     ; 

statement: 
     assignment 
     | 
     selection 
     ; 

assignment: 
     identifier ASSIGNOP expression semiColon 
     ; 

expression: 
     '(' expression ')' 
     | 
     expression '*' expression 
     | 
     expression '/' expression 
     | 
     expression '+' expression 
     | 
     expression '-' expression 
     | 
     factor 
     ; 

factor: 
     identifier 
     | 
     NUMBER 
     ; 

selection: 
     IF '(' logical_expression ')' THEN statement_list ELSE statement_list 
     ; 

logical_expression: 
     expression '=' expression 
     | 
     expression '>' expression 
     | 
     expression '<' expression 
     ; 

%% 
void yyerror(char *s){ 
    fprintf(stderr,"Error at line: %d\n",linenum); 
} 
int yywrap(){ 
    return 1; 
} 
int main(int argc, char *argv[]) 
{ 
    /* Call the lexer, then quit. */ 
    yyin=fopen(argv[1],"r"); 
    yyparse(); 
    fclose(yyin); 
    return 0; 
} 

主要變化包括移除assignment_listexpression_list和修改logical_expression使得膨脹的雙方expression,而非LHS是logical_expression(然後從來沒有一個原始定義,導致警告的問題)。

仍有問題需要解決; selection中的expression_list應該更加嚴格以準確地反映Pascal的語法。 (您需要一個塊,其中可能是單個語句或BEGIN,語句列表END。)

+0

是這個代碼更令人滿意,但你確定它顯示在6_?導致它顯示在我的終端上3? – quartaela 2012-03-24 23:03:14

+0

現在確定它顯示在7,我要添加這些printf函數。你是可行的,這將是更有幫助 – quartaela 2012-03-24 23:11:08

+0

以及我說我剛剛開始一個簡單的例子。所以這段代碼會隨時間而改變(例如,如果我嘗試解析函數調用)。而且我也嘗試了無意義的陳述來檢查它是否給出錯誤。但編譯器繼續編譯,即使沒有警告。這就是爲什麼我讓簡單的計算作爲聲明 – quartaela 2012-03-24 23:22:24

1

對於調試語法,YYDEBUG是您的朋友。您可以在%{ .. %}之間粘貼#define YYDEBUG 1。Ÿ文件,或編譯-DYYDEBUG,並呼籲yyparse之前粘在main一個yydebug = 1;,你會得到什麼記號解析器是看到什麼是他們做信息的轉換....

+0

是的,這是很好的另一種方式來實現你的解析器做什麼!非常感謝 – quartaela 2012-03-24 23:54:32