一、背景
从零开始学习下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 }