2010-03-02 138 views
4

我應該編寫一個程序來做2 + 2 = 4和2.2 + 2 = 4.2。yacc:從浮點數中區分整數

我已經做到了,所以它將所有事情視爲浮點,但那是「錯誤的」。我必須區分它們。這是我到目前爲止:

%{ 
#include <stdio.h> 
#include <ctype.h> 
%} 

%token <dval> FLOAT 
%token <ival> INTEGER 

%union 
{ 
    float dval; 
    int ival; 
} 

%type <dval> command exp term factor 

%% 

command : exp   {printf("%f\n",$1);} 
    ; 

exp : exp '+' term  {$$ = $1 + $3;} 
    | exp '-' term  {$$ = $1 - $3;} 
    | term   {$$ = $1;} 
    ; 

term : term '*' factor {$$ = $1 * $3;} 
    | factor  {$$ = $1;} 
    ; 

factor : '(' exp ')'  {$$ = $2;} 
    | FLOAT   {$$ = $1;} 
    | INTEGER  {$$ = $1;} 
    ; 

%% 

int main() 
{ 
    return yyparse(); 
} 

int yylex() 
{ 
    int c; 
    while((c=getchar()) == ' '); 
    if(isdigit(c)) 
    { 
     ungetc(c, stdin); 
     float f1; 
     scanf("%f", &f1); 
     int i1 = (int) f1; 
     if(f1 == 0) 
     { 
     yylval.ival = 0; 
    return INTEGER; 
     } 
     else if((((float) i1)/f1) == 1) 
     { 
    yylval.ival = i1; 
     return INTEGER; 
     } 
     else 
     { 
    yylval.dval = f1; 
    return FLOAT; 
     } 
     //scanf("%f",&yylval.dval); 
     //return(NUMBER); 
    } 
    if(c == '\n') return 0; 
    return c; 
} 

int yyerror(char *s) 
{ 
    fprintf(stderr,"%s\n",s); 
    return 0; 
} 

我遇到的問題是,每個表達式只能有1個類型。現在一切都基本上是浮動的,所以雖然操作是正確的,但這不是正確的解決方案。

我想過定義更多的表達式,基本上有factor_int和factor_float,然後替換它中的所有東西,但這似乎是錯誤的。我不知道如何做到這一點,我看到的教程並沒有真正幫助我。

+0

我認爲factor_int和factor_float方法是正確的。基本上,真正的編譯器是如何工作的。每個表達式都是浮點或整數。 – 2010-03-02 20:46:10

+0

所以如果我有8種不同的類型,我會有2個操作數的2^8表達式?太瘋狂了!!!或者我正在做數學錯誤。 – 2010-03-02 20:48:48

+0

你有沒有聽說過lex?我認爲較新的一個叫做flex。 – 2010-03-02 20:53:55

回答

1

基本上你可以做這樣的事情:

%{ 
#include <stdio.h> 
#include <ctype.h> 

struct number 
{ 
    union 
    { 
    int ival; 
    float fval; 
    } 
    char type; 
} 

char INT_TYPE = 1; 
char FLOAT_TYPE = 2; 

%} 

%union 
{ 
    struct number value; 
} 

%token <value> FLOAT INTEGER command exp term factor 

int yylex() 
{ 
    ... 
    if(f1 == 0) 
    { 
    yylval.value.type = INT_TYPE; 
    yylval.value.ival = 0 
    } 
    ... 
} 

等..

這樣就可以減少規則是一定要產生新的正確的類型。例如,當檢查操作數:

exp : exp '+' term { 
    if ($1.type == INT_TYPE && $3.type == INT_TYPE) 
    { 
     $$.type = INT_TYPE; 
     $$.ival = $1.ival + $3.ival; 
    } 
    else if ($1.type == INT_TYPE && $3.type == FLOAT_TYPE) 
    { 
     // this is a sort of implicit conversion to float 
     $$.type = FLOAT_TYPE; 
     $$.fval = $1.ival + $3.fval; 
    } 
    // and so on 

} 

PS。我做了類似的Flex +野牛,我不知道是否一切都支持,以及在Lex + Yacc,但我認爲是這樣..

0

在yylval結構/聯合中編碼數據類型。

而不是寫每個可能的組合+運算符,只爲yacc中的+運算符定義1條規則,並在運行時檢查數據類型(存儲在yylval中)的有效性。

使用容器或數組來存儲所有有效的組合,並使用此容器在運行時檢查有效性。如果你沒有找到有效的組合,你可以給出至少一個體面的運行時間錯誤,如「對不起,你不能添加日期和浮點數」。而不是語法錯誤(如果您要在yacc中定義單獨的規則,您會得到這個錯誤)。

作爲最後的錦上添花,添加「自動轉換」邏輯。如果您沒有找到有效的組合,請嘗試將其中一個操作數轉換爲另一種類型。一種這樣的典型的硬編碼轉換是「int to float」。例如。如果您的容器僅允許添加2個整數或2個浮點數,並且用戶輸入1 + 3.14(這是整數+浮點數),則在容器中找不到有效組合。將int轉換爲float並再次在容器中查找。如果轉換次數不是那麼大,它應該足夠快。

+0

我很抱歉問,但你能給我一個「檢查yylval數據類型的有效性」的例子嗎?儘管1 + 3.14不起作用,我也可以通過編寫所有可能的組合來實現操作實際上的有效性。 – 2010-03-02 21:28:25

+0

查看Jack的回答。 – Patrick 2010-03-03 07:12:34

0

我認爲@Jack給出的答案將工作,但它可能更多簡潔地將所有規則基於浮點數的計算,然後在最上面的規則(最後被評估)檢查結果是整數還是浮點數並打印出適當的結果。

你的主要方法將減少到:

main(){ 
return yyparse(); 
} 

int yylex(void){ 
int c; 
    while((c = getchar()) == ' '); 
    if (isdigit(c)){ 
    ungetc(c, stdin); 
    scanf("%lf", &yylval); 
    return NUMBER; 
    } 
    if (c == '\n'){ 
    return 0; 
} 
    return c; 
} 

int yyerror(char * s){ 
fprintf(stderr, "%s\n", s); 
    return 0; 
} 

和你最上面的規則應改爲:

/*This is where we distinguish between float and integer*/ 
    command : exp{ 
     if((((int)$1)/$1) == 1){ 
     printf("%d\n", (int)$1); 
     } 
     else{ 
     printf("%lf\n", $1); 
     } 
     } 
     ; 

使用這種方法,你只需要一個令牌(數字而不是FLOAT的和INTEGER),並且還需要您爲源代碼添加一條%type聲明以解釋運營商。您的%union聲明將包含double val;char op;