有时象注释这样的部分在编译时并不是完全放弃的,如java中在/**….*/的注释形式中可以添加说明最终生成程序的文档,在.NET中使用 /// 注释形式也有类似的功能。也就是说我们在编译代码时忽略这些注释,而在分析生成文档时又要用到这些注释。这样的话就不能使用skip();方法了,skip()方法是抛弃其内容。所以ANTLR中加入了channel属性用来指定当前匹配的内容是属于哪一个频道,分析过程中关心哪个频道就可以只接收哪个频道的内容。ANTLR中定义有两个频道常量DEFAULT_CHANNEL和HIDDEN_CHANNEL,一般情况下匹配的内容都中放到DEFAULT_CHANNEL中。我们可以指定让当前匹配的内容放到哪个频道中。
前面示例中的词法规则可以改成如下形式。
COMMENT : '/*' .* '*/' {$channel=HIDDEN;} ;
LINE_COMMENT :'//' ~ ('\n' | '\r') * '\r'? '\n' {$channel=HIDDEN;} ;
WS : ( ' ' | '\t'| '\n' | '\r' ) + {$channel=HIDDEN;};
COMMENT、LINE_COMMENT和WS词法规则后面加入{$channel=HIDDEN;},这样匹配的字符就会被存放到HIDDEN频道中。下面将这个修改后的文法编译运行,其结果和使用skip()方法时是一样的。这就是说存在于HIDDEN频道中的内容被语法分析器忽略了,因为在默认情况下ANTLR语法分析程序只分析DEFAULT_CHANNEL中的内容。
Channel这个概念是介于词法分析和语法分析之间的一个概念,将存放匹配的内容存放到各个频道中是在词法分析时进行的,而语法分析时可以有选择的读某一个频道中的内容。那么如何让语法分析程序处理示例中的注释内容呢?我们再次修改文法因为我们只关心注释所以把WS : ( ' ' | '\t' | '\n' | '\r' ) + {Skip();};改回成skip()方法因为空白我们是不关心的,下面给出全部的文法。
grammar TestHidden;
options {
language=CSharp;
output=AST;
}
a : b INT b;
b : (COMMENT |LINE_COMMENT)*;
INT : DIGIT+;
DIGIT : '0' ..'9';
COMMENT : '/*' .* '*/' {$channel=HIDDEN;} ;
LINE_COMMENT :'//' ~ ('\n' | '\r') * '\r'? '\n' {$channel=HIDDEN;} ;
WS : ( ' ' | '\t'| '\n' | '\r' ) + {Skip();} ;
下面是运行代码,运行代码中加入了tokenStream.SetTokenTypeChannel方法。
TestSkipLexer lex= new TestSkipLexer(new ANTLRFileStream("f1.txt"));
CommonTokenStreamtokenStream = new CommonTokenStream(lex);
TestSkipParserparser = new TestSkipParser(tokenStream);
tokenStream.SetTokenTypeChannel(TestSkipLexer.COMMENT,Token.DEFAULT_CHANNEL);
tokenStream.SetTokenTypeChannel(TestSkipLexer.LINE_COMMENT,Token.DEFAULT_CHANNEL);
TestSkipParser.a_return bReturn = parser.a();