首页 > 解决方案 > Flex 可重入从用户特定状态开始

问题描述

Flex在被调用时默认设置YY_STATE为。我正在尝试制作一个可以从用户特定状态而不是初始状态开始的可重入扫描程序。INITIALyyscan_t

这是案例

/* comment start       //not passed into flex
   in comment          //first line passed into flex
   end of comment*/    //second line passed into flex

由于某些原因,这两条线被分别馈送到可重入扫描器中,并且这条线所属的 YY_STATE 是已知的。我需要的是在开始 lexing 之前将评论状态传递给reentrant flex 并切换YY_STATE到。我的解决方法是在行首添加一个虚拟标记并将状态作为flex 传递。识别出虚拟令牌后,切换到特定状态。因此 flex 开始使用特定的 YY_STATE 对行进行词法分析。但是,在每行的开头添加一个虚拟标记非常耗时。COMMENT in comment\nyyextra

这是我以前称为可重入 flex 的方式:

yyscan_t scanner;                                                                                                                                                                                                                
YY_BUFFER_STATE buffer;                                                                                                                                                                                                          
yylex_init(&scanner);                                                                                                                                                                                                            
buffer = yy_scan_string(inputStr, scanner);                                                                                                                                                                                      
yyset_extra(someStructure, scanner);                                                                                                                                                                                                       
yylex(scanner);                                                                                                                                                                                                                  
yy_delete_buffer(buffer, scanner);                                                                                                                                                                                               
yylex_destroy(scanner); 

是否可以在调用之前设置 YY_STATE yylex(scanner)

标签: flex-lexer

解决方案


如果您只yylex为每个输入行调用一次,那么您可以添加一个额外的参数来yylex提供要切换到的开始条件,并将开始条件设置在 yylex 的顶部。

但是没有简单的方法可以从 flex 文件外部引用开始条件,也没有一种方便的方法可以从yystate_t对象中提取当前的开始条件。您声称拥有此信息的事实表明您在更改启动状态时将其存储在某处,因此您可以在启动时从同一位置恢复启动状态yylex。存储信息的最简单的地方是yyextra对象,所以这是这个示例代码的基础:

文件开始.int.h

/* This is the internal header file, which defines the extra data structure
 * and, in this case, the tokens.
 */
#ifndef BEGIN_INT_H
#define BEGIN_INT_H

struct Extra {
  int start;
};

enum Tokens { WORD = 256 };

#endif

文件开始.h

/* This is the external header, which includes the header produced by
 * flex. That header cannot itself be included in the flex-generated code,
 * and it depends on the internal header. So the order of includes here is
 * (sadly) important.
 */
#ifndef BEGIN_H_
#define BEGIN_H_

#include "begin.int.h"
#include "begin.lex.h"

#endif

文件:begin.l

/* Very simple lexer, whose only purpose is to drop comments. */
%option noinput nounput noyywrap nodefault 8bit
%option reentrant
%option extra-type="struct Extra*"
%{
#include "begin.int.h"
/* This macro ensures that start condition changes are saved */
#define MY_BEGIN(s) BEGIN(yyextra->start = s)
%}

%x IN_COMMENT
%%
  /* See note below */
  BEGIN (yyextra->start);
"/*"          MY_BEGIN(IN_COMMENT);
[[:alnum:]]+  return WORD;
[[:space:]]+  ;
.             return yytext[0];

<IN_COMMENT>{
  "*/"        MY_BEGIN(INITIAL);
  .|[^*]+     ;
}

笔记:

在第一个模式之后和第一个模式之前的任何缩进代码%%都插入到 ; 的开头yylex。唯一在它之前执行的是对象的一次性初始化(yystate_t如有必要)。

文件开始.main.c

/* Simple driver which creates and destroys a scanner object for every line
 * of input. Note, however, that it reuses the extra data object, which holds
 * persistent information (in this case, the current start condition).
 */
#include <stdio.h>
#include "begin.h"

int main ( int argc, char * argv[] ) {
  char* buffer = NULL;
  size_t buflen = 0;

  struct Extra my_extra = {0};

  for (;;) {
    ssize_t nr = getline(&buffer, &buflen, stdin);
    if (nr < 0) break;
    if (nr == 0) continue; 

    yyscan_t scanner;
    yylex_init_extra(&my_extra, &scanner);

    /* Ensure there are two NUL bytes for yy_scan_buffer */
    if (buflen < nr + 2) {
      buffer = realloc(buffer, nr + 2);
      buflen = nr + 2;
    }
    buffer[nr + 1] = 0;
    YY_BUFFER_STATE b = yy_scan_buffer(buffer, nr + 2, scanner);

    for (;;) {
      int token = yylex(scanner);
      if (token == 0) break;
      printf("%d: '%s'\n", token, yyget_text(scanner));
    }
    yy_delete_buffer(b, scanner);
    yylex_destroy(scanner);
  }
  return 0;
}

建造:

flex -o begin.lex.c --header-file begin.lex.h begin.l
gcc -Wall -ggdb -o begin begin.lex.c begin.main.c

推荐阅读