【问题标题】:how to parse only some comments using ANTLRv4如何使用 ANTLRv4 仅解析一些评论
【发布时间】:2014-08-08 11:19:03
【问题描述】:

我使用 ANTLRv4 开发分析 Java 源代码的应用程序。我声称将所有单行 cmets 与第一个令牌 TODO(例如 // TODO <some-comment>)以及直接以下语句匹配。

示例代码:

class Simple {
    public static void main(String[] args) {
        // TODO develop cycle
        for (int i = 0; i < 5; i++) {
            // unmatched comment
            System.out.println("hello");
        }
        // TODO atomic
        int a;

        // TODO revision required
        {
            int b = a+4;
            System.out.println(b);
        }
    }
}

结果 = 地图像这样:

"develop cycle" -> for(...){...}
"atomic" -> int a
"revision required" -> {...}

关注 official book (1) 和 stackoverflow 上的类似主题((2)(3)(4)(5)(6))我尝试了几种方法。

起初我希望有 (1)(2) 中描述的特殊 COMMENTS 频道,但出现错误 rule 'LINE_COMMENT' contains a lexer command with an unrecognized constant value; lexer interpreters may produce incorrect output

我想以忽略所有单行 cmets 但以 TODO 开头的方式解析源代码会更好。我希望可以将 todo-cmets 直接添加到 AST 中以使用侦听器/步行器。比我只需要为 TODO 注释注册监听器/walker 并提取以下语句,将两者都添加到所需的 ma​​p

我已经修改官方Java8 gammar 两天了,但没有任何成功。编译器抱怨或 AST 被错误混合。

这是我所做的更新:

// ...
COMMENT
    :   '/*' .*? '*/' -> skip
    ;

TODO_COMMENT
    :   '// TODO' ~[\r\n]*
    ;

LINE_COMMENT
    :   '//' ~[\r\n]* -> skip
    ;

谁能帮帮我?语法不是我的一杯茶。提前致谢

EDIT1:

上面贴的语法修改没有错误,但是生成了下面的树(请注意红色标记的节点包括int

EDIT2:

假设上面的代码示例,在调用parser.compilationUnit();时会产生以下错误

line 3:2 extraneous input '// TODO develop cycle;' expecting {'abstract', 'assert', 'boolean', 'break', 'byte', 'char', 'class', 'continue', 'do', 'double', 'enum', 'final', 'float', 'for', 'if', 'int', 'interface', 'long', 'new', 'private', 'protected', 'public', 'return', 'short', 'static', 'strictfp', 'super', 'switch', 'synchronized', 'this', 'throw', 'try', 'void', 'while', IntegerLiteral, FloatingPointLiteral, BooleanLiteral, CharacterLiteral, StringLiteral, 'null', '(', '{', '}', ';', '<', '!', '~', '++', '--', '+', '-', Identifier, '@'}
line 8:2 extraneous input '// TODO atomic;' expecting {'abstract', 'assert', 'boolean', 'break', 'byte', 'char', 'class', 'continue', 'do', 'double', 'enum', 'final', 'float', 'for', 'if', 'int', 'interface', 'long', 'new', 'private', 'protected', 'public', 'return', 'short', 'static', 'strictfp', 'super', 'switch', 'synchronized', 'this', 'throw', 'try', 'void', 'while', IntegerLiteral, FloatingPointLiteral, BooleanLiteral, CharacterLiteral, StringLiteral, 'null', '(', '{', '}', ';', '<', '!', '~', '++', '--', '+', '-', Identifier, '@'}
line 11:2 extraneous input '// TODO revision required;' expecting {'abstract', 'assert', 'boolean', 'break', 'byte', 'char', 'class', 'continue', 'do', 'double', 'enum', 'final', 'float', 'for', 'if', 'int', 'interface', 'long', 'new', 'private', 'protected', 'public', 'return', 'short', 'static', 'strictfp', 'super', 'switch', 'synchronized', 'this', 'throw', 'try', 'void', 'while', IntegerLiteral, FloatingPointLiteral, BooleanLiteral, CharacterLiteral, StringLiteral, 'null', '(', '{', '}', ';', '<', '!', '~', '++', '--', '+', '-', Identifier, '@'}

显然语法是不正确的,因为它与简单的例子很矛盾

【问题讨论】:

  • 上述语法摘录的确切错误是什么?会发生什么样的不匹配?
  • @UweAllner 所做的编辑,请参阅提供的方案 - 为什么int 也被标记?
  • @ptrbel 对我来说这看起来很正确。 int 不是// TODO xxx 的一部分,而是直接在下一个块语句子树中跟随它。据我了解您的问题,这似乎确实是理想的结果。你期待什么?
  • @UweAllner 例如ANTLR 没有生成Java8BaseVisitor::visitTODO_COMMENT - 如何在没有访问者的情况下直接匹配以下语句?
  • 恕我直言,int 仅被标记,因为它直接跟随“额外”TODO_COMMENT。我会忽略此错误,尤其是因为错误消息不会报告 int 的错误。

标签: parsing comments antlr antlr4


【解决方案1】:

原因是您不希望在任何解析器规则中出现您的特殊注释,即没有解析器会匹配它。

您可以(至少)执行以下操作:

  1. 添加一个可选的TODO_COMMENT?在每个解析器规则之前。
  2. TODO_COMMENT 令牌添加到单独的频道,例如ToDoCommentChannel(不要忘记为该通道定义常量!)并在 tree-walking 中选择注释后面的每个构造。

我将要做什么的大致轮廓:

  • TODO_COMMENTs 使用单独的频道。
  • 像往常一样使用 lex 和解析
  • 从令牌流中获取所有令牌并找到所需通道的令牌,并在默认通道上获取以下令牌并将它们存储在列表中。
  • 如果起始标记在列表中,则遍历解析并检查每个输入的规则。如果是,则将规则文本复制到结果列表中,否则递归(如果 TODO_COMMENT 可以嵌套,甚至在起始标记在列表中时递归)。

更新:

关于rule 'LINE_COMMENT' contains a lexer command with an unrecognized constant value; lexer interpreters may produce incorrect output 错误:

这可以忽略,因为它只影响 Antlrworks2 或插件使用的解释器。你也可以这样做:

//Instead of
TODO_COMMENT
    :   '// TODO' ~[\r\n]*  -> channel(ToDoCommentChannel)
    ;    

// do this (assuming the channel value is indeed 42):
TODO_COMMENT
    :   '// TODO' ~[\r\n]*  -> channel(42 /*ToDoCommentChannel*/)
    ;    

这将在 Antlrworks2 和代码中都有效(您仍然可以在您的 java 运行时代码中为通道使用常量值)。

【讨论】:

  • 频道工作正常,但我打算将 TODO 用作 AST 节点。但是,我认为这个问题已经结束,谢谢大家的帮助:)
  • 如果您希望它们作为节点(我真的不知道为什么要这样做),请使用选项 1 并将它们放在可能出现的每个位置的前面。
猜你喜欢
  • 1970-01-01
  • 2012-07-12
  • 2020-06-30
  • 2019-05-01
  • 2012-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多