lex和yacc是什么
lex是一个生成词法分析器的程序,flex是其开源版本。
所谓生成词法分析器,就是按照你的意愿,遇到某个词法时执行某个动作
yacc时生成编译器的编译器,生成的主要是编译器的词法解析器。配合yacc使用,当解析到某种语法时执行一定的动作。
lex的开源版本为flex,yacc对应gnu的bison
kali安装
1
| sudo apt install flex bison
|
lex使用简单介绍
lex文件格式为*.l,例子:
1 2 3 4 5 6 7
| %{ #include <stdio.h> %} %% stop printf("Stop command received\n"); start printf("Start command received\n"); %%
|
保存,然后执行
会在同目录下生成lex.yy.c
执行命令
1
| cc lex.yy.c -ll -o a # -ll代表使用lex库,提供了main函数,-o a代表输出到
|
运行a程序,发现输入stop和start时会打印对应的东西,其他情况回显
lex使用正则表达式
yacc使用
yacc语法分析器解析一个标识流符,这里就需要lex配合了。因为yacc并不知道输入流中的字符是什么东西。
假定有一个温度计,使用如下语言控制它
1 2 3 4 5
| heat on Heater on! heat off Header off! target temperature set!
|
lex的tokenizer为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| %{ #include <stdio.h> #include "y.tab.h" %}
%% [0-9]+ return NUMBER; heat return TOKHEAT; on|off return STATE; target return TOKTARGET; temperature return TOKTEMPERATURE; \n ; [ \t]+ %%ure set\n"); };
|
y.tab.h中定义了NUMBER,TOKHEAT这些常量,这个文件由yacc根据我们编写的语法规则生成
yacc的文件后缀名为*.y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| %{ #include <stdio.h> #include <string.h>
extern int yylex(void);
extern int yyparse(void);
int yywrap() { return 1; }
void yyerror(const char *s) { printf("[error] %s\n", s); }
int main() { yyparse(); return 0; } %}
%token NUMBER TOKHEAT STATE TOKTARGET TOKTEMPERATURE
%% commands: | commands command ;
command: heat_switch | target_set ;
heat_switch: TOKHEAT STATE { printf("\tHeat turned on or off\n"); };
target_set: TOKTARGET TOKTEMPERATURE NUMBER { printf("\tTemperature set\n"); }; %%
|
编译运行这个程序
1 2 3
| lex a.l yacc -d a.y # 增加-d选项以生成y.tab.h cc lex.yy.c y.tab.c -o a
|
现在为止,已经具备一个编译器的基本单元。但是考虑这个问题:lex识别了一个标识符,但是C语言中不同的标识符有不同的含义,所以还需要为它生成一个ID,为语义分析做准备。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| %{ #include <stdio.h> #include "y.tab.h" %}
%% [0-9]+ yylval = atoi(yytext); return NUMBER; heat return TOKHEAT; on|off yylval = !strcmp(yytext, "on"); return STATE; target return TOKTARGET; temperature return TOKTEMPERATURE; \n [ \t]+ %%
|
yacc规则中也需要做出一些更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| target_set: TOKTARGET TOKTEMPERATURE NUMBER { printf("\tTemperature set to %d\n", $3); };
heat_switch: TOKHEAT STATE { if ($2) printf("\tHeat turned on\n"); else printf("\tHeat turned off\n"); }
|
参考资料
[1] Lex与YACC详解 - Deepliu的文章 - 知乎