本篇文章给大家分享的是有关如何理解lex和yacc,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
一、背景
从零开始学习下lex和yacc
1. 基础
lex只有状态和状态转换,没有栈,善于模式匹配;yacc能处理带栈的FSA(有限状态机),更适合更复杂的任务。
模式匹配原语
元字符
| 匹配说明
|
.
| 任意字符( 除了换行)
|
\n
| 换行
|
*
| 0次或者多次重复前面的表达式
|
+
| 1次或者多次重复前面的表达式
|
?
| 0次或者1次重复前面的表达式
|
^
| 行的开始
|
$
| 行的结尾
|
a|b
| a or b
|
(ab)+
| 1次或者多次重复组ab
|
[...]
| 任意一个出现的字符
|
一些匹配的例子
表达式
| 匹配说明
|
abc
| abc
|
abc*
| ab, abc, abcc, abccc,.....
|
abc+
| abc, abcc, baccc,......
|
a(bc)+
| abc, abcbc, abcbcbc,......
|
a(bc)?
| a, abc
|
[abc]
| a, b, c
|
[a-z]
| a到z的任意字符
|
[a\-z]
| a, -, z
|
[-az]
| -, a, z
|
[a-zA-Z0-9]+
| 一个或者多个任何数字字母
|
[ \t\n]
| witespace
|
[^ab]
| 除了a,b的任何字符
|
[a^b]
| a, ^, b
|
[a|b]
| a, |, b
|
a|b
| a or b
|
匹配规则:
1. 贪心: 两个模式去匹同一个字符串,匹配上最长的模式
2. 顺序优先: 两个相同长度的模式, 匹配上先定义的模式
.l文件内容的格式被%%分成了三部分,如下:
....definitions.....
%%
.....rules....
%%
...subroutines...
其中rules是必须的,其他部分可选
一些内置函数以及变量
int yylex(void)
| 调用分析器,返回 token
|
char *yytext
| 指定匹配的字符串
|
yyleng
| 匹配上的字符串的长度
|
int yywrap(void)
| 返回1 则结束了
|
FILE *yyout
| 输出文件,默认 stdout
|
FILE *yyin
| 输入文件, 默认stdin
|
INITIAL
| initial start condition |
BEGIN condition
| switch start condition
|
ECHO
| write mached string
|
#define ECHO fwrite(yytext, yyleng, 1, yyout)
二、开始第一个例子
环境说明:
VMware Workstation 12 Pro, ubuntu17.04 ,lex2.6.1
输出文件的内容并且在前面增加行号
lineno.l
%{
int yylineno;
%}
%%
^(.*)\n printf("%4d\t%s", ++yylineno, yytext);
%%
int main(int argc, char *argv[])
{
FILE *fp = NULL;
if (argc == 2) {
fp = fopen(argv[1], "r");
if (NULL != fp) {
yyin = fp;
}
}
yylex();
if (NULL != fp) {
fclose(fp);
}
return 0;
} 使用lex将lineno.l文件转换为.c文件
$ lex lineno.l
$ls
lex.yy.c lineno.l
使用gcc 将lex.yy.c编译成可执行文件
$ gcc lex.yy.c -o lineno
/tmp/ccNgesbZ.o:在函数‘yylex’中:
lex.yy.c:(.text+0x55c):对‘yywrap’未定义的引用
/tmp/ccNgesbZ.o:在函数‘input’中:
lex.yy.c:(.text+0x116c):对‘yywrap’未定义的引用
collect2: error: ld returned 1 exit status
网上查询说是要在.l文件中实现yywrap函数
修改后:
%{
int yylineno;
%}
%%
^(.*)\n printf("%4d\t%s", ++yylineno, yytext);
%%
int main(int argc, char *argv[])
{
FILE *fp = NULL;
yylineno = 0;
if (argc == 2) {
fp = fopen(argv[1], "r");
if (NULL != fp) {
yyin = fp;
}
}
yylex();
if (NULL != fp) {
fclose(fp);
}
return 0;
}
int yywrap()
{
return 1;
}再次编译成功
$gcc lex.yy.c -o lineno
$lineno lineno.l
1 %{
2 int yylineno;
3 %}
4
5 %%
6 ^(.*)\n printf("%4d\t%s", ++yylineno, yytext);
7 %%
8
9 int main(int argc, char *argv[])
10 {
11 FILE *fp = NULL;
12 yylineno = 0;
13
14 if (argc == 2) {
15 fp = fopen(argv[1], "r");
16 if (NULL != fp) {
17 yyin = fp;
18 }
19 }
20
21 yylex();
22
23 if (NULL != fp) {
24 fclose(fp);
25 }
26
27 return 0;
28 }
29
30 int yywrap()
31 {
32 return 1;
33 }
以上就是如何理解lex和yacc,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注天达云行业资讯频道。