【问题标题】:bison/flex: calculator interprets double values as integers so I added #define YYSTYPE double, but I multiple compilation errorsbison/flex:计算器将双精度值解释为整数,所以我添加了 #define YYSTYPE 双精度,但出现多个编译错误
【发布时间】:2026-02-12 22:45:02
【问题描述】:

我正在使用 flex 和 bison 实现计算器,但是 double 值被解释为整数,然后我在互联网上寻找答案,我意识到错误可能是 bison 将数字解释为整数,所以如果我输入“1.2 " 在 *yylval = atof(yytext) 中是 1 而不是 1.2。所以我尝试在 parser.ypp 中添加 #define YYSTYPE double 但我得到了编译错误。 我知道有几个类似的问题,但没有一个真正帮助我。 这是我的 lex 和 ypp 文件以及我得到的编译错误。
提前致谢。

lexer.lex:

%option noyywrap
%option noinput
%option nounput

%{
    #include <iostream>
    #include <cstdlib>
    #include <string>
    #include <fstream>

    #include "parser.tab.hpp"

%}

%%

[0-9]+([.][0-9]+)? {
    *yylval = atof(yytext);
        return NUMBER;
}



sin {
    return SIN;
}

cos {
    return COS;
}

tan {
    return TG;
}

tg {
    return TG;
}

ctan {
    return CTG;
}

ctg {
    return CTG;
}

asin {
    return ASIN;
}

acos {
    return ACOS;
}

atan {
    return ATG;
}

actan {
    return ACTG;
}

ln {
    return LN;
}

log {
    return LOG;
}

exp {
    return EXP;
}

sqrt {
    return SQRT;
}

abs {
    return ABS;
}

mod {
    return MOD;
}

[a-z] {
    return VARIABLE;
}

[-+*/^()%!,] {
    return *yytext;
}


[ \t\n] ;

. {

}

parser.ypp:


%{

#define YYSTYPE double


#include <iostream>
#include <cstdlib>
#include <string>
#include <cmath>

 bool indicator_calculating_value ;

extern int yylex();

void yyerror(double *return_value, std::string s);

%}




%parse-param  { double *return_value}

%left '+' '-'
%left '*' '/' '%'
%left SIN COS TG CTG ASIN ACOS ATG ACTG LN LOG MOD
%left UMINUS
%left '^' EXP SQRT
%left ABS
%left '!'


%type <double> E
%token <double> NUMBER
%token <char> VARIABLE;
%start pocetak

%%
pocetak
    : E {
       *return_value = $1;

    };

E   
    : E '+' E {
        if (indicator_calculating_value) {
            $$ = $1 + $3;
        }
    } 
    | E '*' E {

        if (indicator_calculating_value) {
            $$ = $1 * $3;
        }
    }
    | E '-' E {
        if (indicator_calculating_value) {
            $$ = $1 - $3;
        }
    }
    | E '/' E {

        if(indicator_calculating_value) {
            if ($3 == 0) {
                yyerror(0, "divide by zero");
            }
            $$ = $1 / $3;
        }
    }
    | MOD '(' E ',' E ')' {
        if(indicator_calculating_value) {
            if ($5 == 0) {
                yyerror(0, "divide by zero");
            }
            $$ = static_cast<int>($3) % static_cast<int>($5);
        }
    }
    | SIN '(' E ')' {
        if(indicator_calculating_value) {
            $$ = sin($3);
        }
    }
    | COS '(' E ')' {
        if(indicator_calculating_value) {
            $$ = cos($3);
        }
    }
    | TG '(' E ')' {
        if(indicator_calculating_value) {
            $$ = tan($3);
        }
    }
    | CTG '(' E ')' {
        if(indicator_calculating_value) {
            $$ = 1 / tan($3);
        }
    }
    | ASIN '(' E ')' {
        if(indicator_calculating_value) {
            $$ = asin($3);
        }
    }
    | ACOS '(' E ')' {
        if(indicator_calculating_value) {
            $$ = acos($3);
        }
    }       
    | ATG '(' E ')' {
        if(indicator_calculating_value) {
            $$ = atan($3);
        }
    }
    | ACTG '(' E ')' {
        if(indicator_calculating_value) {
            $$ = 1 / atan($3);
        }
    }
    | LN '(' E ')' {
        if(indicator_calculating_value) {
            $$ = log($3);
        }
    }
    | LOG '(' E ',' E ')' {
        if(indicator_calculating_value) {
            $$ = log($5) / log($3);
        }
    }
    | EXP '(' E ')' {
        if(indicator_calculating_value) {
            $$ = exp($3);
        }
    }
    | SQRT '(' E ')' {
        if(indicator_calculating_value) {
            $$ = sqrt($3);
        }
    }
    | E '^' E {
        if(indicator_calculating_value) {
            $$ = pow($1, $3);
        }
    }
    | '-' E %prec UMINUS {
        if(indicator_calculating_value) {
            $$ = -$2;
        }
    } 
    | ABS '(' E ')' {
        if(indicator_calculating_value) {
            $$ = fabs($3);
        }
    }
    | E '!' {
        if(indicator_calculating_value) {
            $$ = 1;
            for (int i = 1; i <= static_cast<int>($1); i++) {
                $$ = $$ * i;
            }
        }
    }
    | '(' E ')' {
        if(indicator_calculating_value) {
            $$ = $2;
        }
    }
    | NUMBER {
        if(indicator_calculating_value) {
            $$ = $1;
        }
    }
    | VARIABLE {

    }
    ;
%%

void yyerror(double *return_value, std::string s)
{
   std::cout << s << std::endl;

}

int main() {

    indicator_calculating_value = true;

    double value = 0.0;
    yyparse(&value);

    std::cout << value << std::endl;



    return 0;
}








错误:

make
bison -d -v parser.ypp
g++ -Wall -L/usr/local/lib -lmgl-qt5 -lmgl -lm  -c -o parser.tab.o parser.tab.cpp
parser.ypp: In function ‘int yyparse(double*)’:
parser.ypp:42:34: error: expected unqualified-id before ‘double’
        *return_value = $1;
                                  ^     
parser.ypp:42:34: error: expected ‘)’ before ‘double’
parser.ypp:49:20: error: expected unqualified-id before ‘double’
             $$ = $1 + $3;
                    ^~~~~~
parser.ypp:49:20: error: expected ‘)’ before ‘double’
parser.ypp:55:20: error: expected unqualified-id before ‘double’
             $$ = $1 * $3;
                    ^~~~~~
parser.ypp:55:20: error: expected ‘)’ before ‘double’
parser.ypp:60:20: error: expected unqualified-id before ‘double’
             $$ = $1 - $3;
                    ^~~~~~
parser.ypp:60:20: error: expected ‘)’ before ‘double’
parser.ypp:66:27: error: expected unqualified-id before ‘double’
             if ($3 == 0) {
                           ^     
parser.ypp:66:27: error: expected ‘)’ before ‘double’
parser.ypp:69:20: error: expected unqualified-id before ‘double’
             $$ = $1 / $3;
                    ^~~~~~
parser.ypp:69:41: error: expected unqualified-id before ‘double’
             $$ = $1 / $3;
                                         ^     
parser.ypp:69:41: error: expected ‘)’ before ‘double’
parser.ypp:69:68: error: expected ‘)’ before ‘;’ token
             $$ = $1 / $3;
                                                                    ^
parser.ypp:74:28: error: expected unqualified-id before ‘double’
             if ($5 == 0) {
                            ^     
parser.ypp:74:28: error: expected ‘)’ before ‘double’
parser.ypp:77:20: error: expected unqualified-id before ‘double’
             $$ = static_cast<int>($3) % static_cast<int>($5);
                    ^~~~~~
parser.ypp:77:58: error: expected unqualified-id before ‘double’
             $$ = static_cast<int>($3) % static_cast<int>($5);
                                                          ^~~~  
parser.ypp:77:58: error: expected ‘)’ before ‘double’
parser.ypp:77:105: error: expected ‘)’ before ‘;’ token
             $$ = static_cast<int>($3) % static_cast<int>($5);
                                                                                                         ^
parser.ypp:77:105: error: expected ‘)’ before ‘;’ token
parser.ypp:82:20: error: expected unqualified-id before ‘double’
             $$ = sin($3);
                    ^~~~~~
parser.ypp:82:20: error: expected ‘)’ before ‘double’
parser.ypp:87:20: error: expected unqualified-id before ‘double’
             $$ = cos($3);
                    ^~~~~~
parser.ypp:87:20: error: expected ‘)’ before ‘double’
parser.ypp:92:20: error: expected unqualified-id before ‘double’
             $$ = tan($3);
                    ^~~~~~
parser.ypp:92:20: error: expected ‘)’ before ‘double’
parser.ypp:97:20: error: expected unqualified-id before ‘double’
             $$ = 1 / tan($3);
                    ^~~~~~
parser.ypp:97:20: error: expected ‘)’ before ‘double’
parser.ypp:102:20: error: expected unqualified-id before ‘double’
             $$ = asin($3);
                    ^~~~~~
parser.ypp:102:20: error: expected ‘)’ before ‘double’
parser.ypp:107:20: error: expected unqualified-id before ‘double’
             $$ = acos($3);
                    ^~~~~~
parser.ypp:107:20: error: expected ‘)’ before ‘double’
parser.ypp:112:20: error: expected unqualified-id before ‘double’
             $$ = atan($3);
                    ^~~~~~
parser.ypp:112:20: error: expected ‘)’ before ‘double’
parser.ypp:117:20: error: expected unqualified-id before ‘double’
             $$ = 1 / atan($3);
                    ^~~~~~
parser.ypp:117:20: error: expected ‘)’ before ‘double’
parser.ypp:122:20: error: expected unqualified-id before ‘double’
             $$ = log($3);
                    ^~~~~~
parser.ypp:122:20: error: expected ‘)’ before ‘double’
parser.ypp:127:20: error: expected unqualified-id before ‘double’
             $$ = log($5) / log($3);
                    ^~~~~~
parser.ypp:127:20: error: expected ‘)’ before ‘double’
parser.ypp:132:20: error: expected unqualified-id before ‘double’
             $$ = exp($3);
                    ^~~~~~
parser.ypp:132:20: error: expected ‘)’ before ‘double’
parser.ypp:137:20: error: expected unqualified-id before ‘double’
             $$ = sqrt($3);
                    ^~~~~~
parser.ypp:137:20: error: expected ‘)’ before ‘double’
parser.ypp:142:20: error: expected unqualified-id before ‘double’
             $$ = pow($1, $3);
                    ^~~~~~
parser.ypp:142:20: error: expected ‘)’ before ‘double’
parser.ypp:147:20: error: expected unqualified-id before ‘double’
             $$ = -$2;
                    ^~    
parser.ypp:147:20: error: expected ‘)’ before ‘double’
parser.ypp:152:20: error: expected unqualified-id before ‘double’
             $$ = fabs($3);
                    ^~~~~~
parser.ypp:152:20: error: expected ‘)’ before ‘double’
parser.ypp:157:20: error: expected unqualified-id before ‘double’
             $$ = 1;
                    ^     
parser.ypp:157:20: error: expected ‘)’ before ‘double’
parser.ypp:158:62: error: expected unqualified-id before ‘double’
             for (int i = 1; i <= static_cast<int>($1); i++) {
                                                              ^     
parser.ypp:158:62: error: expected ‘)’ before ‘double’
parser.ypp:158:70: error: expected ‘)’ before ‘;’ token
             for (int i = 1; i <= static_cast<int>($1); i++) {
                                                                      ^
parser.ypp:159:24: error: expected unqualified-id before ‘double’
                 $$ = $$ * i;
                        ^~~~~ 
parser.ypp:159:24: error: expected ‘)’ before ‘double’
parser.ypp:165:20: error: expected unqualified-id before ‘double’
             $$ = $2;
                    ^     
parser.ypp:165:20: error: expected ‘)’ before ‘double’
parser.ypp:170:20: error: expected unqualified-id before ‘double’
             $$ = $1;
                    ^     
parser.ypp:170:20: error: expected ‘)’ before ‘double’
Makefile:15: recipe for target 'parser.tab.o' failed
make: *** [parser.tab.o] Error 1

【问题讨论】:

  • 在一些与你得到的错误无关的注释上,我建议你阅读生成的头文件"parser.tab.hpp",因为这样你会看到yylval 不是一个指针。此外,您链接的库应该是 last 在您构建时(或至少在所有源文件和目标文件之后),并且您不需要在生成对象时添加链接器库文件。

标签: c++ parsing bison flex-lexer


【解决方案1】:

您的野牛文件定义了两种不同类型的符号:

%type <double> E
%token <double> NUMBER
%token <char> VARIABLE;

(第三行末尾的分号不正确,虽然我认为bison会忽略它。)

正如bison manual 中所讨论的,最常见的实现方式是使用%union bison 声明,它将YYSTYPE 声明为C 联合。这与使用预处理器将 #define YYSTYPE 作为单一类型不兼容。

如果使用%union 声明,则为YYSTYPE 的每个变体指定一个C 类型和一个标签。然后放在尖括号之间的是标签,而不是类型。例如,

%union {
     double number;
     char id;
}
%token <number> NUMBER

通过该声明,bison 将声明一个 union 类型,其中包含两个名为 numberid 的成员。它会通过附加成员选择器自动使$n 引用正确的成员(如果$n 引用的符号具有已知类型)。换句话说,如果$1 引用NUMBER,bison 将在生成的代码中将其替换为stack[top - 2 + 1].number。 (如果没有 %union 和类型声明,替换将类似于 stack[top - 2 + 1]。)

Bison 不要求您使用 %union。如果你不这样做并且你仍然为你的标记和非终端声明类型,bison 会假设你知道你在做什么,并且你已经以某种方式将 YYSTYPE 声明为复合类型。它将执行完全相同的替换,将成员选择器添加到对具有已知类型的堆栈值的引用。

因此,在您的情况下,您已经声明了 %type &lt;double&gt; E,因此野牛将在 $1 指代符号 E 的操作中将 $1 替换为 stack[top - 4 + 1].double 之类的东西。

当然,这会导致编译器阻塞。 double 是保留字,不能用作复合成员名称,而且无论如何YYSTYPE 是一个标量(double),因为这是您定义它的方式。

您还没有尝试编译您的扫描仪,但在那里您会遇到类似的问题。显然,YYSTYPE 在扫描器和解析器中必须是相同的类型,它们是单独的 C 翻译单元。如果您使用了 %union 声明,bison 会将 C union 放入 tje 生成的标头中,您在生成的扫描仪中 #include。这保证了一致性。如果您不使用%union,那么您必须确保扫描仪具有正确的定义。

由于您没有在 flex 文件中执行任何操作来声明 YYSTYPE,因此生成的代码将回退到 int。它通过包含这样的块来做到这一点:

#ifndef YYSTYPE
#define YYSTYPE int
#endif

野牛手册包含许多简单的示例。选项比我在这里介绍的要多得多,建议您花点时间阅读手册。

【讨论】:

  • 谢谢。首先我使用联合,它在终端中工作,但由于它在 Qt 中不起作用(仅在调试模式下工作,很奇怪),我试图更改我的词法分析器和解析器但失败了,如你所见。
  • @baksuz:我不知道 Qt 与任何事情有什么关系,而且我显然无法看到您对 union 的尝试是如何失败的。甚至你所说的“不起作用”。随意提出一个新问题,但请将您的代码缩减到查看问题所需的最低限度,如minimal reproducible example 上的 SO 帮助页面中所述。您的代码还有其他问题。从您想做的一小部分开始并逐渐添加更多功能总是好的。这样,您就可以让每个小部分都正常工作。