Flex/Bison程序仅返回整数,无法正确输出实数的问题排查
问题描述
折腾一整天都没找出代码里的问题,用Flex和Bison写的编译器本该支持整数、实数、布尔值,但只要使用或输出实数,就会被自动转成整数,需要让程序正确返回实数值。
Flex代码
%{ #include <cstdio> #include <string> using namespace std; #include "values.h" #include "listing.h" #include "tokens.h" %} %option noyywrap ws [ \t\r]+ comment \-\-.*\n comment2 \/\/.*\n line [\n] id [A-Za-z](_?[A-Za-z0-9])* digit [0-9] int -?{digit}+ punc [(\),:;.] real {digit}+[.]{digit}*([eE]?[+-]?{digit}+)? %% {ws} { ECHO;} {comment} { ECHO; nextLine();} {comment2} {ECHO;nextLine();} {line} { ECHO; nextLine();} "=>" {ECHO; yylval.oper = ARROW; return(ARROP); } "<" { ECHO; yylval.oper = LESS; return(RELOP); } ">" { ECHO; yylval.oper = GREATER; return(RELOP); } "=" { ECHO; yylval.oper = EQUAL; return(RELOP); } "/=" { ECHO; yylval.oper = UNEQUAL; return(RELOP); } ">=" { ECHO; yylval.oper = GR_EQUAL; return(RELOP); } "<=" { ECHO; yylval.oper = LESS_EQUAL; return(RELOP); } "+" { ECHO; yylval.oper = ADD; return(ADDOP); } "-" { ECHO; yylval.oper = SUBTRACT; return(ADDOP); } "*" { ECHO; yylval.oper = MULTIPLY; return(MULOP); } "/" { ECHO; yylval.oper = DIVIDE; return(MULOP); } "**" { ECHO; yylval.oper = POWER; return(EXPOP); } "%" { ECHO; yylval.oper = REM; return(REMOP); } rem {ECHO; return(REMOP);} begin { ECHO; return(BEGIN_); } boolean { ECHO; return(BOOLEAN); } true { ECHO; yylval.value = TRUE; return(BOOL_LITERAL); } false { ECHO; yylval.value = FALSE; return(BOOL_LITERAL); } or { ECHO; return(OROP); } end { ECHO; return(END); } endreduce { ECHO; return(ENDREDUCE); } function { ECHO; return(FUNCTION); } integer { ECHO; return(INTEGER); } is { ECHO; return(IS); } reduce { ECHO; return(REDUCE); } returns { ECHO; return(RETURNS); } not { ECHO; return(NOTOP);} case { ECHO; return(CASE); } else { ECHO; return(ELSE); } endcase { ECHO; return(ENDCASE); } endif { ECHO; return(ENDIF); } if { ECHO; return(IF); } others { ECHO; return(OTHERS); } real { ECHO; return(REAL); } then { ECHO; return(THEN); } when { ECHO; return(WHEN); } and { ECHO; return(ANDOP); } {id} { ECHO; yylval.iden = (CharPtr)malloc(yyleng + 1); strcpy(yylval.iden, yytext); return(IDENTIFIER);} {int} { ECHO; yylval.value = atoi(yytext); return(INT_LITERAL); } {punc} { ECHO; return(yytext[0]); } {real} { ECHO; yylval.value = atof(yytext); return(REAL_LITERAL); } . { ECHO; appendError(LEXICAL, yytext); } %%
Bison代码
%{ #include <iostream> #include <string> #include <vector> #include <map> #include <cassert> #include <cstdlib> #include <queue> #include <math.h> using namespace std; #include "values.h" #include "listing.h" #include "symbols.h" int yylex(); void yyerror(const char* message); Symbols<int> symbols; int result; double * params; %} %define parse.error verbose %union { CharPtr iden; Operators oper; int value; } %token <iden> IDENTIFIER %token <value> INT_LITERAL REAL_LITERAL BOOL_LITERAL TRUE FALSE %token <oper> ADDOP MULOP RELOP OROP NOTOP REMOP EXPOP %token ANDOP %token BEGIN_ BOOLEAN END ENDREDUCE FUNCTION INTEGER IS REDUCE RETURNS %token THEN WHEN %token ELSE ENDCASE ENDIF IF OTHERS REAL CASE ARROP %type <value> body statement_ statement reductions unary exponent expression binary relation term factor primary case cases %type <oper> operator %% function: function_header optional_variable body {result = $3;} ; function_header: FUNCTION IDENTIFIER parameters RETURNS type ';' | error ';'; optional_variable: optional_variable variable | ; variable: IDENTIFIER ':' type IS statement_ {symbols.insert($1, $5);} | error ';' ; parameters: parameter optional_parameter; optional_parameter: optional_parameter ',' parameter | ; parameter: IDENTIFIER ':' type {symbols.insert($1, params[0]);} ; type: INTEGER | REAL | BOOLEAN ; body: BEGIN_ statement_ END ';' {$$ = $2;} ; statement_: statement ';' | error ';' {$$ = 0;} ; statement: expression | REDUCE operator reductions ENDREDUCE {$$ = $3;} | IF expression THEN statement_ ELSE statement_ ENDIF {if ($2 == true) { $$=$4; } else { $$=$6; } } ; /* | CASE expression IS cases OTHERS ARROW statement_ ENDCASE {$$ = $<value>4 == 1 ? 4 : 7;} ; cases: cases case {$$ = $<value>1 == 1 ? 1 : 2;} | %empty {$$ = <isnan>;;}; case: case WHEN INT_LITERAL ARROW statement_ | ; */ operator: ADDOP | RELOP | EXPOP | MULOP ; reductions: reductions statement_ {$$ = evaluateReduction($<oper>0, $1, $2);} | {$$ = $<oper>0 == ADD ? 0 : 1;} ; expression: expression OROP binary {$$ = $1 || $3;} | binary; binary: binary ANDOP relation {$$ = $1 && $3;} | relation ; relation: relation RELOP term {$$ = evaluateRelational($1, $2, $3);} | term ; term: term ADDOP factor {$$ = evaluateArithmetic($1, $2, $3);} | factor ; factor: factor MULOP exponent {$$ = evaluateArithmetic($1, $2, $3);} | factor REMOP exponent {$$ = $1 % $3; } | exponent ; exponent: unary | unary EXPOP exponent { $$=pow($1, $3); } ; unary: NOTOP primary {$$ = $2;}| primary; primary: '(' expression ')' {$$ = $2;} | INT_LITERAL | REAL_LITERAL | BOOL_LITERAL | IDENTIFIER {if (!symbols.find($1, $$)) appendError(UNDECLARED, $1);} ; %% void yyerror(const char* message) { appendError(SYNTAX, message); } int main(int argc, char *argv[]) { params = new double[argc - 1]; for (int i = 1; i < argc; i++) { params[i - 1] = atof(argv[i]); } firstLine(); yyparse(); if (lastLine() == 0) cout << "Result = " << result << endl; return 0; }
问题根源
- 类型存储截断:Bison的
%union里只定义了int value,REAL_LITERAL用atof得到的double值存入int时直接丢失小数部分;符号表Symbols<int>也只能存整数,参数里的double转int后截断。 - 正则匹配顺序错误:Flex里
{int}规则在{real}前面,导致像123.45这样的实数会先被匹配成整数123,剩下的.45被当成 lexical 错误。 - 结果类型错误:最终结果
result是int类型,即使计算过程中用到了double(比如pow),最终赋值给int时还是会被截断。 - 语法规则类型统一为int:所有数值计算相关的非终结符都绑定到
<value>(int类型),导致中间计算结果的小数部分丢失。
修复方案
1. 调整Flex正则规则顺序
把{real}的匹配规则移到{int}前面,确保实数能被正确识别:
{real} { ECHO; yylval.real_val = atof(yytext); return(REAL_LITERAL); } {int} { ECHO; yylval.int_val = atoi(yytext); return(INT_LITERAL); }
2. 修改Bison的类型定义
更新%union,区分整数、实数、布尔值的存储:
%union { CharPtr iden; Operators oper; int int_val; double real_val; bool bool_val; }
对应调整token的类型绑定:
%token <iden> IDENTIFIER %token <int_val> INT_LITERAL %token <real_val> REAL_LITERAL %token <bool_val> BOOL_LITERAL TRUE FALSE %token <oper> ADDOP MULOP RELOP OROP NOTOP REMOP EXPOP
数值计算相关的非终结符改为<real_val>,布尔逻辑相关的改为<bool_val>:
%type <real_val> body statement_ statement reductions unary exponent term factor primary %type <bool_val> expression binary relation %type <oper> operator
3. 修正符号表和结果类型
把符号表改成支持double存储,结果变量改为double:
Symbols<double> symbols; double result;
参数插入符号表时直接存double:
parameter: IDENTIFIER ':' type {symbols.insert($1, params[0]);} ;
4. 调整语法规则的类型转换
比如primary规则里,整数转成double参与计算:
primary: '(' expression ')' {$$ = $2;} | INT_LITERAL {$$ = static_cast<double>($1);} | REAL_LITERAL {$$ = $1;} | BOOL_LITERAL {$$ = $1 ? 1.0 : 0.0; /* 根据需求调整布尔值转数值的逻辑 */} | IDENTIFIER {if (!symbols.find($1, $$)) appendError(UNDECLARED, $1);} ;
布尔表达式规则里,基于数值判断布尔值:
relation: relation RELOP term {$$ = evaluateRelational($1, $2, $3);} | term {$$ = $1 != 0;} ;
5. 修正Flex的yylval赋值
对应%union的新字段修改赋值:
true { ECHO; yylval.bool_val = true; return(BOOL_LITERAL); } false { ECHO; yylval.bool_val = false; return(BOOL_LITERAL); } {int} { ECHO; yylval.int_val = atoi(yytext); return(INT_LITERAL); } {real} { ECHO; yylval.real_val = atof(yytext); return(REAL_LITERAL); }
6. 调整输出格式
输出结果时用double格式:
cout << "Result = " << result << endl;
内容的提问来源于stack exchange,提问作者user2097371
相关产品推荐
相关产品推荐

