Bison 在表达式类型之间转换

Bison cast between expression types

我有下面的 bison 代码,下面的语句工作正常。

1 + 1
1.0 + 1.0

但我希望以下语句有效

1.0 + 1

我知道下面的代码不是最理想的,但我被要求保持这种格式。

    %{
    #include <stdio.h>
    #include <math.h>
    #include <ctype.h>
    #include <string.h>
    #include <errno.h>

    /* Prototypes */
    int yylex(void);
    void yyerror(char *);

    int isFloat = 0;
    %}

    %union {
        int iVal;
        double fVal;
    }

    %token <iVal> INTEGER
    %token <fVal> FLOAT

    %type <iVal> expri termi utermi factori parti
    %type <fVal> exprf termf utermf factorf partf 

    %%
    command : expri         {printf("%d\n", ); return;}
            | exprf     {printf("%f\n", ); return;}
            ;

    expri   : expri '+' termi   {$$ =  + ;}
            | expri '-' termi   {$$ =  - ;}
            | utermi        {$$ = ;}
            ;
    termi   : termi '*' factori     {$$ =  * ;} 
            | termi '/' factori {$$ =  / ;}
            | termi '%' factori     {$$ =  % ;}
            | factori       {$$ = ;}
            ;
    utermi  : utermi '*' factori    {$$ =  * ;} 
            | utermi '/' factori    {$$ =  / ;}
            | utermi '%' factori    {$$ =  % ;}
            | '-' factori       {$$ = -;}
            | factori       {$$ = ;}
            ;
    factori : factori '^' parti {$$ = pow(, );}
            | parti         {$$ = ;}
            ;
    parti   : '(' expri ')'     {$$ = ;} 
            | INTEGER       {$$ = ;}
            ;
    /* FLOAT RULES */
    exprf   : exprf '+' termf   {$$ =  + ;}
            | exprf '-' termf   {$$ =  - ;}
            | utermf        {$$ = ;}
            ;
   termf    : termf '*' factorf     {$$ =  * ;} 
            | termf '/' factorf {$$ =  / ;}
            | termf '%' factorf     {$$ = fmodf(, );}
            | factorf       {$$ = ;}
            ;
    utermf  : utermf '*' factorf    {$$ =  * ;}
            | utermf '/' factorf    {$$ =  / ;}
            | utermf '%' factorf    {$$ = fmodf(,);}
            | '-' factorf       {$$ = -;}
            | factorf       {$$ = ;}
            ;
    factorf : factorf '^' partf {$$ = pow(, );}
            | partf         {$$ = ;}
            ;
    partf   : '(' exprf ')'     {$$ = ;}
            | FLOAT         {$$ = ;}
            ;
    %%

虽然这两种类型在它们自己的分支中都可以正常工作,但结构(显然)非常不理想,存在大量重复,但我不知道解决方法,也不知道如何在它们之间转换。

您可以通过其他几种方法解决此问题:

  1. 最简单的解决方案(如果您不需要类型信息)是使整数适合 double 类型,然后您就可以合并规则 partipartf等合二为一。但是,这确实会改变结构,因此它可能不是您想要的。

  2. 如果您需要类型信息,正如 Paul Ogilvie 所建议的那样,您可能需要一些 Val 结构,该结构包含 integer/double 与类型标记的联合。同样,这可能不是您想要的,因为它确实需要更多的努力。但是...

  3. 我仔细考虑了一下,也许有一个可能的解决方案可以最大限度地减少对现有语言格式的更改。本质上,我们添加 "widening" 从整数到浮点数的转换规则:

    exprf: /* other exprf rules */
         | expri { $$ = ; /* Implicit integer widening conversion*/}
    termf: /* other termf rules */
         | termi { $$ = ; }
    utermf: /* other utermf rules */
          | utermi { $$ = ; }
    /* ... */
    

    这将是一个非常棘手和笨拙的解决方案,因为会弹出减少-减少和移位-减少冲突。 (例如,表达式 1 + 1 可以简化为 expriexprf)。您可能需要查看 glr-parsing and merging glr parses 来解决一些歧义。我希望这可能是您正在寻找的东西。

前两个是更优雅的解决方案,但也许如果你真的需要,第三个选项是一个可行的选项,(如果你选择这个选项,我建议使用 bison -v 来尝试调试这些冲突,并尝试在 bison 中玩耍一下)。