为什么flex / bison中的多行注释如此回避?

时间:2010-11-10 14:29:43

标签: c comments bison multiline flex-lexer

我正在尝试在我的flex(.l)文件中解析C风格的多行注释:

%s ML_COMMENT
%%

...

<INITIAL>"/*"                   BEGIN(ML_COMMENT);
<ML_COMMENT>"*/"                BEGIN(INITIAL);  
<ML_COMMENT>[.\n]+              { }

我没有返回任何令牌,我的语法(.y)也没有以任何方式处理评论。

当我运行我的可执行文件时,我得到一个解析错误:

$ ./a.out
/*
abc 
def
Parse error: parse error
$ echo "/* foo */" | ./a.out
Parse error: parse error

(我的yyerror函数执行printf(“解析错误:%s \ n”),这是冗余错误消息的前半部分来自的地方)。

我可以看到为什么第二个示例失败,因为整个输入是注释,并且由于语法忽略了注释,因此没有语句。因此输入不是有效的程序。但是在我完成评论之前,第一部分会抛出一个解析错误。

同样令人困惑:

$ ./a.out
/* foo */
a = b;
Parse error: parse error

在这种情况下,注释在实际有效输入之前关闭(没有注释,解析就好了)。解析“a”后实际发生失败,而不是在尝试解析赋值“a = b;”之后。如果我在自己的行上输入“a”,它仍然会抛出错误。

鉴于错误消息是解析器错误而不是扫描程序错误,我的.y文件中是否存在一些重要的内容?或者我在扫描器规则中做错了什么,传播到解析器端?

编辑: Per @ Rudi的建议,我打开调试并发现:

$ ./a.out
Starting parse
Entering state 0
Reading a token: /*
foo
Next token is 44 (IDENTIFER)
Shifting token 44 (IDENTIFER), Entering state 4
Reducing via rule 5 (line 130), IDENTIFER  -> identifier
state stack now 0
Entering state 5

我关闭了调试,发现/* foo */ = bar;确实解析了foo = bar;。我正在使用flex 2.5.4;它没有给我任何关于我试图使用的有状态规则的警告。

4 个答案:

答案 0 :(得分:5)

以这种方式解析评论会导致错误,因为:

  • 您需要为所有lex规则添加条件
  • 如果您还想处理//评论
  • ,它会变得更加复杂
  • 你仍然存在yacc / bison合并两条评论的风险,包括其中的所有内容

在我的解析器中,我处理这样的评论。首先为评论的开头定义lex规则,如下所示:

\/\*     {
         if (!SkipComment())
            return(-1);
         }

\/\/     {
         if (!SkipLine())
            return(-1);
         }

然后编写SkipComment和SkipLine函数。他们需要消耗所有输入,直到找到注释的结尾(这是相当古老的代码,所以请原谅我有些过时的结构):

bool SkipComment (void)
{
int Key;

Key=!EOF;
while (true)
   {
   if (Key==EOF)
      {
      /* yyerror("Unexpected EOF within comment."); */
      break;
      }
   switch ((char)Key)
      {
      case '*' :
         Key=input();
         if (char)Key=='/') return true;
         else               continue;
         break;
      case '\n' :
         ++LineNr;
         break;
      }
   Key=input();
   }

return false;
}

bool SkipLine (void)
{
int Key;

Key=!EOF;
while (true)
   {
   if (Key==EOF)
      return true;
   switch ((char)Key)
      {
      case '\n' :
         unput('\n');
         return true;
         break;
      }
   Key=input();
   }

return false;
}

答案 1 :(得分:5)

我认为您需要将ML_COMMENT启动条件声明为独占启动条件,因此只有ML_COMMENT规则处于活动状态。 %x ML_COMMENT代替%s ML_COMMENT

否则没有开始条件的规则也是有效的。

答案 2 :(得分:1)

除了%x vs %s的问题之外,您还遇到. [.\n]匹配(仅)文字.的问题'除了换行符之外的任何字符'就像裸.那样。你想要一个像

这样的规则
<ML_COMMENT>.|"\n"     { /* do nothing */ }

代替

答案 3 :(得分:1)

我发现C语言语法(实际上只是词法分析器)的这种描述非常有用。我认为它与帕特里克的答案大致相同,但略有不同。

http://www.lysator.liu.se/c/ANSI-C-grammar-l.html