flex词法分析 bison语法分析
flex有两种使用方式,一种是flex单独做一个词法分析程序,另一种是flex和bison协同构建一个词法语法分析程序
我们在北大编译实践开源文档看到的显然就是第二种
一般来说,开发人员不需要关注flex和bison生成的.c文件,只需要关注.y文件和.l文件
flex语法:
一个.l文件的基本结构:
定义
%%
规则
%%
c语言辅助代码
这三部分的关系:
- 定义一般是include头文件
- 规则是正则表达式,flex会根据这部分来生成词法分析程序
- 辅助代码会在规则处被调用,每按照识别一个单词,要做一些action,action就会用到辅助代码
定义部分:
%{ c语言代码 %}
可以是头文件:
%{
#include <stdio.h>
}
也可以去定义一些宏、常量、变量
%{
int num=0;
int chars=0;
%}
规则部分:
pattern { action }
pattern就是正则表达式,action就是匹配成功后要执行的c语言代码
正则表达式的语法:
//这个图说的还有一些不太清楚,此处多做一些说明:
连接:
我们学编译原理的时候在纸上写正则表达式,连接就是两部分写成前后就行了,但flex中要用+号连接,比如(1-9)+(0-9)*表示一个不能以0开头的数字,
action可以用定义部分定义的东西,也可以用辅助代码
flex对任何没有匹配到的字符,会自动做原样输出操作。因此,对于那些不需要将其输出,而是需要将其忽略的pattern,应该进行匹配并做无操作处理。
词法分析和语法分析其实是两层抽象,词法分析是从单个字符到词的抽象,语法分析是从词到语法成分的抽象。最终要将源代码抽象成一个树形结构存放在内存中,然后遍历才能做中间代码生成和目标代码生成
感觉需要去复习正则表达式的书写
flex全局变量:
全局变量是自动定义在lex.yy.c中,如果你在别的文件中想用的话应外部声明
yyin:
定义在lex.yy.c文件中:
FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
Bison全局变量
yylval
- 由bison中的union来声明和定义,
- 用于在flex和bison之间传递语义值
用法是首先在bison里写一个union,
%union {
int intval;
char* strval;
ASTNode* node; // 直接使用定义在 %code requires 中的 ASTNode
}
因为是bison中的全局变量,所以flex中必须外部声明:
extern YYSTYPE yylval;
在 Flex 文件里,当识别到不同的词法单元时,需要将相应的语义值存储到 yylval 中,并返回对应的词法单元类型:
{DIGIT}+ {
yylval.intval = atoi(yytext); // 将匹配到的字符串转换为整数并存储到 yylval.intval 中
return INTC;
}
{ID} {
// 处理保留字
// ...
yylval.strval = strdup(yytext); // 复制匹配到的字符串并存储到 yylval.strval 中
return ID;
}
然后在 Bison 的语法规则里,可以通过 $n 来访问第 n 个输入符号的语义值,$$ 则表示当前规则左部的语义值,
expression: INTC {
$$ = createASTNodeForInt($1); // $1 表示 INTC 的语义值,即 yylval.intval
}
| ID {
$$ = createASTNodeForId($1); // $1 表示 ID 的语义值,即 yylval.strval
}
;
bison和flex协同工作:
正常来说应该先编写.y然后编写.l,然后交替调整两者,最终做出词法+语法分析器
bison -d -o MySNL_BI.c MySNL_BI.y
flex MySNL_LEX.l
gcc -o snlc MySNL_BI.c lex.yy.c -lm
bison核心功能就是产生一个yyparse函数,flex核心功能就是产生一个yylex函数
.y中应该外部声明yylex()函数,否则bison编译过不去:
extern int yylex();
你在.y文件中放这个声明,然后这个声明就会出现在bison.c中,yyparse会自动调用这个yylex(),如果你忘记放了那么bison -d -o MySNL_BI.c MySNL_BI.y这一步bison就不知道yylex是啥那么就过不去
应该先编译bison再编译flex因为flex要用到bison中的token结构,
先编译bison时,bison也会用到flex中的东西比如yylex()这个函数还有一些全局变量,但是这些东西用外部声明就可以,编译能过,链接的时候来处理这些
然后编译flex,flex要用到bison中的%token定义以及一些全局变量,token定义通过include"bison.tab.h"来引入,然后其他全局变量也是外部声明,
然后main.c,可能用到flex全局变量和bison全局变量,都用外部声明,然后就是yyparse这个函数也是外部声明。