2010-03-02 132 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;