如何用Yacc编写最基础的解析器生成器?附Lex/Yacc示例与编译问题排查
极简Lex+Yacc联动程序问题说明
- 学习Lex/Flex的过程相对轻松,但入门Yacc/Bison时因为缺少足够轻量化的示例,整体理解门槛很高。目前公开的入门级Yacc示例大多为计算器类程序,对新手不够友好,单靠读复杂源码很难理解Yacc的核心工作逻辑,因此动手编写了一个最小化的Lex+Yacc联动程序。
程序核心逻辑非常简单:词法分析器匹配到固定字符串后向Yacc解析器返回对应标记,由Yacc侧的语法规则捕获标记后,打印对应的
Caught PRINT或Caught EXIT提示。
项目源文件
test.l(词法规则文件)
%{ #include "y.tab.h" %} %% "PRINT" { printf("Returning PRINT\n"); return PRINT; } "EXIT" { printf("Returning EXIT\n"); return EXIT; } . ; [ \t\n] ; %% int yywrap(void) { return 1; }
test.y(语法规则文件)
%{ int yylex(); #include <stdio.h> #include <stdlib.h> //#include "y.tab.h" %} %start line %token PRINT %token EXIT %% line: PRINT {printf("Caught PRINT\n");} | EXIT {printf("Caught EXIT\n");} | ; %% int main() { yyparse(); }
最初的编译尝试
自行摸索搭建了基础逻辑后,程序始终无法正常运行,最初采用的编译流程为:
- 执行
yacc -d test.y生成语法分析相关文件 - 执行
lex test.l生成词法分析相关文件 - 执行
gcc lex.yy.c y.tab.h -ll尝试生成可执行文件
问题排查与修复
经实际排查,编译流程的错误是程序无法正常运行的核心原因,排查过程中也参考了其他开发者的思路梳理,具体问题和修复方案如下:
- 原编译命令错误将头文件
y.tab.h作为编译输入,完全遗漏了Yacc生成的核心语法实现源码y.tab.c,导致编译链路缺失核心逻辑,程序无法正常运行 - 正确的编译步骤为:
- 运行
yacc -d test.y,生成包含语法分析核心逻辑的y.tab.c,以及包含标记常量定义的头文件y.tab.h - 运行
lex test.l,生成包含词法分析核心逻辑的lex.yy.c - 运行
gcc lex.yy.c y.tab.c -ll,将两个核心源码文件共同编译,链接Lex运行时库-ll,即可生成可正常运行的程序
- 运行
- 补充说明:
y.tab.h是供源码引用的头文件,不需要直接作为编译单元传给gcc,它的作用是在词法分析器编译时引入Yacc定义的标记值,保证词法侧返回的标记和语法侧的规则定义完全匹配。
内容的提问来源于stack exchange,提问作者HamzaKerem
相关产品推荐
相关产品推荐

