正在使用Flex进行模式识别的基于套接字的扫描仪(连续流)。Flex没有找到重叠"数组边界"的匹配。因此,我实现了yywrap(),以便在yylex()检测到<>时设置新的数组内容(它将调用yywrap)。到目前为止没有成功。
基本上(为了明确我的问题)这是我的代码:
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFERSIZE 26
/* 0123456789012345678901234 */
char cbuf1[BUFFERSIZE] = "Hello everybody, lex is su"; // Warning, no ' '
char cbuf2[BUFFERSIZE] = "per cool. Thanks! ";
char recvBuffer[BUFFERSIZE];
int packetCnt = 0;
YY_BUFFER_STATE bufferState1, bufferState2;
%}
%option nounput
%option noinput
%%
"super" { ECHO; }
. { printf( "%c", yytext[0] );}
%%
int yywrap()
{
int retval = 1;
printf(">> yywrap()n");
if( packetCnt <= 0 ) // Stop after 2
{
// Copy cbuf2 into recvBuffer
memcpy(recvBuffer, cbuf2, BUFFERSIZE);
//
yyrestart(NULL); // ?? has no effect
// Feed new data to flex
bufferState2 = yy_scan_bytes(recvBuffer, BUFFERSIZE);
//
packetCnt++;
// Tell flex to resume scanning
retval = 0;
}
return(retval);
}
int main(void)
{
printf("Lenght: %dn", (int)sizeof(recvBuffer)) ;
// Copy cbuf1 into recvBuffer
memcpy(recvBuffer, cbuf1, BUFFERSIZE);
//
packetCnt = 0;
//
bufferState1 = yy_scan_bytes(recvBuffer, BUFFERSIZE);
//
yylex();
yy_delete_buffer(bufferState1);
yy_delete_buffer(bufferState2);
return 0;
}
这是我的输出:
dkmbpro:test dkroeske$ ./text
Lenght: 26
Hello everybody, lex is su>> yywrap()
per cool. Thanks! >> yywrap()
所以'super'上没有匹配。根据文档,lexer不会在yywrap之间"重置"。我错过了什么?谢谢。
为flex
提供输入流的机制是提供YY_INPUT宏的定义,每次flex
需要重新填充缓冲区时调用该宏[注1]。宏调用时带有三个参数,大致如下:
YY_INPUT(buffer, &bytes_read, max_bytes)
宏预计将最多max_bytes
读入buffer
,并将bytes_read
设置为实际读取的字节数。如果在这个流中没有更多的输入,YY_INPUT
应该将bytes_read
设置为YY_NULL
(即0)。除了设置文件结束条件外,没有其他方法可以标记输入错误。不设置YY_INPUT
为负值
注意,YY_INPUT
没有提供从哪里读取输入的指示,也没有提供任何userdata
参数。唯一提供的机制是全局yyin
,它是一个FILE*
。(您可以使用fdopen
从文件/套接字描述符创建FILE*
,并使用fileno
获取描述符。其他解决方法超出了这个答案的范围。
当扫描器遇到流结束时,如YY_INPUT
返回0所示,它完成当前令牌[注2],然后调用yywrap
来决定是否有另一个流要处理。正如手册所指出的,它不会重置解析器状态(即,它恰好处于哪个启动条件;如果启用了行计数,则为当前行号,等等)。但是,它不允许令牌跨越两个流。
yywrap
机制最常用于将解析器/扫描器应用于命令行中指定的许多不同文件时。在这个用例中,如果一个令牌可以从一个文件开始并继续到另一个文件中,那就有点奇怪了;大多数语言实现都希望它们的文件在某种程度上是自包含的。(例如,考虑多行字符串字面量。)通常情况下,实际上还需要重置更多的解析器状态(当然包括行号,有时还包括启动条件),但这是yywrap
的责任。[注3]
对于套接字的词法分析,您可能需要从YY_INPUT
实现中调用recv
。但出于实验目的,这里有一个简单的YY_INPUT
,它只从内存缓冲区返回数据:
/* Globals which describe the input buffer. */
const char* my_in_buffer = NULL;
const char* my_in_pointer = NULL;
const char* my_in_limit = NULL;
void my_set_buffer(const char* buffer, size_t buflen) {
my_in_buffer = my_in_pointer = buffer;
my_in_limit = my_in_buffer + buflen;
}
/* For debugging, limit the number of bytes YY_INPUT will
* return.
*/
#define MY_MAXREAD 26
/* This is technically incorrect because it returns 0
* on EOF, assuming that YY_NULL is 0.
*/
#define YY_INPUT(buf, ret, maxlen) do {
size_t avail = my_in_limit - my_in_pointer;
size_t toread = maxlen;
if (toread > avail) toread = avail;
if (toread > MY_MAXREAD) toread = MY_MAXREAD;
*ret = toread;
memcpy(buf, my_inpointer, toread);
my_in_pointer += toread;
} while (0)
指出
这并不完全正确;缓冲区状态包括一个标志,该标志指示缓冲区是否可以重新填充。如果您使用
yy_scan_bytes
,创建的缓冲状态被标记为不可重新填充。实际上比这更复杂,因为flex扫描器有时需要提前查看以确定匹配了哪个令牌,并且流结束指示可能在提前查看期间发生。在扫描器备份到已识别令牌的末尾之后,它仍然必须重新扫描前瞻字符,这可能包含更多的令牌。为了处理这个问题,它在缓冲区状态中设置一个标志,表示已经到达流结束,这可以防止每次扫描程序到达缓冲区结束时都调用
YY_INPUT
。尽管如此,确保您的YY_INPUT
实现将继续返回流结束,以防在流结束返回后再次调用它,这可能是一个好主意。对于另一个具体的例子,假设您想要实现某种
#include
机制。flex
提供了yy_push_state/yy_pop_state
机制,允许您实现包含堆栈。一旦扫描了include
指令,您将调用yy_push_state
,但yy_pop_state
需要从yywrap
调用。同样,很少有语言允许在包含的源文件中开始标记并继续遵循include
指令。
多亏了rice,答案是重新定义YY_INPUT宏。所以我做了:
#undef YY_INPUT
#define YY_INPUT(buf, result, max_size) inputToFlex(buf, &result, max_size)
....
void inputToFlex(char *buf, unsigned long int *result, size_t max_size)
{
if( recv(psock, recvBuffer, RECVBUFFERSIZE, MSG_WAITALL) )
{
memcpy(buf, recvBuffer, RECVBUFFERSIZE );
*result = RECVBUFFERSIZE;
}
else
{
*result = YY_NULL;
}
}
这工作完美,它调用yywrap()时,套接字被关闭(由客户端)。注意我使用的MSG_WAITALL而不是更常见的'0'。
还要注意rici的注释2。如果你的扫描器需要寻找头我的解决方案是不够的你需要实现"1字符重叠缓冲区管理"。
谢谢flex。(它对二进制流也很有效)