JFlex将嵌套注释作为一个标记进行匹配

hal*_*tan 7 grammar comments lex jflex

在Mathematica中,注释以#(*结尾开头,*)注释可以嵌套.我目前使用JFlex扫描注释的方法包含以下代码

%xstate IN_COMMENT

"(*"  { yypushstate(IN_COMMENT); return MathematicaElementTypes.COMMENT;}

<IN_COMMENT> {
  "(*"        {yypushstate(IN_COMMENT); return MathematicaElementTypes.COMMENT;}
  [^\*\)\(]*  {return MathematicaElementTypes.COMMENT;}
  "*)"        {yypopstate(); return MathematicaElementTypes.COMMENT;}
  [\*\)\(]    {return MathematicaElementTypes.COMMENT;}
  .           {return MathematicaElementTypes.BAD_CHARACTER;}
}
Run Code Online (Sandbox Code Playgroud)

方法yypushstateyypopstate定义为

private final LinkedList<Integer> states = new LinkedList();

private void yypushstate(int state) {
    states.addFirst(yystate());
    yybegin(state);
}
private void yypopstate() {
    final int state = states.removeFirst();
    yybegin(state);
}
Run Code Online (Sandbox Code Playgroud)

让我有机会跟踪我正在处理多少嵌套级别的评论.

不幸的是,这导致COMMENT一个注释的几个标记,因为我必须匹配嵌套的注释开始和注释结束.

问:是否有可能与JFlex的使用其API使用类似的方法yypushbackadvance()等,以在整个注释范围恰好返回一个道理,即使评论嵌套?

hal*_*tan 5

似乎没有人需要赏金,因为解决方案是如此简单,以至于我没有考虑过。让我解释。扫描简单的嵌套注释时

(* (*..*) *)
Run Code Online (Sandbox Code Playgroud)

我必须跟踪我看到了多少个开始注释标记,以便最终我在最后一个真正的结束注释上可以将整个注释作为一个标记返回。

我没有意识到的是,当JFlex 匹配某些内容时,不需要告诉它前进到下一部分。经过仔细的审查,我看到这里对此进行了解释,但是在我不关心的部分中有些隐藏:

因为我们尚未将值返回到解析器,所以我们的扫描器会立即继续进行。

因此,flex像这样的文件中的规则

[^\(\*\)]+ { }
Run Code Online (Sandbox Code Playgroud)

读取所有字符,除了那些可能以注释开头/结尾的字符外,不执行任何操作,但前进到下一个标记

这意味着我可以简单地执行以下操作。在YYINITIAL状态下,我有一条规则与开头的注释匹配,但是它什么也不做,然后将词法分析器切换到该IN_COMMENT状态。特别是,它不返回任何令牌:

{CommentStart}      { yypushstate(IN_COMMENT);}
Run Code Online (Sandbox Code Playgroud)

现在,我们处于IN_COMMENT状态,在那里,我照做。我吃光了所有字符,但从不返回令牌。当我碰到一个新的开头评论时,我会小心地将其推入堆栈,但什么也不做。只有当我点击最后一个结束语时,我知道我要离开IN_COMMENT状态,这是唯一一点,最后我要返回令牌。让我们看一下规则:

<IN_COMMENT> {
  {CommentStart}  { yypushstate(IN_COMMENT);}
  [^\(\*\)]+      { }
  {CommentEnd}    {  yypopstate();
                     if(yystate() != IN_COMMENT)
                       return MathematicaElementTypes.COMMENT_CONTENT;
                  }
    [\*\)\(]      { }
    .             { return MathematicaElementTypes.BAD_CHARACTER; }
}
Run Code Online (Sandbox Code Playgroud)

而已。现在,无论您的注释嵌套的深度如何,您都将始终获得一个包含整个注释的单个标记。

现在,我很尴尬,对这样一个简单的问题感到抱歉。

最后说明

如果您正在执行类似的操作,则必须记住,只有在您击中正确的结束“字符”时才返回令牌。因此,您绝对应该制定一个捕获文件结尾的规则。在IDEA中,默认行为是仅返回注释令牌,因此您需要另一行(有用或无用,我想优雅地结束):

    <<EOF>>  { yyclearstack(); yybegin(YYINITIAL);
               return MathematicaElementTypes.COMMENT;}
Run Code Online (Sandbox Code Playgroud)