在Flex/Lex中难以使用C风格注释

31

我想在Flex中创建一个规则来匹配C风格的注释,例如 /* */。

我有以下代码:

c_comment "/*"[\n.]*"*/"

但它从未被匹配。 任何想法为什么?如果需要更多我的代码,请告诉我,我会提交全部内容。感谢回复的任何人。


1
我不确定为什么你没有匹配成功,但是你的表达式会匹配文件中第一个"/"和最后一个"/"之间的所有内容。你用于匹配注释内容的表达式必须排除掉"*/"。一种方法是:http://flex.sourceforge.net/manual/How-can-I-match-C_002dstyle-comments_003f.html - Nate C-K
1
谢谢,那个网站很有帮助。 - adhanlon
9个回答

49

我建议你使用开始条件

%x C_COMMENT

"/*"            { BEGIN(C_COMMENT); }
<C_COMMENT>"*/" { BEGIN(INITIAL); }
<C_COMMENT>\n   { }
<C_COMMENT>.    { }
请注意,<condition>和规则之间不能有任何空格。 %x C_COMMENT定义了C_COMMENT状态,并将规则/*与它开始联系。一旦开始,*/将使其返回到初始状态(INITIAL是预定义的),每个其他字符都将被消耗而没有特定的动作。当两个规则匹配时,Flex通过选择具有最长匹配长度的规则来消除歧义,因此点规则不会阻止*/匹配。\n规则是必需的,因为点可匹配除换行符以外的所有字符%x定义使C_COMMENT成为一个独占状态,这意味着词法分析器只会在进入该状态后匹配“标记”<C_COMMENT>的规则。
这里有一个小例子Lexer,通过打印除/* comments */之外的所有内容实现了此答案。

2
我知道我来晚了,但是这个正则表达式会错误地将/* rubbish */ */识别为完整的块注释(从/*到第二个*/),而不是C风格的块注释,在这种注释中,开头的/*由最近的结束符*/终止,另一个*/被识别为程序中的杂字符。以下正则表达式(用于flex / lex)也处理了这种情况"/*"((("*"[^/])?)|[^*])*"*/"来源- [链接](https://dev59.com/BmQo5IYBdhLWcg3wUN7Y) - Shobhit
这里的问题出在 <C_COMMENT>. { } 上,如果 @zneak 使用以下代码,则可以解决问题 <C_COMMENT>[^*\n]*<C_COMMENT>"*"+[^*/\n]*。它会吞掉除了 * 后跟 / 之外的所有内容。 因此,在这种情况下,它将以第一个 * 后跟 / 结束。所以 /* rubbish */ foolosh */,它将注释掉 /* rubbish */ 并跟随下一个标记 foolish */ - Nitin Tripathi
1
@NitinTripathi,你确定这是必要的吗?我在这里没有访问flex,但是文档指出,当多个规则匹配时,选择最长的匹配。在我看来,规则不应该匹配关闭注释的*,因为关闭注释比任何字符都长。 - zneak
@NitinTripathi,这个非常简单的Flex词法分析器没有你(和@Shobhit)描述的/* rubbish */ foolosh */问题。 - zneak
我将“小例子词法分析器”编译成a.out,然后运行: echo "/* this is a multiline comment */abc" | ./a.out 其中注释块有四个换行符,结果是四个换行符后跟着'abc'。我认为这不正确 - 整个注释块应该被忽略,因此注释块中的换行符不应影响输出。 - mwag
显示剩余2条评论

9

如果有人不清楚如何使用zneak的答案,请看下面的例子:

(基本上,你把“%x C_COMMENT”放在第一部分中,其余内容放在第二部分中,正如他提供的有用链接所解释的那样)

foo.l

%{
// c code..
%}
%x C_COMMENT

%%
"/*"            { BEGIN(C_COMMENT); }
<C_COMMENT>"*/" { BEGIN(INITIAL); }
<C_COMMENT>.    { }

%%
// c code..

希望这能帮助到一些人! Tiff

7

不确定为什么它没有被捕捉到,但我知道这种模式可能会产生大型词汇元素。检测只是开始注释标记并将一切都扔进位桶,直到找到结束标记更有效率。

这个网站有代码可以实现这一点:

"/*" {
    for (;;) {
        while ((c = input()) != '*' && c != EOF)
            ; /* eat up text of comment */
        if (c == '*') {
            while ((c = input()) == '*')
                ;
            if (c == '/')
                break; /* found the end */
        }
        if (c == EOF) {
            error ("EOF in comment");
            break;
        }
    }
}

1
我不确定以那种方式消耗输入是否真的好。=/ 那不是一种关注点混杂吗? - zneak
我通常更倾向于实用主义而非教条主义 :-) - paxdiablo
1
我只看到一个问题,那就是吃掉注释以便继续词法分析真正的标记。然而,你可以争辩说这个例子没有利用flex提供的抽象机制来使你所做的更清晰。 - Nate C-K
1
@Nate,我不怀疑有更好的方法来解决这个问题,我只提供一种解决方案。我的经验是使用lex/yacc,因为在我需要开发的平台上没有flex/bison可用。这是很久以前的事情了,在那些日子里,编译器甚至看不到注释——它们被预处理器剥离,然后由我们开发环境中的一个单独的程序处理:AT&T 3B2老式机器,这应该能说明我的年龄 :-) - paxdiablo
1
在我看来,这是解决这个特定问题的一种好方法。C风格的注释在lex/flex框架中无法很清晰地表达,所以你可以写一些代码来处理它,就像你已经做的那样。这样做的好处是不需要lex状态,我觉得这样的语法更容易理解。我的评论更多是对zneak的回应:只要这里的代码严格进行词法分析(确实如此),我认为它放在正确的位置,并不会引起关注分离方面的问题。 - Nate C-K

2
我认为这个解决方案更简单:
"/*"((\*+[^/*])|([^*]))*\**"*/"

即使它是正确的(对我来说很难看出),但它是低效的,因为一个相当长的词元可能需要在 yytext 中缓冲。 - wcochran

1

Flex手册中有一个实例,它可以正确处理棘手的边缘情况:

<INITIAL>"/*"         BEGIN(IN_COMMENT);
<IN_COMMENT>"*/"      BEGIN(INITIAL);
<IN_COMMENT>[^*\n]+   // eat comment in chunks
<IN_COMMENT>"*"       // eat the lone star
<IN_COMMENT>\n        yylineno++;

1

我尝试了几种建议的解决方案,这里是结果。

  • 我无法在实践中使得C_COMMENT解决方案起作用(其中一个评论解释了至少一个原因),它应该被降级,绝对不应该是最高投票的解决方案。
  • Mugen的解决方案似乎在我运行它的所有代码中都起作用。
  • 我无法在lex中使Andrey的解决方案编译,我查看了引用网站并使用了那里的模式,但没有帮助。
  • paxdiablo的答案有效,并且有易读性的优点。我进一步修改如下:

    "/*" { int c1 = 0, c2 = input();
           for(;;) {
             if(c2 == EOF) break;
             if(c1 == '*' && c2 == '/')
               break;
             c1 = c2;
             c2 = input();
           }
         }
    

我不完全清楚为什么我的答案中的解决方案对你不起作用。如果两个 flex 规则匹配,最长的规则具有优先权。这意味着“.”规则永远不应该消耗“*/”标记的“”。此词法分析器没有遇到您描述的问题:输入“/ hello */world */”会产生预期的输出“world */”。 - zneak
我在你的答案中添加了一条评论,解释了我遇到的问题,这与注释块中嵌入的换行符有关。 - mwag

1
另一个例子:
"/*"([^*]*|(\*+[^/]))*"*/"

0

0

一个编程的实例是:

\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/

你可以在 ostermiller.org 找到它。


在Flex中,[^*] 包括 \r\n(以及除 * 之外的每个8位代码),因此 |[\r\n] 是不必要的。(就像链接文章中大多数其他正则表达式环境一样,除了 nedit。) - rici

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接