一些比较好的资料:
Bison进行语法分析几个步骤
编写Bison源代码文件:syntax.y
编译该源代码:bison syntax.y
编译好的结果为:syntax.yy.c
,该C语言源代码中有用的函数是yyparse()
yyparse():作用是对输入文件进行语法分析,分析成功没有错误返回0,否则返回非0
除此之外,Bison还需要获得词法单元,通过yylex()
,它会返回词法单元——使用Flex生成的yylex函数来进行词法分析,yylex()相当于嵌在Bison里的词法分析程序。
怎么使用?在syntax.y文件的第三部分函数部分加上一句#include "lex.yy.c"
yylex()函数实际上是由Flex生成的词法分析器(即编译flex文件生成的lex.yy.c源代码)中的函数。
在生成的词法分析器源代码(通常是lex.yy.c)中,Flex会自动生成一个名为yylex()的函数,用于执行词法分析的工作。这个函数会根据在Flex文件中定义的规则进行匹配,并返回相应的词法单元。
因此,在Bison的驱动程序中,需要包含生成的词法分析器源代码的头文件(#include “lex.yy.c”),以便能够调用yylex()函数来获取词法单元。
重新编译Bison源文件:bison -d syntax.y
-d这个参数的含义是,将编译的结果分拆成syntax.tab.c和syntax.tab.h两个文件,其中.h文件里包含着一些词法单元的类型定义之类的内容。
接着需要修改flex源文件并重新编译其,使得flex中:
- 引用syntax.tab.h,通过
#include "syntax.tab.h"
- 能够返回在syntax.y中有定义的词法单元,通过修改规则部分,return词法单元
最后连接在一起:
gcc main.c syntax.tab.c -lfl -ly -o parser
如果main函数没有单独写一个文件则不需要加上main.c
由于lex.yyc.已经在syntax.tab.c中引用了,所以这里不需要再写上这个文件。
如果-ly报错:
sudo apt-get install libbison-dev
总结:
- 需要编写bison源代码syntax.y,并且在其中引用lex.yy.c以获得词法单元,syntax.y中也要相应的定义词法单元(否则在flex中写没有定义的return词法单元,会报错)
- flex要引用syntax.tab.h,并且修改规则,使得其可以返回词法单元给语法分析器parser()函数
- 最后文件放在一起编译,使得文件之间连接起来
yywrap()
yywrap()函数: 如果yywrap()函数返回0,则词法分析器会继续读取下一个输入流,继续进行词法分析。
如果yywrap()函数返回非零值(通常是1),则词法分析器将停止分析,认为已经完成了所有输入。
词法分析器在扫描当前输入流中的词法单元时,通常会根据一定的规则来确定输入流的结束。具体的结束规则取决于词法分析器的设计和需求。
在典型的情况下,词法分析器会根据以下规则来确定输入流的结束:
遇到文件结束符(EOF):当词法分析器从输入流中读取到文件结束符时,即表示输入流结束。
遇到特定的终止符号:有时,词法分析器在扫描输入流时会使用特定的终止符号来表示输入流的结束。例如,在某些编程语言中,分号 (😉
可以表示语句的结束。当词法分析器遇到分号时,可以将其视为输入流的结束。遇到特定的分隔符:词法分析器可能根据特定的分隔符来确定输入流的结束。例如,词法分析器可以将换行符 (\n)
视为输入流的结束符,即一行输入的结束。需要注意的是,词法分析器并不是直接处理输入流的,而是从输入流中逐个读取字符,并将其组合成词法单元。因此,词法分析器通常是在每次调用yylex()函数时检查输入流是否结束,并根据相应的规则来判断。
如果希望在遇到换行符时结束输入流,可以在yywrap()函数中添加适当的逻辑来判断是否遇到了换行符,然后返回相应的值。
编写Bison源代码syntax.y
主要是四个部分:
%{
// 第一部分-放在定义部分前面,可以在此处声明变量、函数或头文件等,这部分内容会被原封不动拷贝到syntax.tab.c.的最前面
...
%}
// 第二部分-定义部分,可以在这里定义词法单元(要从flex文件中return的词法单元必须在此处有定义)、规定算符结合性和优先级、
...
// 可能包含的有:
%union{} // 将所有可能的类型包含进去
%token // 定义词法单元(终结符)
// 规定结合性,规定优先级:Bison规定放在更前面的优先级更高
%nonassoc // 不可结合,可以用来自定义算符及其优先级(通过放的前后)
%left // 左结合
%right // 右结合
%%
// 第三部分-规则部分:包括具体的语法和相应的语义动作
...
%%
// 第四部分-用户函数部分:这部分代码会被原封不动地拷贝到syntax.tab.c中
...