使用 ANTLR 捕获(并保留)所有评论
Posted
技术标签:
【中文标题】使用 ANTLR 捕获(并保留)所有评论【英文标题】:Catching (and keeping) all comments with ANTLR 【发布时间】:2012-09-11 04:43:01 【问题描述】:我正在用 ANTLR 编写一个语法,它将 Java 源文件解析为 AST 以供以后分析。与其他解析器(如 JavaDoc)不同,我试图保留所有的 cmets。这是很难的 cmets 可以在代码中的任何地方使用。如果源代码中的注释与语法不匹配,ANTLR 无法完成文件的解析。
有没有办法让 ANTLR 自动将它找到的任何 cmets 添加到 AST 中?我知道词法分析器可以使用skip();
或通过将文本发送到隐藏通道来简单地忽略所有 cmets。设置了这些选项中的任何一个后,ANTLR 都可以毫无问题地解析文件。
欢迎提出任何想法。
【问题讨论】:
【参考方案1】:“The Definitive Antlr 4 Reference”中的第 12.1 节展示了如何访问 cmets,而不必在整个语法中散布 cmets 规则。简而言之,您将其添加到语法文件中:
grammar Java;
@lexer::members
public static final int WHITESPACE = 1;
public static final int COMMENTS = 2;
然后为您的 cmets 规则执行以下操作:
COMMENT
: '/*' .*? '*/' -> channel(COMMENTS)
;
LINE_COMMENT
: '//' ~[\r\n]* -> channel(COMMENTS)
;
然后在您的代码中通过 getHiddenTokensToLeft/getHiddenTokensToRight 请求令牌并查看本书中的 12.1 部分,您将了解如何执行此操作。
【讨论】:
不起作用。警告(155):vhdl.g4:1645:24:规则 SPACE 包含一个具有无法识别的常量值的词法分析器命令;词法解释器可能会产生不正确的输出错误(164):vhdl.g4:26:0:组合语法中不支持自定义通道【参考方案2】:首先:将所有 cmets 定向到某个频道(仅 cmets)
COMMENT
: '/*' .*? '*/' -> channel(2)
;
LINE_COMMENT
: '//' ~[\r\n]* -> channel(2)
;
第二个:打印出所有cmets
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);
【讨论】:
这不是字面问题的答案有没有办法让 ANTLR 自动将它找到的任何 cmets 添加到 AST?,但这是我需要的解决方案:-) 谢谢【参考方案3】:有没有办法让 ANTLR 自动将它找到的任何 cmets 添加到 AST 中?
不,您必须在整个语法中添加额外的 comments
规则,以说明 cmets 可能出现的所有有效位置:
...
if_stat
: 'if' comments '(' comments expr comments ')' comments ...
;
...
comments
: (SingleLineComment | MultiLineComment)*
;
SingleLineComment
: '//' ~('\r' | '\n')*
;
MultiLineComment
: '/*' .* '*/'
;
【讨论】:
我就是这么想的。那好吧。真正的问题是 cmets 可以在源代码中的任何位置,所以每个规则都必须有“cmets”?在它的每一个部分。 @TSuds,是的,这是正确的。请注意,由于我的comments
规则不匹配任何或更多 cmets,因此在它之后不需要 ?
。
根据用例,这可能不是一个好的解决方案,请参阅其他。【参考方案4】:
也可以使用“孤岛语法”功能。请参阅 ANTLR4 书中的以下部分:
Island Grammars:处理同一文件中的不同格式
【讨论】:
【参考方案5】:我在我的词法分析器部分做到了:
WS : ( [ \t\r\n] | COMMENT) -> skip
;
fragment
COMMENT
: '/*'.*'*/' /*single comment*/
| '//'~('\r' | '\n')* /* multiple comment*/
;
就像它会自动删除它们!
【讨论】:
【参考方案6】:对于 ANTLR v3:
解析器通常不处理空白标记,但它们仍会在 HIDDEN 通道上捕获。
如果您使用BufferedTokenStream
,您可以通过它获取所有令牌的列表并进行后处理,根据需要添加它们。
【讨论】:
以上是关于使用 ANTLR 捕获(并保留)所有评论的主要内容,如果未能解决你的问题,请参考以下文章