antlr4语法,用于迭代解析单个InputStream中的重复内容

antlr4语法,用于迭代解析单个InputStream中的重复内容,antlr4,Antlr4,我有一个InputStream,其中包含如下重复块: fld1:val1 fld2:val2 [A B C D] [E F] fld1:val3 fld2:val4 [M N] [Q S T Y] fld1:val5 ... 我想构造一个解决方案,我可以解析 FLD:Val块,跳过空白行分隔符,然后解析“Listy”部分,然后停止在下一行的解析,并在同一个打开的流中重置解析器以处理下一个块。我在想,通过访问解析器并调用reset(),可以在重写baselistener类exitList

我有一个
InputStream
,其中包含如下重复块:

fld1:val1
fld2:val2

[A B C D]
[E F]

fld1:val3
fld2:val4

[M N]
[Q S T Y]

fld1:val5
...

我想构造一个解决方案,我可以解析<代码> FLD:Val块,跳过空白行分隔符,然后解析“Listy”部分,然后停止在下一行的解析,并在同一个打开的流中重置解析器以处理下一个块。我在想,通过访问解析器并调用

reset()
,可以在重写baselistener类
exitListy
回调时实现这一点。理想情况下,这将结束对
ParseTree t=parser.parse()
的调用链,并让控制权立即返回到
parse()
之后的代码。我对此进行了实验,并且,在某种程度上可以预见,这里出现了一个空指针异常:
org.antlr.v4.runtime.parser.exitRule(parser.java:639)
我无法更改输入流的格式,例如在此处插入剪贴标记或类似的内容。

(基于注释的全新答案)

侦听器对解析完成后返回的解析树进行操作。在您的情况下,您将监听一个基本上是无止境的流,并希望定期返回数据

我强烈推荐实用程序员的“权威ANTLR 4参考”

有两个非常相关的部分:

  • “在分析过程中使事情发生”
  • “无缓冲字符和令牌流”
对于你的语法,尝试类似于以下“草稿”的东西(这可能不是在你想要的时候报告,但希望能给你一个想法)

语法流;
@解析器::成员{
java.util.function.Consumer;
MyData MyData=新的MyData();
公共StreamingParser(令牌流输入,java.util.function.Consumer){
这(输入);
这个。消费者=消费者;
}
}
流:(fldLine emptyLine listLine emptyLine)EOF;
fldLine:
fld=项目冒号val=项目下线{
//向MyDataObject添加数据
};
列表行:
O_括号(项目=项目)*C_括号{
//向MyDataObject添加数据
};
空位线:
下线{
consumer.accept(myData);
//重置myData
};
O_括号:“[”;
C_括号:']';
下线:'\n';
冒号:':';
项目:[a-zA-Z][a-zA-Z0-9]*;
空格->跳过;
这利用了第一节中描述的嵌入式操作

然后,第二部分描述了如何使用无缓冲流

类似的内容(未经测试;直接从参考书中摘取)

CharStream输入=新的未缓冲CharStream();
StreamingLexer lex=新StreamingLexer(输入);
lex.setTokenFactory(新的CommonTokenFactory(true));
令牌流令牌=新的无缓冲的令牌流(lex);
StreamingParser=新的StreamingParser(标记,
//当遇到空行时,此lambda将处理返回的数据
myData->handle(myData));
//您只需要ANTLR定期返回报告
//没有建立一个巨大的解析树
setBuildParseTree(false);
parser.stream();//在关闭输入流之前不会返回

@ggorlen这不是我遇到的“问题”;我只是在寻找顶级指导/技巧,了解如何使这样的结构发挥作用。块的特定规则并不重要,我使用它们的语法也很好。如果您想一次解析0到n个块,这是正确的解决方案。但我不希望解析器构建(fldblock listblock)对的列表;对于包含数百万项的流,它会破坏内存。antlr一直在“嗅探前方”。你最好的回答是:推荐我阅读,;-)谢谢这给了我新的探索领域,我不知道。将对其进行编码,并查看其是否完全工作。
grammar Streaming;

@parser::members {
    java.util.function.Consumer<MyData> consumer;
    MyData myData = new MyData();
    public StreamingParser(TokenStream input, java.util.function.Consumer<MyData> consumer) {
        this(input);
        this.consumer = consumer;
    }
}

stream: (fldLine emptyLine listLine emptyLine) EOF;

fldLine:
    fld = ITEM COLON val = ITEM EOL {
    // add data to MyDataObject
};

listLine:
    O_BRACKET (items = ITEM)* C_BRACKET {
    // add data to MyDataObject
};

emptyLine:
    EOL {
    consumer.accept(myData);
    // reset myData
};

O_BRACKET: '[';
C_BRACKET: ']';
EOL: '\n';
COLON: ':';
ITEM: [a-zA-Z][a-zA-Z0-9]*;
SPACE: ' ' -> skip;
CharStream input = new UnbufferedCharStream(<your stream>);
StreamingLexer lex = new StreamingLexer(input);
lex.setTokenFactory(new CommonTokenFactory(true));
TokenStream tokens = new UnbufferedTokenStream<CommonToken>(lex);
StreamingParser parser = new StreamingParser(tokens,
     // This lambda will handle data reported back when a blank line is encountered
     myData -> handle(myData));
// You just want ANTLR reporting back periodically
// not building a giant parse tree
parser.setBuildParseTree(false); 
parser.stream();  // won't return until you shut down the input stream