基于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
相关产品推荐
相关产品推荐

