flex 中无法识别的规则 - 负先行

Unrecognized rule in flex - negative lookahead

当我 运行 修改此代码时,它会抱怨 unrecognized rule。我想匹配像 (b|B)^n(a|A)^m 这样的字符串 n >= 4 and m <= 3。我在 regex101 上测试了正则表达式,它工作正常。这是代码:

%{
    #include <stdio.h>
    ...
%}
* some rules *
STRING  ([bB]{4,}[aA]{1,3}(?!(a|A)+))+     // rule causing error
%%
...
{STRING} {      
            printf("%s ", yytext);
         }

编辑:应匹配整个字符串,允许的分隔符为 whitespace and\or comma。不应匹配子字符串。

(F)lex 没有实现 negative lookahead assertions。 (注意:在 link 之前 不是 背书。)

您可以在 flex manual 中找到 flex 接受的正则表达式运算符的完整列表;如果语法不在该列表中,则无论在线正则表达式服务告诉您什么,都不会被识别。 (注意:在 link 之前是 背书。)

(F)lex 确实实现了一个积极的先行断言,但只在模式的最后。这用 trailing context operator / 表示。您可以使用该运算符通过要求其后跟 A:

以外的内容来识别您的令牌
[bB]{4,}[aA]{1,3}/[^Aa]  { printf("%s ", yytext); }

但这不是完全相同的语义,因为它无法识别输入末尾的标记。它要求令牌后跟 something 而不是 A; begin 后跟 nothing 不算数。 (在实践中,这可能没有太大区别。如果您正在扫描来自文本流的输入,您可以合理地期望该流将换行符作为最后一个字符,并且换行符将匹配 [^Aa]。但是,如果您打算扫描可能根本没有换行符的文本字符串,那么您需要注意这个问题。)

大多数时候,这并不是您真正想要的。或者,如果它确实是您想要的,那么 (f)lex 可能不适合您的用例。

(F)lex 旨在将输入分成连续的标记。它不搜索令牌;它标识当前输入点的令牌。它预计整个输入将由标记组成,因此某些模式需要在每个点匹配。

在此基础上,您需要考虑不匹配序列是一种什么样的token。举个例子:

bbbbbbbaaaa

a 太多,无法成为符合您规则的 "string"。那是什么?

  1. 有效标记 bbbbbbbaaa 后跟另一个以 a?

  2. 开头的标记
  3. 匹配其他模式的有效令牌? (例如 LONG_STRING)?

  4. 应该忽略的无效令牌,允许扫描继续?

  5. 不可恢复的错误?

所有这些情况都可以在不使用任何环视运算符的情况下处理。

在第一种情况下,使用匹配有效标记的正则表达式就足够了:

[bB]{4,}[aA]{1,3}     { printf("Valid STRING: %s ", yytext); }

在第二种情况下,您可以依赖 (f)lex maximum munch 匹配规则,该规则声明将使用对应于最长匹配的模式:

[bB]{4,}[aA]{1,3}     { printf("Valid STRING: %s ", yytext); }
[bB]{4,}[aA]{4,}      { printf("Valid LONG STRING: %s ", yytext); }

可以简化为:

[bB]{4,}[aA]{1,3}     { printf("Valid STRING: %s ", yytext); }
[bB]{4,}[aA]          { printf("Valid LONG STRING: %s ", yytext); }

这将产生相同的效果,因为 (f)lex 规则决定两个具有最长匹配的模式是使用输入文件中的第一个模式。所以 bbbbaa--- 的前六个字符与两个模式都匹配,因此第一个获胜,而 bbbbaaaa--- 与第一个模式有七个字符匹配,与第二个模式有八个字符匹配,所以第二个赢了。

对于第三种和第四种情况,也可以使用上述一对模式;唯一的区别在于对应于第二个模式的动作。对于情况 3:忽略令牌,可能会发出警告;对于情况 4:产生一条错误消息并终止扫描。