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

Flex C++中yytext函数复制内容异常的技术求助

Flex中yytext无法正确复制变量名的问题排查与解决

问题描述

使用Flex+Yacc开发时,遇到yytext无法正确捕获变量名的问题:变量名以@开头,这本身不影响解析,但符号表中仅能正确保存逗号后的变量名,其余情况(如分号、括号前的变量)都将分隔符(;、)、,)当成变量名存入,导致符号表数据错误。

相关代码

.l文件核心代码

progr: declaratii bloc {printf("program corect sintactic\n");}
     ;

declaratii :  declaratie ';'
       | declaratii declaratie ';'
       | declaratii structura 
       | declaratii clasa ';'
       | declaratii function 
       | function
       | clasa ';'
       | structura 
       ;

declaratie : INT var_int 
           | INT lista_int
           | CHAR var_char
           | CHAR lista_char
           | FLOAT var_float
           | FLOAT lista_float
           | STRING var_string
           | STRING lista_string
           | BOOL var_bool
           | BOOL lista_bool
           ;

lista_int : ID '(' lista_param_int ')' {add("lista", "int");}
     | ID '(' ')' {add("lista", "int");}
     | lista_int ',' ID '(' lista_param_int ')' {add("lista", "int");}
     | lista_int ',' ID '(' ')' {add("lista", "int");}
     ;
var_int : ID {add("var", "int");}
     | var_int ','  ID {add("var", "int");}
     ;
lista_param_int : INT ID 
            | lista_param_int ',' INT ID 
            ;

lista_char : ID '(' lista_param_char ')' {add("lista", "char");}
     | ID '(' ')' {add("lista", "char");}
     | lista_char ',' ID '(' lista_param_char ')' {add("lista", "char");}
     | lista_char ',' ID '(' ')' {add("lista", "char");}
     ;


void add(char * tip, char * tipDate){
     symbol_table[contor].nume = strdup(yytext) ;
     symbol_table[contor].tip = tip;
     symbol_table[contor].tipDate = tipDate;
     contor++;
}

输入文本

class Test{
    private:
        int @x;
    public:
        int @z;
    protected:
        char @o;
        int @tr2;
    protected:
        float @k;
        int @dasdas(int @var23);
};
struct structura{
    char @nume;
    char @tip;
};

float functie($int @var0 : char @sir : bool @binar){
    $int @var1;
    $@var1 := 1;
    $if'(@var1 :== 1){
        @var1 := 2;
        if'(@var1 :== 2){
            @var1 := 3;
        }
    }
    $@var1 := 8;
    $int @var2;
    $int @var3, @var4;
    $@var2 := 6;
}

int @a, @b, @c;
int @xz, @dsada;
int @d(), @xsadas(int @arata);
char @c;
begin_progr
@a := 3;
if'(@a :== 3){
    @a := 2;
}
end_progr

错误输出

nume      tip      tipDate
;          var      int      
;          var      int      
;          var      char      
;          var      int      
;          var      float      
)          lista      int      
;          var      char      
;          var      char      
:          var      int      
:          var      char      
)          var      bool      
;          var      int      
;          var      int      
,          var      int      
@var4          var      int      
,          var      int      
@b          var      int      
@c          var      int      
,          var      int      
@dsada          var      int      
)          lista      int      
)          lista      int      
;          var      char   

原因分析

yytext是Flex内部的缓冲区指针,它始终指向当前刚匹配完成的Token。而Yacc的动作是在整个规则匹配完成后才执行,此时Flex已经读取了下一个Token(比如分号、逗号、括号),yytext已经被更新为下一个Token的内容,导致动作中调用strdup(yytext)时,复制的是分隔符而非目标变量名。

比如在规则var_int : ID {add(...);}中,当执行add时,Flex已经匹配了ID后面的分号,yytext此时指向分号,而非之前的ID。

解决方案

通过Yacc的语义值传递保存Token内容,避免依赖yytext的即时值:

1. 修改Yacc定义,添加语义值存储

在Yacc文件的开头定义语义值联合体,并声明ID的语义类型:

%union {
    char* str;
}

%token <str> ID
%token INT CHAR FLOAT STRING BOOL CLASS STRUCT FUNCTION BEGIN_PROGR END_PROGR
// 其他Token声明...

2. 修改Flex规则,保存ID的字符串值

在Flex的匹配规则中,将ID的内容复制到语义值中,再返回Token:

@[a-zA-Z0-9]+    { 
    yylval.str = strdup(yytext); 
    return ID; 
}

3. 修改Yacc规则动作,使用语义值而非yytext

更新所有调用add的动作,传递ID的语义值:

var_int : ID {add("var", "int", $1);}
     | var_int ','  ID {add("var", "int", $3);}
     ;

lista_int : ID '(' lista_param_int ')' {add("lista", "int", $1);}
     | ID '(' ')' {add("lista", "int", $1);}
     | lista_int ',' ID '(' lista_param_int ')' {add("lista", "int", $3);}
     | lista_int ',' ID '(' ')' {add("lista", "int", $3);}
     ;

lista_param_int : INT ID {add("var", "int", $2);}
            | lista_param_int ',' INT ID {add("var", "int", $4);}
            ;

4. 修改add函数,接收变量名参数

更新add函数,直接使用传入的变量名字符串:

void add(char * tip, char * tipDate, char* nume){
     symbol_table[contor].nume = nume;
     symbol_table[contor].tip = tip;
     symbol_table[contor].tipDate = tipDate;
     contor++;
}

5. 注意内存管理

由于使用了strdup,后续需要在合适的时机(比如符号表销毁时)释放这些字符串,避免内存泄漏。

内容的提问来源于stack exchange,提问作者fabi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 10:05:33