【问题标题】:Unrecognized rule in flex - negative lookaheadflex中无法识别的规则 - 负前瞻
【发布时间】:2017-12-30 11:11:11
【问题描述】:

当我在这段代码上运行 flex 时,它会抱怨 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 和\或comma。不应匹配子字符串。

【问题讨论】:

    标签: regex flex-lexer lex


    【解决方案1】:

    (F)lex 没有实现negative lookahead assertions。 (注意:前面的链接不是认可。)

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

    (F)lex 确实实现了积极的前瞻断言,但仅在模式的最后。这用trailing context operator / 表示。您可以使用该运算符来识别您的令牌,方法是要求它后跟 A 以外的其他内容:

    [bB]{4,}[aA]{1,3}/[^Aa]  { printf("%s ", yytext); }
    

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

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

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

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

    bbbbbbbaaaa
    

    这有太多的 a 不能成为您的规则的“字符串”。那是什么?

    1. 有效令牌 bbbbbbbaaa 后跟另一个以 a 开头的令牌?

    2. 与其他模式匹配的有效令牌? (例如LONG_STRING)?

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

    4. 不可恢复的错误?

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

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

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

    在第二种情况下,您可以依赖 (f)lex 最大 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:生成错误消息并终止扫描。

    【讨论】:

    • 感谢您的全面回答!实际上我仍然无法解决我的问题。我发现即使它有效,我的原始模式逻辑也不好,因为我只需要匹配由空格或逗号分隔的整个单词,而不是子字符串(编辑问题)。我可以做到这一点,但最后的分隔符成为我比赛的一部分。在这种情况下使用前瞻似乎很自然。
    • @pavlee:您需要(对自己)回答我在回答开头提出的问题:bbbbaaaa 是什么类型的令牌? 因为如果您是将使用标记器,everything 是一个标记。 (甚至空格,它通常是一个被忽略的——因此未报告的——标记。)我建议的双模式框架几乎肯定是你需要的。底线:由于最长可能的令牌是匹配的,因此您无需明确考虑接下来会发生什么,因为您已经知道接下来发生的事情不能成为令牌的一部分。
    • 这为我清除了一切,现在我完全理解了两种模式的东西:) 我对正则表达式的了解不多,所以一开始就知道这一点非常有用。感谢您的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    • 2014-01-19
    • 1970-01-01
    • 2012-08-11
    • 2011-01-25
    • 1970-01-01
    相关资源
    最近更新 更多