定义
%{
Declarations
%}
Definitions
%%
Rules
%%
User subroutines
输入文件的第 1 段 %{ 和 %} 之间的为 声明(Declarations) ,都是 C 代码,这些代码会被原样的复制到 lex.yy.c 文件中,一般在这里声明一些全局变量和函数,这样在后面可以使用这些变量和函数。
第 2 段 %} 和 %% 之间的为 定义(Definitions),在这里可以定义正则表达式中的一些名字,可以在 规则(Rules) 段被使用,如本文件中定义了 WORD 为 ([^ \t\n\r\a]+) , 这样在后面可以用 {WORD} 代替这个正则表达式。
第 3 段为 规则(Rules) 段;会按从上往下的顺序尝试匹配。By default, any text not matched by a flex scanner is copied to the output
第 4 段为 用户定义过程(User subroutines) 段,也都是 C 代码,本段内容会被原样复制到 yylex.c 文件的最末尾,一般在此定义第 1 段中声明的函数。
函数和变量
yywrap
yywrap 函数的作用是将多个输入文件打包成一个输入,当 yylex 函数读入到一个文件结束(EOF)时,它会向 yywrap 函数询问, yywrap 函数返回 1 的意思是告诉 yylex 函数后面没有其他输入文件了,此时 yylex 函数结束,yywrap 函数也可以打开下一个输入文件,再向 yylex 函数返回 0 ,告诉它后面还有别的输入文件,此时 yylex 函数会继续解析下一个输入文件。总之,由于我们不考虑连续解析多个文件,因此此处返回 1 。
关于刚刚匹配到的字符串
flex 提供的两个全局变量 yytext 和 yyleng,分别用来表示刚刚匹配到的字符串以及它的长度
简单举例1
https://www.oreilly.com/library/view/flex-bison/9780596805418/ch01.html
文件test.l
/* just like Unix wc */
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
%%
[a-zA-Z]+ { words++; chars += strlen(yytext); }
\n { chars++; lines++; } /* 注意,.不会匹配\n字符 */
. { chars++; }
%%
int main(int argc, char **argv)
{
yylex();
printf("%8d%8d%8d\n", lines, words, chars);
}
运行
flex + gcc
简单举例1中的代码运行:
$ flex test.l # tell flex to translate our program
$ gcc -lfl lex.yy.c # compile lex.yy.c; link it with the flex library, -lfl
$ ./a.out # run it; and type a little input for it to count.
The boy stood on the burning deck
shelling peanuts by the peck
^D
2 12 63
$
如果link flex lib步骤中ld报错找不到库的话,可以这样定位下问题:
输出ld实际上想找哪些库:
ld -lfl --verbose
可以看到没有你要的
如果你需要的libfl.so
文件在目录/path/to/lib
下:
gcc -L/path/to/lib -lfl lex.yy.c
/home/yuanzhiqiu/built/flex-2.6.4/lib
简单举例2
/* scanner for a toy Pascal-like language */
%{
/* Declarations */
/* need this for the call to atof() below */
#include <math.h>
%}
/* Definitions */
DIGIT [0-9]
ID [a-z][a-z0-9]*
%%
/* Rules */
/* pattern {action} */
{DIGIT}+ {
printf( "An integer: %s (%d)\n", yytext, atoi( yytext ) );
}
{DIGIT}+"."{DIGIT}* {
printf( "A float: %s (%g)\n", yytext, atof( yytext ) );
}
if|then|begin|end|procedure|function {
printf( "A keyword: %s\n", yytext );
}
{ID} {
printf( "An identifier: %s\n", yytext );
}
"+"|"-"|"*"|"/" {
printf( "An operator: %s\n", yytext );
}
"{"[^}\n]*"}" {} /* no action: eat up one-line comments */
[ \t\n]+ {} /* no action: eat up whitespace */
. {
printf( "Unrecognized character: %s\n", yytext );
}
%%
/* User subroutines */
int main(int argc, char **argv)
{
if ( argc > 1 )
yyin = fopen( argv[1], "r" );
else
yyin = stdin;
yylex(); /* entry */
}
编译安装
https://github.com/westes/flex/blob/master/INSTALL.md
https://github.com/westes/flex/releases
正则表达式
https://www.jianshu.com/p/cb721b883b57
一些语法
x 匹配字符 x
. 除换行符外的任何字符
[xyz] 字符类别;匹配x
、y
、z
[abj-oZ] 具有范围的字符类;匹配 a
, b
, j
到o
中的任何字母或 Z
[^A-Z] 否定字符类,即该类中的字符以外的任何字符 [^A-Z\n] 除大写字母或换行符外的任何字符
r* 零个或多个r
,其中r
是任何正则表达式(如何确定r:*前的子表达式)
r+ 一个或多个 r
r? 零或一个r
r{2,5} 从两到五个
r{2,} 两个或多个
r{4} 恰好4个
上面这些默认是最长匹配,加一个?则是最短匹配
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
{name} 扩展name
定义,把别处定义的name
拿过来用
\X 普通字符(不是特殊字符,比如不是*
,"
等)不转义,就是表示\x
。否则,为字符X
举例:”[xyz]"foo” 表示[xyz]"foo
,外面的””表示是一个字符串,里面的"表示是一个转义字符,匹配"
\0 一个NUL字符(ASCII代码0)
\123 八进制值123的字符
\x2a 十六进制值为2a的字符
(r) 匹配r
;定义子表达式;括号用于改变优先级;且可以和\1
等配合进行捕获
rs 正则表达式r
后跟正则表达式s
,称为串联
r|s r
或s
r/s 一个r
,但前提是其后跟一个s
。确定此规则是否为最长匹配项时,
将包含用s
匹配的文本,但在执行操作之前,该文本将返回到输入。因此,
该操作只会看到与r
匹配的文本。这种模式称为尾随上下文。 (flex不能正确
匹配某些r/s组合。有关危险的尾随上下文,请参见限制。)
^r 一个r
,但仅在一行的开头(即刚开始扫描时,或在扫描换行符之后)。
r$ 一个r
,但只能在一行的末尾(即,在换行符之前)。等同于r/\n。
请注意,flex的”换行符”概念与C编译器用来将flex解释为\n的情况完全相同。
特别是,在某些DOS系统上,您必须自己过滤掉输入中的\r,
或显式地将” r /\r\ n”用作” r $”。
r 一个r
,但仅在起始条件s中(请参阅”起始条件”以了解起始条件)。
<s1,s2,s3> r 同上,但在任何启动条件s1,s2或s3中。
<*> r 任何开始条件下的r
,甚至是排他条件。
<< EOF >> 文件结束。
<s1,s2><
匹配任意字符串
注意这样不行:[.\n]* 这样会匹配.
或\n
而不是任意字符
要这样写:(.|\n)*