我将正常的空白分隔用于隐藏的通道,但我有一条规则,我希望包含任何空格以便以后处理,但我找到的任何示例都需要一些非常奇怪的手动编码。
没有简单的选项可以从多个频道中读取,例如从一开始就把空白放在那里的选项。
实施例。这是WhiteSpace词法分析器
WS : ( ' '
| '\t'
| '\r'
| '\n'
) {$channel=HIDDEN;}
;
这是我的规则,我希望包含空格
raw : '{'? (~('{'))*;
基本上捕获所有与其他模式不匹配的内容的捕获所有规则,因此我需要原始流。
我希望有一个{$channel==DEFAULT || $channel==HIDDEN}
语法示例,但找不到任何。
我的目标是C#,但如果需要,我可以重写Java示例。
答案 0 :(得分:4)
AFAIK,这是不可能的。但是,您可以在解析期间扩展UnbufferedTokenStream
以更改channel
。您不能使用CommonTokenStream
,因为它缓冲了可变数量的令牌(并且缓冲区中的令牌可能位于错误的通道上!)。请注意,您至少需要ANTLR 3.3:在以前的版本中,UnbufferedTokenStream
尚未包含在内。
假设你要解析(并显示)大写或小写字母。大写字母放在HIDDEN
通道上,因此通过deafult,只会解析小写字母。但是,当解析器偶然发现小写"q"
时,我们希望更改为HIDDEN
频道。在HIDDEN
频道进行解析后,我们希望"Q"
再次将我们带回DEFAULT_CHANNEL
。
因此,在解析来源"aAbBcqCdDQeE"
时,会先打印"a"
,"b"
和"c"
,然后更改频道,然后"C"
和{{ 1}}打印,然后再次更改频道,最后"D"
打印到控制台。
这是一个执行此操作的ANTLR语法:
"e"
这是自定义令牌流类:
grammar ChannelDemo;
@parser::members {
private void handle(String letter) {
if("Q".equals(letter)) {
((ChangeableChannelTokenStream)input).setChannel(Token.DEFAULT_CHANNEL);
}
else if("q".equals(letter)) {
((ChangeableChannelTokenStream)input).setChannel(HIDDEN);
}
else {
System.out.println(letter);
}
}
}
parse
: any* EOF
;
any
: letter=(LOWER | UPPER) {handle($letter.getText());}
;
LOWER
: 'a'..'z'
;
UPPER
: 'A'..'Z' {$channel=HIDDEN;}
;
还有一个小型的主类来测试它:
import org.antlr.runtime.*;
public class ChangeableChannelTokenStream extends UnbufferedTokenStream {
public ChangeableChannelTokenStream(TokenSource source) {
super(source);
}
public Token nextElement() {
Token t = null;
while(true) {
t = super.tokenSource.nextToken();
t.setTokenIndex(tokenIndex++);
if(t.getChannel() == super.channel) break;
}
return t;
}
public void setChannel(int ch) {
super.channel = ch;
}
}
最后,生成一个词法分析器/解析器(1),编译所有源文件(2)并运行Main类(3):
java -cp antlr-3.3.jar org.antlr.Tool ChannelDemo.g
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main
java -cp .;antlr-3.3.jar Main
将导致以下内容打印到控制台:
a b c C D e
您可以在语法文件中包含该类,如下所示:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("aAbBcqCdDQeE");
ChannelDemoLexer lexer = new ChannelDemoLexer(in);
ChangeableChannelTokenStream tokens = new ChangeableChannelTokenStream(lexer);
ChannelDemoParser parser = new ChannelDemoParser(tokens);
parser.parse();
}
}
从上面的语法生成的解析器将在遇到grammar ChannelDemo;
@parser::members {
private void handle(String letter) {
if("Q".equals(letter)) {
((ChangeableChannelTokenStream)input).setChannel(Token.DEFAULT_CHANNEL);
}
else if("q".equals(letter)) {
((ChangeableChannelTokenStream)input).setChannel(HIDDEN);
}
else {
System.out.println(letter);
}
}
public static class ChangeableChannelTokenStream extends UnbufferedTokenStream {
private boolean anyChannel;
public ChangeableChannelTokenStream(TokenSource source) {
super(source);
anyChannel = false;
}
@Override
public Token nextElement() {
Token t = null;
while(true) {
t = super.tokenSource.nextToken();
t.setTokenIndex(tokenIndex++);
if(t.getChannel() == super.channel || anyChannel) break;
}
return t;
}
public void setAnyChannel(boolean enable) {
anyChannel = enable;
}
public void setChannel(int ch) {
super.channel = ch;
}
}
}
parse
: any* EOF
;
any
: letter=(LOWER | UPPER) {handle($letter.getText());}
| STAR {((ChangeableChannelTokenStream)input).setAnyChannel(true);}
;
STAR
: '*'
;
LOWER
: 'a'..'z'
;
UPPER
: 'A'..'Z' {$channel=HIDDEN;}
;
时启用从所有通道读取的内容。所以在解析"*"
:
"aAbB*cCdDeE"
打印以下内容:
a b c C d D e E
答案 1 :(得分:0)
也许你应该考虑将空白作为你的gramar的一部分。但是为什么要用这些不重要的信息来混淆你的gramar呢?好吧,因为它并不重要。换行符在某些情况下有所改善。当您需要IDE支持时,例如从visual studio语言服务器,你需要指定一个语言语法,没有所有低级ANTLR定制的花哨。
答案 2 :(得分:0)
在Antler 4
我正在使用一个简单的解决方案。我没有在Antlr 3
中测试它。它是C#,但您可以轻松地将其转换为Java。
将parser1.g4
更改为下一个:
parser grammar Parser1;
options { tokenVocab=Lexer1; }
startRule
@init { SetWhiteSpacesAcceptence(false); }
: (componentWithWhiteSpaces | componentWithoutWhiteSpaces)* EOF
;
componentWithWhiteSpaces : { SetWhiteSpacesAcceptence(true); }
component1 component2 component3
{ SetWhiteSpacesAcceptence(false); }
;
componentWithoutWhiteSpaces : component4 component5 component6
将lexer1.g4
更改为下一个:
lexer grammar Lexer1;
WS : [ \t\r\n] { if( this.IsWhiteSpacesAccepted() ) Skip(); };
将Parser1
课程扩展为下一个:
class MyParser : Parser1
{
public void SetWhiteSpacesAcceptence(bool isAccept)
{
if (_input != null && _input.TokenSource != null)
{
if (_input.TokenSource is MyLexer)
{
MyLexer lexer = _input.TokenSource as MyLexer;
if (lexer != null)
lexer.SetWhiteSpacesAcceptence(isAccept);
}
}
}
public bool IsWhiteSpacesAccepted()
{
if (_input != null && _input.TokenSource != null)
{
if (_input.TokenSource is MyLexer)
{
MyLexer lexer = _input.TokenSource as MyLexer;
if (lexer != null)
return lexer.IsWhiteSpacesAccepted();
}
}
return false;
}
}
将Lexer1
课程扩展为下一个:
class MyLexer : Lexer1
{
private bool isWhiteSpacesAccepted;
public void SetWhiteSpacesAcceptence(bool isAccept) { isWhiteSpacesAccepted = isAccept }
public bool IsWhiteSpacesAccepted() { return isWhiteSpacesAccepted; }
}
现在Main
功能为下一个:
static void Main()
{
AntlrFileStream input = new AntlrFileStream("pathToInputFile");
MyLexer lexer = new MyLexer(input);
UnbufferedTokenStream tokens = new UnbufferedTokenStream(lexer);
MyParser parser = new MyParser(tokens);
parser.startRule();
}