flex学习 - 多输入缓冲区
第九章多输入缓冲区
一些扫描器(比如那些支持”include”文件的扫描器)需要从多个输入流中读取数据。由于flex扫描器执行需要大量缓冲,因此无法通过简单的写入对扫描上下文敏感的YY_INPUT()来控制从何处读取下一个输入。YY_INPUT()仅在扫描器到达其缓冲区的末端时调用,这可能是在扫描语句(如需要切换输入源的include语句)后很长一段时间。
为了解决这类问题,flex提供了一种在多个输入缓冲区之间创建和切换的机制。一个输入缓冲区通过使用下面的方式创建:
函数:YY_BUFFER_STATE yy_create_buffer(FILE *file,int size)
它接受一个FILE指针和大小,并创建一个与给定文件相关联的缓冲区,该缓冲区的大小足以容纳size个大小的字符(如果有疑问,使用YY_BUF_SIZE作为大小)。它返回一个YY_BUFFER_STATE的句柄,然后可以传递给其它程序(见下文)。YY_BUFFER_STATE类型是一个指向不透明结构的yy缓冲状态的结构指针,所以你可以安全的将YY_BUFFER_STATE变量初始化为((YY_BUFFER_STATE)0),如果你愿意的话,也可以引用不透明结构,以便正确的在源文件中正确声明输入缓冲区而不是在你的扫描器中。请注意,调用yy_create_buffer创建缓冲区中的FILE指针仅用作YY_INPUT()看到的yyin值。如果你重新定义YY_INPUT()使它不能使用yyin,则你可以安全的传递一个NULL FILE指针给yy_create_buffer。选择要扫描的特定缓冲区:
函数:void yy_switch_to_buffer(YY_BUFFER_STATE new_buffer )
上面的函数切换扫描器的输入缓冲区,因此后续的标记将来new_buffer缓冲区。请注意,yywrap()可以使用yy_switch_buffer()来设置继续扫描的内容,而不是打开一个新文件并将yyin指向它。如果你正在寻找输入缓冲区的栈,那么您需要使用yypush_buffer_state()而不是这个函数。请注意,通过yy_switch_to_buffer()或yywrap()切换输入源不会改变启动条件。
函数:void yy_delete_buffer(YY_BUFFER_STATE buffer)
被用于回收相关存储的缓冲区。(buffer可以是NULL,这个将引起函数什么也不做。)你也可以使用下面的函数清除当前的缓冲区内容:
函数:void yypush_buffer_state()
这个函数压入新的缓冲区状态到内部栈中。被压入的状态变为新的当前状态。这个栈由flex维护,并将根据需要增长。当您想要更改状态,但是要保留当前状态以供以后使用时,可以使用此函数而不是yy_switch_to_buffer()。
函数:void yypop_buffer_state()
这个函数从栈顶移除当前的状态,并通过调用yy_delete_buffer()删除它。如果可能,在栈上的下一个状态,变为当前新的状态。
函数:void yy_flush_buffer(YY_BUFFER_STATE buffer)
这个函数废弃缓冲区的内容,以至于下一次扫描器尝试匹配缓冲区的标记,它将首先使用YY_INPUT()填充缓冲区。
函数:YY_BUFFER_STATE yy_new_buffer(FILE file,int size)
它是yy_create_buffer()的别名,提供了C++使用new和delete创建和销毁动态目标的兼容性。
YY_CURRENT_BUFFER宏返回一个YY_BUFFER_STATE句柄到当前缓冲区。它不应当被使用作为一个lvalue。
下面是使用这些特性来编写扩展include文件的扫描器的两个示例(下面将讨论<
这里的第一个示例使用yypush_buffer_state和yypop_buffer_state。flex内部维护堆栈。
/
* of an include file
*/
%x incl
%%
include BEGIN(incl);
[a-z]+ ECHO;
[^a-z\n]*\n? ECHO;
<incl>[ \t]* /* eat the whitespace */
<incl>[^ \t\n]+ { /* got the include file name */
yyin = fopen( yytext, "r" );
if ( ! yyin )
error( ... );
yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE ));
BEGIN(INITIAL);
}
<<EOF>> {
yypop_buffer_state();
if ( !YY_CURRENT_BUFFER )
{
yyterminate();
}
}
下面的第二个示例与前面的示例做了同样的事情,但是手动管理自己的输入缓冲区堆栈(而不是让flex来做)。
/* the "incl" state is used for picking up the name
* of an include file
*/
%x incl
%{
#define MAX_INCLUDE_DEPTH 10
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;
%}
%%
include BEGIN(incl);
[a-z]+ ECHO;
[^a-z\n]*\n? ECHO;
<incl>[ \t]* /* eat the whitespace */
<incl>[^ \t\n]+ { /* got the include file name */
if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
{
fprintf( stderr, "Includes nested too deeply" );
exit( 1 );
}
include_stack[include_stack_ptr++] =
YY_CURRENT_BUFFER;
yyin = fopen( yytext, "r" );
if ( ! yyin )
error( ... );
yy_switch_to_buffer(
yy_create_buffer( yyin, YY_BUF_SIZE ) );
BEGIN(INITIAL);
}
<<EOF>> {
if ( --include_stack_ptr 0 )
{
yyterminate();
}
else
{
yy_delete_buffer( YY_CURRENT_BUFFER );
yy_switch_to_buffer(
include_stack[include_stack_ptr] );
}
}
对于扫描内存中的字符串替代文件下面的函数可以用于设置输入缓冲区。对于扫描字符串它们都创建一个新的输入缓冲区,并返回对应的YY_BUFFER_STATE句柄(当完成后,您应该使用yy_delete_buffer()删除它)。它们也使用yy_switch_to_buffer()切换到新的缓冲区,因此下一次调用yylex()将开始扫描字符串。
函数:YY_BUFFER_STATE yy_scan_string(const char *str)
扫描一个以NUL结尾的字符串
函数:YY_BUFFER_STATE yy_scan_bytes(const char *bytes,int len)
扫描从len长度字节(包括可能的NULs)开始的bytes位置
注意这两个函数都创建和扫描字符串和字节的复制。(这个可能是需要的,当yylex()在扫描中更新缓冲区的内容时。)你可以使用下面的函数避免复制:
函数:YY_BUFFER_STATE yy_scan_buffer(char *base,yy_size_t size)
它扫描从base开始的缓冲区,包含size个字节,最后两个字节必须时YY_END_OF_BUFFER_CHAR(ASCII NUL)。这个最后两个字节不被扫描;因此,扫描包含base[0]到base[size-2]。
如果以这种方式设置base失败(即,忘记最后两个YY_END_OF_BUFFER_CHAR字节),则yy_scan_buffer()返回一个NULL指针以替代创建新的输入缓冲区。
数据类型:yy_size_t
是一个整数类型,您可以将反映缓冲区大小的整型表达式强制转换为该类型。