And*_*eyP 6 sockets stream eof antlr4
Antlr是否适合解析文本解析后没有EOF的流中的数据?根据我的观察,词法分析器在收到下一个令牌的第一个字符之前不会发出当前令牌.最重要的是 - 在收到下一个规则的第一个标记之前,解析器似乎不会发出规则.这是我尝试过的简单语法:
fox: 'quick' 'brown' 'fox' '\r'? '\n' ;
Run Code Online (Sandbox Code Playgroud)
然后我使用生成的解析器与UnbufferedCharStream和UnbufferedTokenStream:
CharStream input = new UnbufferedCharStream(is);
MyLexer lex = new MyLexer(input);
lex.setTokenFactory(new CommonTokenFactory(true));
TokenStream tokens = new UnbufferedTokenStream(lex);
MyParser parser = new MyParser(tokens);
MyParser.FoxContext fox = parser.fox();
Run Code Online (Sandbox Code Playgroud)
当流" 快速 "时 - 没有任何反应.
当' b '进来时 - 进入规则' 狐狸 '
然后' roun ' - 什么都没有(2个令牌在流中 - 没有人知道它们!)
只有在' f '之后,听众才会访问第一个令牌:' quick '
然后 - ' 牛 ' 没什么
在新行(unix):访问令牌' 棕色 '
现在,流具有所有数据(4个令牌),但只能识别2个令牌.
我发现为了通过系统推送这些令牌,流可以发出2个令牌,即语法已知的任何令牌.它可能是2个额外的新行,或者说' 狐狸 '和' 棕色 '.只有这样才能访问令牌" 狐狸 "和" \n ",解析器退出规则" 狐狸 "并解析完成.
这是一个错误还是一个功能?有没有办法消除这种滞后?
谢谢!
ANTLR 4本书最初将包含一个解析流输入的示例,但我反对它,因为使用自适应无限前瞻解析器不可避免地会产生严重的复杂性.
ANTLR 4没有保证前瞻限制(并且无法告诉它查找甚至尝试强制执行),因此任何在阻塞流上运行的实现都有可能出现死锁,而不会返回有关该解析的信息. .除非我先看到一个中间缓冲区,否则我甚至不会接受解析流输入的可能性.
String或char[].ANTLRInputStream为缓冲区创建一个.解析的结果将告诉您是否将结果丢弃到该点,或者在有更多数据可用时保持它们重试:
如果没有语法错误发生,则输入已成功解析,并且您可以在稍后可用时解析输入的下一部分.
如果报告一个语法错误之前的EOF令牌被消耗,然后会出现语法错误,在实际输入,所以你要处理它(报告给用户,等...).
如果在使用EOF令牌的位置报告语法错误,则可以使用其他输入来解决问题 - 忽略当前解析的结果,然后再次从输入流重试数据.
我认为您正确使用了无缓冲流,并且您看到的是使用这些流的预期结果。但我认为你可能对他们抱有期望,但他们没有义务满足。
下面是我们用棍子戳的测试代码。我用于System.in输入,因此我修改了语法以考虑单词标记之间的换行符。
grammar Streaming;
fox : 'quick' NL 'brown' NL 'fox' NL DONE NL;
DONE : 'done';
NL : '\r'? '\n';
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
下面是上面程序中的词法分析器和解析器提供/接收的输入和输出的混合。每行输出都以->. 之后我会解释为什么事情会变成这样。
-> 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
Run Code Online (Sandbox Code Playgroud)
我注意到的第一件事是词法分析器立即收到quick NL输入,但只提供了 的令牌quick。造成这种差异的原因是,UnbufferedCharStream又预读了一个字符(尽管它已经NL为我准备好了一个完美的标记!),因为它不会位于空的预读字符缓冲区上。唉,未缓冲的流已被缓冲。根据类本身的 Javadoc 注释:
这里的“无缓冲”是指它不缓冲所有数据,而不是按需加载字符。
这一额外的读取转化为等待流上的更多输入,这解释了为什么词法分析器比其余输入落后一个标记。
现在进入解析器。为什么它比词法分析器的标记落后两个标记?简单:因为UnbufferedTokenStream也不会坐在空的前瞻缓冲区上。但它无法创建下一个标记,直到 a) 它从词法分析器获得了备用标记,并且 b) 词法分析器UnbufferedCharStream读取了自己的先行字符。实际上,它是词法分析器的单字符“滞后”加上单标记“滞后”。
看来,在 ANTLR v4 中获得“无滞后”的按需数据流意味着需要编写自己的流。但在我看来,现有的流按预期工作。
Antlr 是否适合解析来自要解析的文本之后没有 EOF 的流中的数据?
我还不能对 ANTLR 4 充满信心地回答这个问题。编写一个在需要之前不提前缓冲的令牌流似乎很容易(覆盖以跳过调用UnbufferedTokenStream),但是字符流由那些自己提前读取的类调用,而不管任何人的缓冲如何。或者看起来是这样。我将尽我所能继续深入研究这一点,但这可能需要学习官方方法来做到这一点。consumesync