在 yacc/bison 之外使用 lex/flex

Using lex/flex outside of yacc/bison

我目前正在利用 bison 和 flex 解析线性时态逻辑公式并从中生成自动机。我以 "default" 的方式使用 flex,即将令牌写入 yylval(如有必要)并返回 bison 令牌标识符。

我正在使用单独的例程来扫描输入文件。输入由标识符名称和 integer/real 数字组成,flex 已经可以处理这两者。 示例:

x    y
3.20 78.3
3.31 76.2
3.32 77.4
//etc

我的问题是:如果可能的话,我将如何利用 flex 扫描输入文件?我知道如何切换 flex 的输入缓冲区,但之后如何获取令牌值?如果解决方案比单独的例程更复杂,那么我不会费心去做。

顺便说一句,我正在使用 C,但我最终需要将其移植到 C++。

谢谢。

如果问题像您的示例所表明的那样简单,您可以只使用 fscanffscanf 的问题可能与使用 flex 生成的扫描器的问题没有什么不同,换行符被简单地视为空白。如果您的扫描器 return 是换行符的特殊标记(或者可以说服您这样做,请参见下文),请继续阅读。

如果我正确理解你想做什么,你只需要正确设置输入,然后重复调用yylex。如果您使用默认的不可重入 API,那么它可能看起来像这样:

// Returns a count, and sets the out parameter to a
// malloc'd array of malloc'd strings.
// The free function is left as an exercise. 
int read_headers(char*** out) {
   int count = 0;
   char** id_array = NULL;
   *out = id_array;
   int token;
   while ((token = yylex()) == TOKEN_IDENTIFIER) {
     id_array = realloc(id_array, (count + 1) * sizeof *id_array);
     if (id_array) *out = id_array;
     else
       // Handle error
     id_array[count ++] = strdup(yytext);
   }
   if (token != '\n')
     // Handle error
   return count;
}

// Reads exactly n numbers and stores them in the out parameter,
// which must point at an array of size n.
// Returns: n on success. 0 if EOF reached. -1 on error
int read_n_numbers(double* out, int n) {
  for (int i = 0; i < n; ++i, ++out) {
    int token = yylex();
    if (token != TOKEN_NUMBER) {
      if (i == 0 && token == 0) return 0;
      // Error: non-number or too few numbers
      return -1;
    }
    *out = yylval.number;
  }
  if (yylex() != '\n') {
    // Error: too many numbers
    return -1;
  }
  return 0;
}

很可能您的扫描器实际上 return \n 换行符。这在表达式语法中很少有用(尽管有时有用)。但是很容易修改扫描器以按需处理换行;您只需要使用开始条件。我们将其设置为 inclusive 开始条件,因为它只需要处理换行符,但请注意,这意味着所有未标记的规则也处于活动状态,因此您需要确保处理的未标记规则换行符也只处理一个换行符。

%s RETURN_NEWLINES
%%
<RETURN_NEWLINES>\n { return '\n'; }
\n                  ; // Ignore a single newline
[ \t]+              ; // Ignore any number of horizontal whitespace

有了它,您可以通过在扫描之前简单地调用 BEGIN(RETURN_NEWLINES) 来启用换行符(并且 BEGIN(INITIAL) 返回忽略它们。)您需要放置启用和在您的 flex 定义文件中禁用换行扫描,因为所需的宏不会被导出。