FLEX 结构
定义段 (definitions) %% 规则段 (rules) %% 用户代码段 (user code)
- rules 由正则表达式和动作(Action)组成
- 注释 /*…*/
- 规则段中,正则表达式必须顶行,否则直接复制到.c文件中
- 例
/*定义段*/
%{
#include<iostream>
#include<string>
extern "C" {
int yywrap();
}
std::string str;
std::string num;
%}
/*规则段*/
%%
[a-zA-Z]+ { str += yytext; return 1; }
[0-9]+ { num += yytext; return 1; }
\n { return 0; }
%%
/*用户代码段*/
int yywrap() { return 1; }
int main() {
while( yylex() );
std::cout<<"Number: " << num <<std::endl;
std::cout<<"String: " << str <<std::endl;
return 0;
}
编译操作
- 控制台操作
- flex fb1-1.l
- g++ lex.yy.c
- a (运行exe)
- 编译像yywrap这样flex的函数需要用C的方式编译,如果不用C++也就是用gcc编译,就不需要extern “C”
- yywrap必须定义,也可以使用%option noyywrap或g++ -lfl 来避免用户来定义它。
但我直接使用
g++ lex.yy.c -lfl
时出现找不到-lfl的错误,解决方案g++ lex.yy.c -lfl -Llib/
-L后面接静态链接库 libfl.a 所在的路径
还有一种解决方案:https://blog.csdn.net/linuxheik/article/details/79557023,但比较麻烦,他用到使VS来链接 libfl.a 而不是gcc或g++
- char *yytext 表示匹配到的内容,由flex定义
匹配原则
- 最长匹配原则
- 最先匹配原则
回溯
需要回溯的情况:
- 模式中一个是另一个的前缀或子串
- “trailing context”和^r会导致回溯
正则匹配
表达式 | 匹配 | 例子 |
---|---|---|
r1r2 | 连接 | |
r1|r2 | 或 | |
(r) | 不改变r表示,主要是用于确定运算优先关系 | |
r* | 零个或多个实例 | |
r+ | 一个或多个实例 | |
r? | 零个或多个实例 | |
[a-c] | 等价于a|b|c,或 [abc] | |
. | 除了换行符以外的任意字符 | |
^ | 一行的开始 | |
$ | 行的结尾 | |
[^abc] | 除了abc以外的任何字符 | |
r{m,n} | 重复出现次数m-n | |
r1/r2 | 后面有r2时的r1 | abc/123 |
\c | 运算类字符的字面值 | \*,\$,\| |
<S>r | r, but in start condition S | |
<<EOF>> | the end-of-file |
lex函数
yylex
int yylex();
yylex() 对输入流(默认stdin)进行分析,当匹配到一个正则表达式,有两种情况
- 诸如
[0-9]+ { num += yytext; return 1; }
动作有返回, yylex()立即返回,在下次调用时从此处继续读取 - 诸如
[ \t\] {}
动作无返回, yylex 继续往后分析
常用函数与变量说明
- 可以在词法分析器的开头设定
%oiption noyywrap
来要求它不使用yywrap
变量 | 说明 |
---|---|
yy_c_buf_p | 缓冲区指针,相当于读头 |
static yyconst int yy_ec[256] | 状态转移矩阵 |
extern FILE *yyin; | yylex()所扫描的文件 |
extern FILE *yyout; | yylex()所输出的文件 |
extern int yyleng; | yylex()当前所识别词形的长度 |
extern char *yytext; | yylex()当前所识别的词形 |
#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) | 打印当前识别的词形到yyout |
#define REJECT reject_used_but_not_detected | 识别下一个最佳词形 |
yymore() | 模式对应的action完成之后yytext不清空 |
yyless(n) | 当前模式匹配后,缓冲区指针回到当前词形的第n+1个字符 |
input() | 从缓冲区读入一个字符,并将指针后移一位 |
unput© | 回退字符c到输入流,即追加c到缓冲区当前扫描字符之前 |
BEGIN(s) | |
int yywrap(); | 当yylex遇到EOF时调用此函数,此函数返回1,yylex()扫描结束,否则继续扫描 |
常用的c语言函数
名称功能 | 声明 | |
---|---|---|
atoi | 字符串转换为10进制数 | int atoi(const char *str) |
strtol | 字符串转换成任意进制数字(2~36) | long int strtol(const char *str, char **endptr, int base) |
flex编译选项
选项 | 说明 |
---|---|
-b | 输出回溯到lex.backup文件中 |
-C | 对输出状态转移矩阵进行不同程度的压缩,强弱次序为:-Cem(缺省),-C,-C{f, F}e, -C{f, F}, -C{f, F}a |
-f | 状态转移矩阵不压缩 |
-d | 每个模式匹配后输出调试信息 |
-v | verbose mode, 输出生成的扫描程序DFA等状态信息 |
-T | trace mode, 跟踪扫描程序的生成的每个过程 |
IO文件操作
例:
extern FILE* yyin;
...
int main(int argc, char **argv)
{
if(argc>1){
if(!(yyin = fopen(argv[1], "r"))){
perror(argv[1]);
return 1;
}
}
...
return 0;
}
踩过的坑
- error: expected identifier or ‘(’ before string constant
- 原因 #include<string.h>, 具体我也不知到为什么
- 解决方案:a.改成cstring, 所以要用g++编译,不能用gcc
b. 在定义段声明 int strlen(char*);
-
注意优先级
如bar* === ba(r*) -
%{一定要顶行书写
-
要匹配空格,需要出现在“ ”或 [ ]中
-
规则段也要顶行书写。
-
{}要平衡,如果少了一个,flex将把后面的代码全部当作action