flex学习 - 可重入的C扫描器

news/2025/3/19 23:30:11/文章来源:https://www.cnblogs.com/xiaobing3314/p/18782146

flex能够生成可重入的C扫描程序。生成的扫描器是可移植的,并且可以安全的在一个或多个独立的控制线程中使用。可重入扫描器最常见的用途是在多线程应用程序中。任何线程都可以创建和执行可重入的flex扫描器,而不需要与其它线程同步。
17.1 可重入的使用
但是可重入扫描器还有其它用途。例如,你可以同时扫描两个或更多的文件来实现在标记级别的差异(即,而不是在字符级别):
/* Example of maintaining more than one active scanner. */

do {int tok1, tok2;tok1 = yylex( scanner_1 );tok2 = yylex( scanner_2 );if( tok1 != tok2 )printf("Files are different.");} while ( tok1 && tok2 );

可重入扫描程序的另一个用途是递归。(注意,也可以使用不可重入的扫描程序和缓冲状态创建递归扫描程序。参见多输入缓冲区。)
下面的简单扫描器通过调用自身的另一个实例来支持’eval’命令。
/* Example of recursive invocation. */

%option reentrant%%
"eval(".+")"  {yyscan_t scanner;YY_BUFFER_STATE buf;yylex_init( &scanner );yytext[yyleng-1] = ' ';buf = yy_scan_string( yytext + 5, scanner );yylex( scanner );yy_delete_buffer(buf,scanner);yylex_destroy( scanner );}
...
%%

17.2 可重入API概述
可重入扫描器的API和不可重入的扫描器不同。这里是一个API的快速概要:
1、%option reentrant必须被指定
2、所有的函数必须添加一个参数:yyscanner
3、所有的全局变量通过它们的等效宏替换。(我们告诉你这是因为在调试器件它是重要的)
4、yylex必须分别在yylex_init之后和yylex_destroy之前调用
5、访问方法(get/set函数)提供访问公共flex变量的方法
6、用户指定的数据可以被存储在yyextra中
17.3 可重入示例
首先,一个可重入扫描器的例子如下:
/* This scanner prints "//" comments. */

%option reentrant stack noyywrap
%x COMMENT%%"//"                 yy_push_state( COMMENT, yyscanner);
.|\n<COMMENT>\n          yy_pop_state( yyscanner );
<COMMENT>[^\n]+      fprintf( yyout, "%s\n", yytext);%%int main ( int argc, char * argv[] )
{yyscan_t scanner;yylex_init ( &scanner );yylex ( scanner );yylex_destroy ( scanner );
return 0;
}

17.4 可重入API的详细描述
以下是使用flex可重入C API需要做或知道的事情:
1、可重入扫描器的声明
%option reentrant(-reentrant)必须被指定
注意%option reentrant是在上面的示例中指定的(参见可重入示例)。如果没有指定这个选项,flex会很高兴的生成一个不可重入的扫描器,而不会由任何的抱怨。如果不想使用可重入扫描程序,可以显示指定%option noreentrant,尽管这不是必须的。默认产生一个非可重入的扫描器。
2、额外的参数
所有的函数添加一个参数:yyscanner
注意,对yy_push_state和yy_pop_state的调用都有一个参数yyscanner,该参数在不可重入的扫描器中不存在。这里的yy_push_state和yy_pop_state在可重入扫描器中被声明:
static void yy_push_state ( int new_state , yyscan_t yyscanner ) ;
static void yy_pop_state ( yyscan_t yyscanner ) ;
注意,参数yyscanner出现在两个函数的声明中。实时上,可重入扫描器中的所有flex函数都有这个附加参数。它始终是参数列表中的最后一个参数,它始终是类型yyscan_t(类型定义为void ),并且始终命名为yyscanner。正如您可能已经猜想的那样,yyscanner是一个指针,指向封装了扫描器当前状态的不透明数据结构。关于函数声明的列表,查看可重入函数章节。注意,预处理的宏,像BEGIN,ECHO,和REJECT,不需要这个参数。
3、通过宏替换全局变量
在传统的flex中所有的全局变量通过等效的宏替换。
注意,在上面的例子中,yyout和yytext不是纯变量。这些宏将被展开为它们等效的lvalue。所有flex系列的全局变量已经通过它们等效的宏替换。典型的,yytext,yyleng,yylineno,yyin,yyout,yyextra,yylval,和yylloc是宏。你可以安全的在操作中使用这些宏,就好像它们是普通的变量一样。我们告诉你这些只是为了让你不希望外部链接到这些变量。目前,每个宏都扩展为内部结构体的成员,例如:
#define yytext (((struct yyguts_t
)yyscanner)->yytext_r)
关于yytext和friends,需要记住的重要一点是,yytext不是可重入扫描器中的全局变量,您不能从操作外部或其它函数直接访问它。你必须使用访问器方法,例如,yyget_text来完成此操作(见下文)。
4、初始化和销毁函数
yylex必须分别在yylex_init之后和yylex_destory之前调用。
int yylex_init ( yyscan_t * ptr_yy_globals ) ;
int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t * ptr_yy_globals ) ;
int yylex ( yyscan_t yyscanner ) ;
int yylex_destroy ( yyscan_t yyscanner ) ;
函数yylex_init必须在任何其它函数调用前调用。yylex_init的参数是一个通过yylex_init填充的未初始化指针地址,复写任何之前的内容。这个函数yylex_init_extra可以被替代使用,它的第一个参数是YY_EXTRA_TYPE类型的变量。查看下面的yyextra章节更详细的信息。
在ptr_yy_globals中存储的值应该随后被传递给yylex和yylex_destroy。flex不会保存传递给yylex_init的参数,因此只要在所有对扫描器调用期间(包括对yylex_destroy的调用)仍然在作用域中,将本地指针的地址传递给yylex_init是安全的。
现在你应当对yylex函数很熟悉了。可重入版本接受一个参数,即yylex_init返回的值(通过参数)。其它的,它的行为与非可重入的yylex版本相似。
yylex_init和yylex_init_extra都以返回0表示成功,非0值表示失败,一些错误码通过下面的值设置:
(1)ENOMEM内存分配错误。查看内存管理
(2)EINVAL无效的参数
yylex_destroy这个函数应当在扫描器使用的资源释放时调用。yylex_destroy被调用后yyscanner的内容不应当被继续使用。可以确认,如果你计划恢复使用它没有需要销毁一个扫描器。flex扫描器(包括可重入和非可重入)可以通过调用yyrestart重新开始。
下面的示例创建一个扫描器,使用它,然后当完成后销毁它:

int main (){yyscan_t scanner;int tok;yylex_init(&scanner);while ((tok=yylex(scanner)) > 0)printf("tok=%d  yytext=%s\n", tok, yyget_text(scanner));yylex_destroy(scanner);return 0;
}

5、可重入扫描器访问变量
访问方法(get/set函数)提供了访问公共flex变量的方法。
你构建的许多扫描器将是更大项目的一部分。项目的某些部分将需要访问flex的值,例如yytext。在不可重入的扫描器中,这些值是全局的,因此访问它们没有问题。然而,在可重入的扫描器中,没有全局的flex值。你不能直接的访问它们。替代的,你必须使用访问器方法(get/set函数)访问flex值。每个访问器方法被命名为yyget_NAME或yyset_NAME,这里的NAME是flex变量的名字。例如:
/* Set the last character of yytext to NULL. */

void chop ( yyscan_t scanner ){int len = yyget_leng( scanner );yyget_text( scanner )[len - 1] = '\0';
}

上面的代码可以像这样从一个动作中调用:
%%
.+\n { chop( yyscanner );}
你可能会发现%option header-file对于生成所有访问器函数的原型特别有用。查看option-header章节。
6、额外的数据
用户指定的数据可以被存储在yyextra中。
在可重入的扫描器中,使用全局变量的程序的不同部分之间进行通信或维护状态是不明智的。但是,你可能需要访问外部数据或从扫描程序操作中调用外部函数。同样,你可能需要将信息传递给扫描器(例如,打开的文件描述符或数据库连接)。在不可重入的扫描程序中,唯一的方法是使用全局变量。flex允许你在扫描器中存储任何的”extra”数据。这些数据可以通过访问器方法yyget_extra和yyset_extra从扫描程序外部访问,也可以通过扫描程序内部的快捷宏yyextra访问。它们的定义如下:
#define YY_EXTRA_TYPE void*
YY_EXTRA_TYPE yyget_extra ( yyscan_t scanner );
void yyset_extra ( YY_EXTRA_TYPE arbitrary_data , yyscan_t scanner);
此外,还提供了yylex_init的一个额外形式,yylex_init_extra。提供这个函数是为了可以从第一个yyalloc中访问yyextra的值,用于分配扫描器本身。
默认情况下,YY_EXTRA_TYPE被定义为void 类型。你可以使用%option extra-type=”your_type”在扫描器中重新定义这个类型:
/
An example of overriding YY_EXTRA_TYPE. */

%{
#include <sys/stat.h>
#include <unistd.h>
%}
%option reentrant
%option extra-type="struct stat *"
%%__filesize__     printf( "%ld", yyextra->st_size  );
__lastmod__      printf( "%ld", yyextra->st_mtime );
%%
void scan_file( char* filename )
{yyscan_t scanner;struct stat buf;FILE *in;in = fopen( filename, "r" );stat( filename, &buf );yylex_init_extra( buf, &scanner );yyset_in( in, scanner );yylex( scanner );yylex_destroy( scanner );fclose( in );
}

7、关于yyscan_t
yyscan_t被定义为:
typedef void* yyscan_t;
它通过yylex_init()初始化指向一个内部结构体。你应当不会直接访问这个值。特别是,永远不要试图释放它(使用yylex_destory()替代。)
17.5 在可重入C扫描器中的函数和宏变量
下面的函数在可重入扫描器中是有效的:

char *yyget_text ( yyscan_t scanner );
int yyget_leng ( yyscan_t scanner );
FILE *yyget_in ( yyscan_t scanner );
FILE *yyget_out ( yyscan_t scanner );
int yyget_lineno ( yyscan_t scanner );
YY_EXTRA_TYPE yyget_extra ( yyscan_t scanner );
int  yyget_debug ( yyscan_t scanner );void yyset_debug ( int flag, yyscan_t scanner );
void yyset_in  ( FILE * in_str , yyscan_t scanner );
void yyset_out  ( FILE * out_str , yyscan_t scanner );
void yyset_lineno ( int line_number , yyscan_t scanner );
void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t scanner );

对于yytext和yyleng没有’set’函数。这是有意为之的。
下面的缩写宏在可重入扫描器中的动作是有效的:
yytext
yyleng
yyin
yyout
yylineno
yyextra
yy_flex_debug
在可重入的C扫描器中,对yylineno的支持总是存在的(即,你可以访问yylineno),但是除非%option yylineno被启用,否则该值永远不会被flex修改。这是为了允许用于独立于flex维护行数。
当%option bison-bridge(‘--bison-bridge’)被指定时下面的函数和宏是有效的:
YYSTYPE * yyget_lval ( yyscan_t scanner );
void yyset_lval ( YYSTYPE * yylvalp , yyscan_t scanner );
yylval
当%option bison-locations(‘--bison-locations’)被指定时下面的函数和宏是有效的:
YYLTYPE *yyget_lloc ( yyscan_t scanner );
void yyset_lloc ( YYLTYPE * yyllocp , yyscan_t scanner );
yylloc
支持yylval的前提是YYSTYPE是一个有效的类型。支持yylloc的前提是YYSLYPE是一个有效的类型。典型的,这些类型通过bison产生,且被包括在flex输入的第一段中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/901782.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pcie 简介及引脚定义

随着现代处理器技术的发展,在互连领域中,使用高速差分总线替代并行总线是大势所趋。与单端并行信号相比,高速差分信号可以使用更高的时钟频率,从而使用更少的信号线,完成之前需要许多单端并行数据信号才能达到的总线带宽。 PCI总线使用并行总线结构,在同一条总线上的所有…

C++ 基础(1)

0x01 第一个C++程序 #include <iostream>int main() {std::cout << "Hello World!\n"; } // std::cout 向控制台输出内容的指令 // << 输出的运算符 // "" 字符串内容的边界符 // \n 输出换行 // Hello World 输出字符…

在ubuntu系统下与开发板连接问题记录

对我所遇到的问题以及解决方法进行简单的记录在开发板与ubuntu(非虚拟机)连接之后使用lsmod查看是否连接lsusb 我的显示如下:如果可以看到自己的USB设备 那么就说明你已经安装了驱动 如果没有 请安装你的串口对应的驱动 我的驱动是CH340 没有安装的朋友可以去下面网站进行…

maven为什么发生依赖冲突?怎么解决依赖冲突?

maven为什么发生依赖冲突?怎么解决依赖冲突? 我们在开发的时候,偶尔会遇到依赖冲突的时候,一般都是NoClassDefFoundError、ClassNotFoundException、NoSuchMethodError。打开搜索框又发现有这个类,明明就是引入进来了,就是找不到,让人头疼 1. 依赖冲突场景 在maven中依赖…

unstructured

unstructured 是一个开源的 Python 库,专门用于处理非结构化数据,如从 PDF、Word 文档、HTML 文件等中提取文本内容,并将其转换为结构化格式(1)安装依赖库pip install unstructured使用textfrom unstructured.partition.auto import partitionfilename = "a.txt"…

idea如何激活到2099年

前言 最近发现idea如何激活使用的问题、 网络上各种都是骗关注加各种公众号的最后也没有解决问题,下面分享一下我的激活方法是如何激活到2099年。目前适用于idea的所有版本。我以最新的ideaIU-2024.3为例。 一去官网上下载idea 官网下载地址:https://www.jetbrains.com.cn/id…

202108032324 - kafka的生产流程

由上图可以看出:KafkaProducer有两个基本线程: 主线程: 负责消息创建,拦截器,序列化器,分区器等操作,并将消息追加到消息收集器 RecoderAccumulator中; 消息收集器RecoderAccumulator为每个分区都维护了一个Deque<ProducerBatch> 类型的双端队列。 ProducerBatch…

Cython二进制逆向系列(三)运算符

在这篇文章里,我们会讨论Cython是如何处理运算符的(数学运算符、位运算符、in/not in 运算符、 ==运算符与逻辑运算符)。总的来叔其中大部分是调用虚拟机api来实现的。Cython二进制逆向系列(三)运算符在开始前,先给出本文用到的py源代码 def test1(x, y):# 数学运算符a…

QOJ 9785 Shrooks

曼哈顿距离转切比雪夫距离,对限制的贪心满足,扩宽限制,简化信息首先考虑限制的形式:对于两点 \((x_1, y_1), (x_2, y_2)\),要求 \(|x_1 - x_2| + |y_1 - y_2| \le n\)。 但是这个式子与 \(x_1, x_2, y_1, y_2\) 这 \(4\) 个值都强相关,且这里的绝对值也并不好拆开处理。 …

golang的GC机制

一、垃圾回收 什么是垃圾回收?垃圾回收(GC,garbage collection)是自动内存管理的一种形式,通常由垃圾收集器收集并适时回收或重用不再被对象占用的内存,比如众所周知的Java语言就能很好的支持GC。后起之秀——Go语言也同样支持垃圾回收,它使得Go程序员在编写程序的时候不…

fastjson漏洞复现(fastjson 1.2.47)

原理: Fastjson 的 @type 字段允许反序列化时动态指定类,攻击者可以通过指定带有危险行为的类,并结合 RMI/LDAP 远程加载恶意类,从而实现命令执行。开启vulhub靶场//如何验证是否存在fastjson反序列化漏洞 有json数据传输的地方可能存在 可以尝试使用dnslog带外但是不知道…

基于神经网络逆同步控制方法的两变频调速电机控制系统matlab仿真

1.课题概述两电机变频调速系统是一个多输入多输出非线性强耦合的控制系统。本课题使用神经网络构造原系统的a阶逆系统,设计线性闭环调节器进行控制,通过matlab对系统进行仿真。对两电机变频调速系统更为精确地实现了张力和速度间的解藕控制。2.系统仿真结果 (完整程序运行后…