Flex,连续扫描流(从套接字).我错过了什么使用yywrap()



正在使用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)

指出

  1. 这并不完全正确;缓冲区状态包括一个标志,该标志指示缓冲区是否可以重新填充。如果您使用yy_scan_bytes,创建的缓冲状态被标记为不可重新填充。

  2. 实际上比这更复杂,因为flex扫描器有时需要提前查看以确定匹配了哪个令牌,并且流结束指示可能在提前查看期间发生。在扫描器备份到已识别令牌的末尾之后,它仍然必须重新扫描前瞻字符,这可能包含更多的令牌。为了处理这个问题,它在缓冲区状态中设置一个标志,表示已经到达流结束,这可以防止每次扫描程序到达缓冲区结束时都调用YY_INPUT。尽管如此,确保您的YY_INPUT实现将继续返回流结束,以防在流结束返回后再次调用它,这可能是一个好主意。

  3. 对于另一个具体的例子,假设您想要实现某种#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。(它对二进制流也很有效)

相关内容

最新更新