Parsing 使用ANTLR捕获(并保留)所有评论

Parsing 使用ANTLR捕获(并保留)所有评论,parsing,comments,antlr,Parsing,Comments,Antlr,我正在用ANTLR编写一个语法,它将Java源文件解析为AST以供以后分析。与其他解析器(如JavaDoc)不同,我试图保留所有注释。很难在代码中的任何地方使用注释。如果源代码中的某个注释与语法不匹配,ANTLR将无法完成对文件的解析 有没有办法让ANTLR自动将找到的任何注释添加到AST中?我知道lexer可以通过使用{skip();}或将文本发送到隐藏频道来忽略所有注释。设置了这两个选项中的任何一个后,ANTLR都可以毫无问题地解析文件 欢迎提出任何意见 有没有办法让ANTLR自动将找到的任

我正在用ANTLR编写一个语法,它将Java源文件解析为AST以供以后分析。与其他解析器(如JavaDoc)不同,我试图保留所有注释。很难在代码中的任何地方使用注释。如果源代码中的某个注释与语法不匹配,ANTLR将无法完成对文件的解析

有没有办法让ANTLR自动将找到的任何注释添加到AST中?我知道lexer可以通过使用
{skip();}
或将文本发送到隐藏频道来忽略所有注释。设置了这两个选项中的任何一个后,ANTLR都可以毫无问题地解析文件

欢迎提出任何意见

有没有办法让ANTLR自动将找到的任何注释添加到AST中

不,您必须在整个语法中添加额外的
注释
规则,以说明注释可能出现的所有有效位置:

...

if_stat
 : 'if' comments '(' comments expr comments ')' comments ...
 ;

...

comments
 : (SingleLineComment | MultiLineComment)*
 ;

SingleLineComment
 : '//' ~('\r' | '\n')*
 ;

MultiLineComment
 : '/*' .* '*/'
 ;
“最终Antlr 4参考”中的第12.1节说明了如何访问注释,而不必在整个语法中散布注释规则。简而言之,您可以将其添加到语法文件中:

grammar Java;

@lexer::members {
    public static final int WHITESPACE = 1;
    public static final int COMMENTS = 2;
}
然后,针对您的评论,请执行以下操作:

COMMENT
    : '/*' .*? '*/' -> channel(COMMENTS)
    ;

LINE_COMMENT
    : '//' ~[\r\n]* -> channel(COMMENTS)
    ;

然后在您的代码中,通过getHiddenTokensToLeft/getHiddenTokensToRight请求令牌,并查看本书中的12.1部分,您将看到如何做到这一点。

我在lexer部件上做到了这一点:

WS  :   ( [ \t\r\n] | COMMENT) -> skip
;

fragment
COMMENT
: '/*'.*'*/' /*single comment*/
| '//'~('\r' | '\n')* /* multiple comment*/
;
就像那样,它会自动删除它们

还可以使用“孤岛语法”功能。请参阅ANTLR4手册中的以下部分:

孤岛语法:处理同一文件中的不同格式


第一:将所有评论定向到某个频道(仅评论)

第二:打印所有评论

      CommonTokenStream tokens = new CommonTokenStream(lexer);
      tokens.fill();
      for (int index = 0; index < tokens.size(); index++)
      {
         Token token = tokens.get(index);
         // substitute whatever parser you have
         if (token.getType() != Parser.WS) 
         {
            String out = "";
            // Comments will be printed as channel 2 (configured in .g4 grammar file)
            out += "Channel: " + token.getChannel();
            out += " Type: " + token.getType();
            out += " Hidden: ";
            List<Token> hiddenTokensToLeft = tokens.getHiddenTokensToLeft(index);
            for (int i = 0; hiddenTokensToLeft != null && i < hiddenTokensToLeft.size(); i++)
            {
               if (hiddenTokensToLeft.get(i).getType() != IDLParser.WS)
               {
                  out += "\n\t" + i + ":";
                  out += "\n\tChannel: " + hiddenTokensToLeft.get(i).getChannel() + "  Type: " + hiddenTokensToLeft.get(i).getType();
                  out += hiddenTokensToLeft.get(i).getText().replaceAll("\\s", "");
               }
            }
            out += token.getText().replaceAll("\\s", "");
            System.out.println(out);
         }
      }
CommonTokenStream令牌=新的CommonTokenStream(lexer);
tokens.fill();
对于(int index=0;index
对于ANTLR v3:

空白标记通常不由解析器处理,但它们仍然在隐藏通道上被捕获


如果您使用
BufferedTokenStream
,您可以通过它获得所有令牌的列表,并进行后处理,根据需要添加它们。

我就是这么想的。哦,好吧。真正的问题是注释可以在源代码中的任何地方,所以每个规则的每个部分都必须有“注释”。@TSuds,是的,这是正确的。请注意,由于我的
注释
规则不匹配任何注释或更多注释,因此它后面不需要
。根据用例的不同,这可能不是一个好的解决方案,请参阅其他解决方案。不起作用。警告(155):vhdl.g4:1645:24:规则空间包含具有无法识别的常量值的lexer命令;lexer解释器可能会产生不正确的输出错误(164):vhdl.g4:26:0:组合语法中不支持自定义通道这不是字面问题的答案有没有办法让ANTLR自动将找到的任何注释添加到AST?但这是我需要的解决方案:-)谢谢
      CommonTokenStream tokens = new CommonTokenStream(lexer);
      tokens.fill();
      for (int index = 0; index < tokens.size(); index++)
      {
         Token token = tokens.get(index);
         // substitute whatever parser you have
         if (token.getType() != Parser.WS) 
         {
            String out = "";
            // Comments will be printed as channel 2 (configured in .g4 grammar file)
            out += "Channel: " + token.getChannel();
            out += " Type: " + token.getType();
            out += " Hidden: ";
            List<Token> hiddenTokensToLeft = tokens.getHiddenTokensToLeft(index);
            for (int i = 0; hiddenTokensToLeft != null && i < hiddenTokensToLeft.size(); i++)
            {
               if (hiddenTokensToLeft.get(i).getType() != IDLParser.WS)
               {
                  out += "\n\t" + i + ":";
                  out += "\n\tChannel: " + hiddenTokensToLeft.get(i).getChannel() + "  Type: " + hiddenTokensToLeft.get(i).getType();
                  out += hiddenTokensToLeft.get(i).getText().replaceAll("\\s", "");
               }
            }
            out += token.getText().replaceAll("\\s", "");
            System.out.println(out);
         }
      }