当前位置: 首页 > 工具软件 > ZZFLEX > 使用案例 >

Flex

华泽语
2023-12-01

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时的r1abc/123
\c运算类字符的字面值\*,\$,\|
<S>rr, but in start condition S
<<EOF>>the end-of-file

lex函数

yylex

int yylex();

yylex() 对输入流(默认stdin)进行分析,当匹配到一个正则表达式,有两种情况

  1. 诸如 [0-9]+ { num += yytext; return 1; } 动作有返回, yylex()立即返回,在下次调用时从此处继续读取
  2. 诸如[ \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每个模式匹配后输出调试信息
-vverbose mode, 输出生成的扫描程序DFA等状态信息
-Ttrace 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;
}

踩过的坑

  1. error: expected identifier or ‘(’ before string constant
  • 原因 #include<string.h>, 具体我也不知到为什么
  • 解决方案:a.改成cstring, 所以要用g++编译,不能用gcc
    b. 在定义段声明 int strlen(char*);
  1. 注意优先级
    如bar* === ba(r*)

  2. %{一定要顶行书写

  3. 要匹配空格,需要出现在“ ”或 [ ]中

  4. 规则段也要顶行书写。

  5. {}要平衡,如果少了一个,flex将把后面的代码全部当作action

 类似资料: