如何防止 Flex 忽略之前的分析?

How to prevent Flex from ignoring previous analysis?

我最近开始使用 Lex,作为解释我遇到的问题的一种简单方法,假设我正在尝试使用 Flex 实现一个词法分析器,打印给定文本中的所有字母和所有二元语法,这看起来非常容易和简单,但是一旦我实现了它,我就意识到它首先显示双字母,只有当它们是单个时才显示字母,例如:对于以下文本

 QQQZ ,JQR

结果是

 Bigram QQ
 Bigram QZ
 Bigram JQ
 Letter R
 Done

这是我的 lex 代码

 %{ 

  %}
  letter[A-Za-z]
  Separ [ \t\n]
   %%
  {letter} {
  printf(" Letter %c\n",yytext[0]);
   }

  {letter}{2} {
  printf(" Bigram %s\n",yytext);
  }
  %%
  main()
  { yylex();
  printf("Done");
   }

我的问题是如何将这两个分析分开实现,知道我的实际问题没有这个例子那么简单

词法分析器将源文本划分为单独的标记。如果您的问题看起来像那样,那么 (f)lex 是一个合适的工具。如果您的问题看起来不像那样,那么 (f)lex 可能不是正确的工具。

同时对文本进行两次分析并不是 (f)lex 的真正用例。一种可能性是使用两个独立的可重入词法分析器,安排为它们提供相同的输入。然而,对于一个可以在几行 C 中轻松解决的问题,这将是很多工作。

既然你说你的问题不同于你问题中的简单问题,我就懒得写简单的 C 代码或更复杂的代码来生成和 运行 两个独立的词法分析器,因为不可能知道这些解决方案中的任何一个是否完全相关。

如果您的问题确实是从相同的起始位置匹配两个(或更多)不同的词素,您可以使用两种策略之一,这两种策略都很丑陋(恕我直言):

  1. 我假设存在处理函数:

    void handle_letter(char ch);
    void handle_bigram(char* s);  /* Expects NUL-terminated string */
    void handle_trigram(char* s); /* Expects NUL-terminated string */
    
  2. 由于历史原因,lex 实现了 REJECT 动作,导致当前匹配被丢弃。这个想法是让你处理一个匹配项,然后拒绝它以处理一个更短的(或替代的)匹配项。对于 flex,强烈建议不要使用 REJECT,因为它效率极低,而且还会阻止词法分析器调整输入缓冲区的大小,这会任意限制可识别标记的长度。但是,在这个特定的用例中,它非常简单:

    [[:alpha:]][[:alpha:]][[:alpha:]]   handle_trigram(yytext); REJECT;
    [[:alpha:]][[:alpha:]]              handle_bigram(yytext); REJECT;
    [[:alpha:]]                         handle_letter(*yytext);
    

    如果您想尝试此解决方案,我建议使用 flex 的调试工具 (flex -d ...) 以查看发生了什么。

    参见 debugging options and REJECT documentation

  3. 虽然代码有点笨拙,但我实际上推荐的解决方案是使用yyless()重新处理部分已识别的令牌。这比 REJECT 效率高很多; yyless()只是改变了一个指针,所以对速度没有影响。如果没有 REJECT,我们必须知道所有需要的词素处理程序,但这并不难。复杂的是 handle_bigram 的接口,它需要 NUL-terminated 字符串。如果您的处理程序没有强加此要求,代码会更简单。

    [[:alpha:]][[:alpha:]][[:alpha:]]   { handle_trigram(yytext);
                                          char tmp = yytext[2];
                                          yytext[2] = 0;
                                          handle_bigram(yytext);
                                          yytext[2] = tmp;
                                          handle_letter(yytext[0]);
                                          yyless(1);
                                        }
    [[:alpha:]][[:alpha:]]              { handle_bigram(yytext);
                                          handle_letter(yytext[0]);
                                          yyless(1);
                                        }
    [[:alpha:]]                         handle_letter(*yytext);
    

    参见 yyless() documentation