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

使用Bison将含标识符的公式转换为AST时出错的解决方法

问题:Bison解析含变量公式时AST构建异常

我正在尝试用Bison把数学公式转换成节点树形式的抽象语法树(AST),已经定义了节点结构体、创建函数和遍历函数,但处理含变量的公式时,AST的输出不符合预期,下面是详细的代码和问题情况:

节点结构体与创建函数

typedef enum {null, opera, var, val} NodeType;
typedef struct Node {
    float val;
    char * name; // val_name
    char * opr; // operator
    NodeType node_type;
    struct Node * left;
    struct Node * right;
} Node;

Node create_op_node(char * op){
    Node node;
    node.opr = op;
    node.node_type = opera;
    return node;
}

Node create_var_node(char * op){
    Node node;
    node.name = op;
    node.node_type = var;
    return node;
}

Node create_val_node(float value){
    Node node;
    node.val = value;
    node.node_type = val;
    return node;
}

树遍历函数

void run_through_tree (Node *node){
    switch (node->node_type){
        case var:
            printf("varable name:%s\n",node->name);
            break;
        case val:
            printf("num value:%f\n",node->val);
            break;
        case opera:
            printf("\noperator:%s\n",node->opr);
            printf("LFS\n");
            run_through_tree(node->left);
            printf("RHS\n");
            run_through_tree(node->right);
    }
}

Bison语法规则

S : S E T_NEWLINE { main_node = $2;run_through_tree(main_node);} // run through the node tree
  | {} ;

E : E T_ADD E 
    {Node * op_node = (Node *) malloc(sizeof(Node));
     *op_node = create_op_node("+");
     connect_node(op_node,$1,$3);
     $$ = op_node;}
  | E T_SUB E 
    {Node * op_node = (Node *) malloc(sizeof(Node));
     *op_node = create_op_node("-");
     connect_node(op_node,$1,$3);
     $$ = op_node;}
  | E T_MUL E 
    {Node * op_node = (Node *) malloc(sizeof(Node));
     *op_node = create_op_node("*");
     connect_node(op_node,$1,$3);
     $$ = op_node;}
  | E T_DIV E 
    {Node * op_node = (Node *) malloc(sizeof(Node));
     *op_node = create_op_node("/");
     connect_node(op_node,$1,$3);
     $$ = op_node;}
  | T_SUB E %prec NEG 
    { Node * op_node = (Node *) malloc(sizeof(Node));
      Node * zero_node = (Node *) malloc(sizeof(Node));
      * zero_node = create_val_node(0.0);
      * op_node = create_op_node("-");
      connect_node(op_node,zero_node,$2);
      $$ = op_node;}
  | T_NUM 
    {Node * val_node = (Node *) malloc(sizeof(Node));
     * val_node = create_val_node($1);
     $$ = val_node;}
  | T_VAR 
    {Node * var_node = (Node *) malloc(sizeof(Node));
     * var_node = create_var_node($1);
     $$ = var_node;}
  | T_LPATH E T_RPATH {$$ = $2;} ;

问题现象

正常情况(无变量公式)

输入2 * ( 3 + 2)时,输出符合预期:

operator:*
LFS
num value:2.000000
RHS
operator:+
LFS
num value:3.000000
RHS
num value:2.000000

异常情况(含变量公式)

输入2 * a - a + 7时,实际输出不符合预期:

operator:-
LFS
operator:*
LFS
num value:2.000000
RHS
varable name:a - a + 7
RHS
varable name:a + 7
RHS
num value:7.000000

预期输出应该是:

operator:-
LFS
operator:*
LFS
num value:2.000000
RHS
varable name:a
RHS
varable name:a
RHS
num value:7.000000

问题分析与解决方案

从实际输出里的varable name:a - a + 7能直接看出:词法分析器(Flex)没有正确识别单个变量名,它错误地把a后面的空格、运算符和其他内容都当成了变量名的一部分。这是问题的核心原因。

1. 修正Flex的词法规则

你需要确保Flex能正确区分变量名、运算符和空白字符,给出参考规则:

// 忽略空白字符(空格、制表符、换行)
[ \t\n]+                  { /* do nothing */ }

// 运算符单独识别
"+"                       { return T_ADD; }
"-"                       { return T_SUB; }
"*"                       { return T_MUL; }
"/"                       { return T_DIV; }
"("                       { return T_LPATH; }
")"                       { return T_RPATH; }

// 变量名:字母/下划线开头,后续可跟字母/数字/下划线
[a-zA-Z_][a-zA-Z0-9_]*    { yylval.str = strdup(yytext); return T_VAR; }

// 数字(整数或浮点数)
[0-9]+(\.[0-9]+)?         { yylval.num = atof(yytext); return T_NUM; }

关键要保证:空白字符被单独忽略,运算符被识别为独立的词法单元,变量名的正则表达式不会匹配到非变量字符。

2. 修复变量名的内存管理问题

当前create_var_node直接使用传入的op指针,如果这个指针指向的是Flex的yytext临时缓冲区,后续会被覆盖导致野指针。需要复制字符串:

Node create_var_node(char * op){
    Node node;
    node.name = strdup(op); // 复制字符串,避免临时缓冲区被覆盖
    node.node_type = var;
    return node;
}

记得后续销毁AST时要释放这些复制的字符串,避免内存泄漏。

3. 确认Bison的优先级与结合性

为了保证表达式运算顺序正确(比如乘法优先级高于加减),需要在Bison文件顶部明确优先级:

%left T_ADD T_SUB       // 加减优先级相同,左结合
%left T_MUL T_DIV       // 乘除优先级相同,左结合
%nonassoc NEG           // 负号(一元运算符)的优先级高于乘除

验证修正效果

当Flex规则正确后,输入2 * a - a + 7会被拆分为正确的词法单元:T_NUM(2)、T_MUL、T_VAR(a)、T_SUB、T_VAR(a)、T_ADD、T_NUM(7),Bison就能按照语法规则构建出符合预期的AST,遍历输出也会和预期一致。


内容的提问来源于stack exchange,提问作者Tan Kian-teng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:15:01