AFAIK, это невозможно.Однако вы можете расширить UnbufferedTokenStream
, чтобы изменить channel
во время анализа.Вы не можете использовать CommonTokenStream
, поскольку он буферизует переменное количество токенов (и в буфере могут быть токены, которые находятся на неправильном канале!).Обратите внимание, что вам нужен как минимум ANTLR 3.3: в предыдущих версиях UnbufferedTokenStream
еще не было включено.
Допустим, вы хотите проанализировать (и отобразить) буквы как в верхнем, так и в верхнем регистре.Буквы верхнего регистра помещаются на канал HIDDEN
, поэтому по умолчанию анализируются только буквы нижнего регистра.Однако, когда синтаксический анализатор наталкивается на нижний регистр "q"
, мы хотим перейти на канал HIDDEN
.Разобравшись на канале HIDDEN
, мы хотим, чтобы "Q"
снова вернул нас к DEFAULT_CHANNEL
.
Итак, при разборе источника "aAbBcqCdDQeE"
, сначала "a"
, "b"
и"c"
печатаются, затем канал изменяется, затем "C"
и "D"
печатаются, затем канал снова изменяется, и, наконец, "e"
выводится на консоль.
Вот ANTLRграмматика, которая делает это:
ChannelDemo.g
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;}
;
А вот пользовательский класс потока токенов:
ChangeableChannelTokenStream.java
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;
}
}
Инебольшой класс Main для проверки всего этого:
Main.java
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();
}
}
Наконец, сгенерируйте лексер / парсер (1), скомпилируйте все исходные файлы (2) и запустите класс Main(3):
1
java -cp antlr-3.3.jar org.antlr.Tool ChannelDemo.g
2
javac -cp antlr-3.3.jar *.java
3 (* nix)
java -cp .:antlr-3.3.jar Main
3 (Windows)
java -cp .;antlr-3.3.jar Main
, что приведет к тому, что на консоль будет выведено следующее:
a
b
c
C
D
e
EDIT
Вы можете включить класс в файл грамматики следующим образом:
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"
:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("aAbB*cCdDeE");
ChannelDemoLexer lexer = new ChannelDemoLexer(in);
ChannelDemoParser.ChangeableChannelTokenStream tokens =
new ChannelDemoParser.ChangeableChannelTokenStream(lexer);
ChannelDemoParser parser = new ChannelDemoParser(tokens);
parser.parse();
}
}
выводится следующее:
a
b
c
C
d
D
e
E