Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

定义

%{
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 提供的两个全局变量 yytextyyleng,分别用来表示刚刚匹配到的字符串以及它的长度

简单举例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

http://web.mit.edu/gnu/doc/html/flex_1.html#:~:text=An%20Overview%20of%20flex%2C%20with%20Examples%201%20Text-Substitution,Language%20Scanner%20A%20somewhat%20more%20complicated%20example%3A%20

/* 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] 字符类别;匹配xyz
[abj-oZ] 具有范围的字符类;匹配 abjo中的任何字母或 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 rs
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><> 在开始条件s1或s2中的文件结束

匹配任意字符串

注意这样不行:[.\n]* 这样会匹配.\n而不是任意字符
要这样写:(.|\n)*

评论