You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
} 

问题根源

  1. 类型存储截断:Bison的%union里只定义了int value,REAL_LITERAL用atof得到的double值存入int时直接丢失小数部分;符号表Symbols<int>也只能存整数,参数里的double转int后截断。
  2. 正则匹配顺序错误:Flex里{int}规则在{real}前面,导致像123.45这样的实数会先被匹配成整数123,剩下的.45被当成 lexical 错误。
  3. 结果类型错误:最终结果result是int类型,即使计算过程中用到了double(比如pow),最终赋值给int时还是会被截断。
  4. 语法规则类型统一为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 02:15:35