我认为您正确使用了无缓冲流,您看到的是使用这些流的预期结果。但我想你可能对他们有期望,他们没有义务满足。
下面是我们用棍子戳的测试代码。我正在使用System.in
输入,所以我修改了语法以说明单词标记之间的换行符。
流媒体.g
grammar Streaming;
fox : 'quick' NL 'brown' NL 'fox' NL DONE NL;
DONE : 'done';
NL : '\r'? '\n';
StreamingTest.java
import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.CommonTokenFactory;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.UnbufferedCharStream;
import org.antlr.v4.runtime.UnbufferedTokenStream;
import org.antlr.v4.runtime.tree.TerminalNode;
public class StreamingTest {
public static void main(String[] args) throws Exception {
lex();
parse();
}
private static void lex() {
System.out.println("-> Reading from lexer:");
UnbufferedCharStream input = new UnbufferedCharStream(System.in);
StreamingLexer lexer = new StreamingLexer(input);
lexer.setTokenFactory(new CommonTokenFactory(true));
Token t;
//read each token until hitting input "done"
while ((t = lexer.nextToken()).getType() != StreamingLexer.DONE){
if (t.getText().trim().length() == 0){
System.out.println("-> " + StreamingLexer.tokenNames[t.getType()]);
} else {
System.out.println("-> " + t.getText());
}
}
}
private static void parse() {
System.out.println("-> Reading from parser:");
UnbufferedCharStream input = new UnbufferedCharStream(System.in);
StreamingLexer lexer = new StreamingLexer(input);
lexer.setTokenFactory(new CommonTokenFactory(true));
StreamingParser parser = new StreamingParser(new UnbufferedTokenStream<CommonToken>(lexer));
parser.addParseListener(new StreamingBaseListener(){
@Override
public void visitTerminal(TerminalNode t) {
if (t.getText().trim().length() == 0){
System.out.println("-> " + StreamingLexer.tokenNames[t.getSymbol().getType()]);
} else {
System.out.println("-> " + t.getText());
}
}
});
parser.fox();
}
}
下面是输入和输出的混合,因为它们在上述程序中的词法分析器和解析器中提供/接收。每行输出都带有前缀->
。我会解释为什么事情是在那之后的样子。
输入输出
-> Reading from lexer:
quick
-> quick
brown
-> NL
-> brown
fox
-> NL
-> fox
done
-> NL
-> Reading from parser:
quick
brown
-> quick
-> NL
fox
-> brown
-> NL
done
-> fox
-> NL
-> done
-> NL
我注意到的第一件事是词法分析器立即收到quick
NL
输入,但只为quick
. 这种差异的原因是UnbufferedCharStream
提前读取了一个字符(即使它NL
为我准备了一个非常好的令牌!),因为它不会位于一个空的预读字符缓冲区中。唉,未缓冲的流被缓冲了。根据类本身的 Javadoc 注释:
这里的“无缓冲”是指它不缓冲所有数据,而不是按需加载char。
这种额外的读取转化为在流上等待更多输入,这解释了为什么词法分析器对于其余输入来说是一个标记。
现在转到解析器。为什么它落后于词法分析器的两个标记?简单:因为UnbufferedTokenStream
也不会坐在空的前瞻缓冲区上。但是它不能创建下一个标记,直到 a) 它有一个来自词法分析器的备用标记和 b) 词法分析器UnbufferedCharStream
读取它自己的前瞻字符。实际上,它是词法分析器的单字符“滞后”加上单标记“滞后”。
看来,在 ANTLR v4 中获得“无延迟”的按需数据流意味着自己编写。但在我看来,现有的流按预期工作。
Antlr 是否适合在要解析的文本之后从没有 EOF 的流中解析数据?
我还不能对 ANTLR 4 充满信心地回答这个问题。编写一个在需要之前不会提前缓冲的令牌流似乎很容易(覆盖UnbufferedTokenStream
'sconsume
以跳过调用sync
),但是无论任何人的缓冲如何,这些字符流都会被自己提前读取的类调用。或者看起来是这样。我会尽我所能继续深入研究,但这可能需要学习官方的方法来做到这一点。