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

基于Union与Struct的Bison简单后缀计算器:变量赋值实现求助

实现支持变量赋值的Bison后缀表达式计算器

看起来你已经抓准了核心思路——用%union处理多类型存储、用结构体统一节点类型确实是解决这个问题的关键。我来帮你把这些想法落地成可运行的代码,分Bison解析器和Lex词法分析器两部分说明:

1. Bison解析器核心修改(calc.y)

首先定义一个Node结构体来统一表达式节点的类型(整数或变量),再通过%union让Bison的语义值能承载这个结构体指针,这样不管是整数还是变量,都能通过节点传递和处理:

%{
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

// 定义统一的表达式节点结构体,区分整数和变量类型
typedef struct {
    enum { INT_VAL, VAR_VAL } type;
    int num;    // 存储整数值
    char var;   // 存储变量名(a-z)
} Node;

// 辅助函数:创建整数节点
Node* create_int_node(int num) {
    Node* node = (Node*)malloc(sizeof(Node));
    node->type = INT_VAL;
    node->num = num;
    return node;
}

// 辅助函数:创建变量节点
Node* create_var_node(char var) {
    Node* node = (Node*)malloc(sizeof(Node));
    node->type = VAR_VAL;
    node->var = var;
    return node;
}

// 辅助函数:释放节点内存(学习阶段可省略,但养成好习惯)
void free_node(Node* node) {
    free(node);
}

// 声明Lex需要用到的节点创建函数(避免编译报错)
extern Node* create_int_node(int);
extern Node* create_var_node(char);
%}

// 语义值的union,统一用Node指针
%union {
    Node* node;
}

// 声明终结符和非终结符的类型
%token <node> NUMBER VAR
%type <node> expr assign_stmt

// 算术运算符优先级(保持常规顺序)
%left '+' '-'
%left '*' '/'

%%

// 程序入口:处理多条语句,每条以分号结尾
program:
    program stmt ';'   { printf("\n"); }  // 每条语句结束后换行
    | /* 空 */         { /* 程序初始化 */ }
    ;

// 语句可以是赋值语句或纯表达式
stmt:
    assign_stmt        { free_node($1); }  // 释放赋值语句的节点
    | expr             { free_node($1); }  // 释放纯表达式的节点
    ;

// 赋值语句规则:变量 = 表达式
assign_stmt:
    VAR '=' expr       { 
        // 输出变量名 → 表达式的后缀序列 → 赋值运算符"+="
        printf("%c", $1->var); 
        printf("+=");
        $$ = $1;
        free_node($3);  // 释放表达式节点
    }
    ;

// 表达式规则:覆盖整数、变量、算术运算和括号
expr:
    NUMBER             { 
        printf("%d", $1->num);  // 输出整数值
        $$ = $1;
    }
    | VAR              { 
        printf("%c", $1->var);  // 输出变量名
        $$ = $1;
    }
    | expr '+' expr    { 
        printf("+");  // 输出加法运算符
        $$ = create_int_node($1->num + $3->num);  // 若需要计算结果则保留,仅输出后缀可忽略
        free_node($1);
        free_node($3);
    }
    | expr '-' expr    { 
        printf("-");
        $$ = create_int_node($1->num - $3->num);
        free_node($1);
        free_node($3);
    }
    | expr '*' expr    { 
        printf("*");
        $$ = create_int_node($1->num * $3->num);
        free_node($1);
        free_node($3);
    }
    | expr '/' expr    { 
        printf("/");
        $$ = create_int_node($1->num / $3->num);  // 注意:未处理除零错误,可自行补充
        free_node($1);
        free_node($3);
    }
    | '(' expr ')'     { 
        $$ = $2;  // 括号不输出,直接返回内部表达式节点
    }
    ;

%%

// 错误处理函数
void yyerror(const char* msg) {
    fprintf(stderr, "Error: %s\n", msg);
}

// 主函数
int main() {
    printf("Enter expressions (end with Ctrl+D):\n");
    return yyparse();
}

2. Lex词法分析器补充(calc.l)

接下来需要让Lex识别小写字母作为变量、数字作为整数,以及运算符和分隔符:

%{
#include "calc.tab.h"  // 包含Bison生成的头文件
#include <stdlib.h>
#include <ctype.h>

// 声明Bison里的节点创建函数
Node* create_int_node(int);
Node* create_var_node(char);
%}

%%

[a-z]           { 
                    // 识别小写字母为变量,创建变量节点
                    yylval.node = create_var_node(yytext[0]);
                    return VAR; 
                }
[0-9]+          { 
                    // 识别数字序列为整数,创建整数节点
                    yylval.node = create_int_node(atoi(yytext));
                    return NUMBER; 
                }
[+\-*/=();]     { return yytext[0]; }  // 直接返回运算符和分隔符
[ \t\n]         { /* 忽略空白字符 */ }
.               { yyerror("Invalid character"); }  // 处理非法字符

%%

// Lex的wrap函数,默认返回1表示输入结束
int yywrap() {
    return 1;
}

3. 编译与测试

编译命令(假设文件名为calc.y和calc.l):

bison -d calc.y
flex calc.l
gcc calc.tab.c lex.yy.c -o calc -Wall

测试输入:

a=3+2;
b=a*5;

输出结果:

a32+=
ba5*+=

完全符合你想要的后缀表达式格式!

关键说明

  • 用结构体Node统一节点类型,解决了表达式既可以是整数又可以是变量的多类型存储问题。
  • 赋值语句的动作里,按「变量名 → 表达式后缀 → 赋值运算符」的顺序输出,刚好匹配你要的a32+=格式。
  • 如果后续需要支持变量取值(比如b=a+5),当前代码已经自动处理,因为变量在表达式中会直接输出变量名。
  • 内存管理部分做了简单的节点释放,学习阶段可以根据需求调整,不用太纠结细节。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 09:15:29