如何使用lex与yacc实现支持字符变量的表达式解析器
问题排查与修改方案
1. Flex端核心问题
- 标识符匹配逻辑错误:当前匹配到字母串后直接返回
*yytext,仅返回了第一个字符的ASCII值,没有给Bison返回约定好的idtoken,也没有传递完整的标识符字符串 - 重复定义main函数:Flex和Bison文件中都定义了main函数,编译时会出现符号冲突
- 头文件兼容问题:引入了C++的
<string>头,但默认生成C代码,无对应使用场景可直接删除
2. Bison端核心问题
- 未指定语义值类型:默认语义值
$$/$1/$3等为int类型,直接相加只会计算ASCII码之和,无法实现字符串拼接/表达式转换的需求 - 运算符对应逻辑错误:
term规则匹配的是*运算符,但原代码的语义动作和加法完全一致,不符合需求 - 无效语法规则:
factor中的id id规则不符合常规表达式语法,也和「末尾插入标识符值」的需求不匹配 - 没有输出最终解析结果:expr作为根节点,没有打印最终的处理结果,无法验证解析是否正确
修改后完整代码
Flex文件(保存为lexer.l)
%{ #include <stdio.h> #include <stdlib.h> #include "y.tab.h" #include <string.h> %} alpha [a-zA-Z] %% {alpha}+ { yylval.str = strdup(yytext); return id; } "+" return '+'; "*" return '*'; [()] return *yytext; [ \t\n] ; %% int yywrap() { return 1; }
Bison文件(保存为parser.y)
%{ #include <stdio.h> #include <stdlib.h> #include <string.h> int yyerror(const char* msg); int yylex(); %} %union { char* str; } %token <str> id %type <str> expr term factor %left '+' %left '*' %% expr : expr '+' term { asprintf(&$$, "(%s+%s)", $1, $3); free($1); free($3); } | term { $$ = $1; } ; term : term '*' factor { asprintf(&$$, "(%s*%s)", $1, $3); free($1); free($3); } | factor { $$ = $1; } ; factor : '(' expr ')' { $$ = $2; } | id { $$ = strdup($1); } ; %% void yyerror(const char* msg) { printf("解析错误: %s\n", msg); } // 标识符值映射表,可按需扩展自定义值 int get_id_value(char* id) { if(strcmp(id, "a") == 0) return 1; if(strcmp(id, "b") == 0) return 2; if(strcmp(id, "c") == 0) return 3; return 0; } // 末尾插入标识符值计算逻辑,可按需调整处理规则 int calculate_expr(char* expr_str) { printf("解析后的表达式: %s\n", expr_str); printf("代入预设值计算:a=1, b=2, c=3,结果为%d\n", (get_id_value("a")+get_id_value("b"))*get_id_value("c")); return 0; } int main() { char* res; yyparse(&res); calculate_expr(res); free(res); return 0; }
编译运行步骤
- 执行
bison -d parser.y生成y.tab.c和y.tab.h - 执行
flex lexer.l生成lex.yy.c - 执行
gcc y.tab.c lex.yy.c -o expr_parser编译生成可执行文件 - 运行后输入
(a+b)*c即可看到解析和计算结果
内容的提问来源于stack exchange,提问作者kim
相关产品推荐
相关产品推荐

