Java Antlr4-当解析器花费的时间太长时,如何停止它
根据我的语法,当我分析如下条目时:Java Antlr4-当解析器花费的时间太长时,如何停止它,java,antlr4,Java,Antlr4,根据我的语法,当我分析如下条目时: ZZ9->ZZ9_LINHA := &(_cAlias+"->(CONTAEBTA)" )+& (_cAlias+"->(REGISTRO)" )+& (_cAlias+"->(TRANSACAO)" )+& (_cAlias+"->(REFERENCIA)" )+; &(_cAlias+"->(REFPAGTO)"
ZZ9->ZZ9_LINHA := &(_cAlias+"->(CONTAEBTA)" )+& (_cAlias+"->(REGISTRO)" )+& (_cAlias+"->(TRANSACAO)" )+& (_cAlias+"->(REFERENCIA)" )+;
&(_cAlias+"->(REFPAGTO)" )+& (_cAlias+"->(STATTRANSA)" )+& (_cAlias+"->(NAOUSADO1)" )+& (_cAlias+"->(NAOUSADO2)" )+;
&(_cAlias+"->(NUMFATURA)" )+& (_cAlias+"->(INDDISPUTA)" )+& (_cAlias+"->(DATFATURAM)" )+& (_cAlias+"->(DATPARTIDA)" )+;
&(_cAlias+"->(NOMVIAJANT)" )+& (_cAlias+"->(INIVIAJANT)" )+& (_cAlias+"->(ROTA)" )+& (_cAlias+"->(NAOUSADO3)" )+;
&(_cAlias+"->(CENTROCUST)" )+& (_cAlias+"->(STATUSPGTO)" )+& (_cAlias+"->(VALORPAGTO)" )+& (_cAlias+"->(SINALPAGTO)" )+;
&(_cAlias+"->(VALORTRANS)" )+& (_cAlias+"->(SINALTRANS)" )+& (_cAlias+"->(NAOUSADO4)" )+& (_cAlias+"->(NAOUSADO5)" )+;
&(_cAlias+"->(NAOUSADO6)" )+& (_cAlias+"->(NAOUSADO7)" )+& (_cAlias+"->(NUMBILHETE)" )+& (_cAlias+"->(CIAAEREA)" )+;
&(_cAlias+"->(DATEMISSAO)" )+& (_cAlias+"->(TAXAEMBARQ)" )+& (_cAlias+"->(SINALTXEMB)" )+& (_cAlias+"->(VLRBILHET)" )+;
&(_cAlias+"->(SINBILHET)" )+& (_cAlias+"->(NAOUSADO8)" )+& (_cAlias+"->(NAOUSADO9)" )+& (_cAlias+"->(TIPDESPESA)" )+;
&(_cAlias+"->(DATDESPESA)" )+& (_cAlias+"->(DEPTO)" )+& (_cAlias+"->(MATRICULA)" )+& (_cAlias+"->(CODIATA)" )+;
&(_cAlias+"->(NAOUSADOA)" )+& (_cAlias+"->(REQVIAGEM)" )+& (_cAlias+"->(NAOUSADOB)" )+& (_cAlias+"->(CLASSEVOO)" )
解析器的速度越来越慢,占用的内存也越来越多
如果工作线程暂停,则ANTLR4堆栈为:
我现在不能重构这个语法。因此,我正在寻找一种通过超时停止解析器执行的方法
我尝试了以下方法,包括ExecutorService和Future:
public class MyParserIsolateThread implements Callable<ParseTree> {
public String ppo;
public MyParserIsolateThread(String ppoInfo)
{
this.ppo = ppoInfo;
}
@Override
public ParseTree call() throws Exception {
NoCaseANTLRStringStream input = new NoCaseANTLRStringStream(this.ppo);
MyLexer lexer = new MyLexer(input);
CommonTokenStream token = new CommonTokenStream(lexer);
MyParser parser = new MyParser(token);
AntlrToSonarErrorListener error = new AntlrToSonarErrorListener(null,null,ppo);
parser.addErrorListener(error);
long startTime = System.currentTimeMillis();
System.out.println("Starting parsing...");
ParseTree tree = parser.program();
long estimatedTime = System.currentTimeMillis() - startTime;
System.out.println("Parse Finished. Elapsed time:" + estimatedTime);
return tree;
}
}
MyParserIsolateThread threadParser = new MyParserIsolateThread(ppo);
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<ParseTree> result = executorService.submit(threadParser);
try {
ParseTree tree = result.get(60, TimeUnit.SECONDS);
System.out.println("Parser OK ");
} catch (Exception e) {
// interrupts if there is any possible error
System.out.println("too long parsing...");
result.cancel(true);
}
executorService.shutdownNow();
try {
executorService.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("end.");
公共类MyParserIsolateThread实现可调用{
公共字符串ppo;
公共MyParserIsolateThread(字符串ppoInfo)
{
this.ppo=ppoInfo;
}
@凌驾
公共ParseTree调用()引发异常{
NoCaseANTLRStringStream输入=新的NoCaseANTLRStringStream(this.ppo);
MyLexer lexer=新的MyLexer(输入);
CommonTokenStream令牌=新的CommonTokenStream(lexer);
MyParser=新的MyParser(令牌);
AntlRTOSonareRorListener错误=新的AntlRTOSonareRorListener(null,null,ppo);
addErrorListener(错误);
long startTime=System.currentTimeMillis();
System.out.println(“开始解析…”);
ParseTree=parser.program();
long estimatedTime=System.currentTimeMillis()-startTime;
System.out.println(“解析完成。运行时间:“+estimatedTime”);
回归树;
}
}
MyParseRisolateThreadParser=新的MyParserIsolateThread(ppo);
ExecutorService ExecutorService=Executors.newFixedThreadPool(1);
未来结果=executorService.submit(threadParser);
试一试{
ParseTree树=result.get(60,时间单位为秒);
System.out.println(“解析器正常”);
}捕获(例外e){
//如果有任何可能的错误,则中断
System.out.println(“解析太长…”);
结果:取消(true);
}
executorService.shutdownNow();
试一试{
执行器服务。等待终止(1,时间单位。秒);
}捕捉(中断异常e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
System.out.println(“结束”);
如果我使用的条目耗时超过60秒,消息将正确显示,但即使使用Shutdownow,工作线程和池线程仍会继续运行
所以
有什么办法可以阻止这场战争吗
我不时想到一些回拨电话,看看我们是否应该停下来
我不想使用线程,而是使用.stop(),因为它已弃用且不安全
有什么想法吗?您可以使用与
紧急救援错误策略
相同的方法:在发生错误的情况下,它会抛出一个异常,而解析机制无法捕获该异常
在运行解析器之前,将侦听器添加到解析器中。重写
enterEveryRule
方法,并在调用时检查解析器到目前为止运行的时间。如果达到超时,抛出停止异常,该异常将立即停止解析运行。您可以在代码中捕获此异常,以查看是否达到超时。我确实需要检查语法,但我现在不能这样做
我遵循迈克的想法,我得到了预期的行为
我实现了对ParserATNSimulator类的扩展:
public class ParserATNSimulatorWithTimeOut extends ParserATNSimulator {
private Instant start = Instant.now();
public ParserATNSimulatorWithTimeOut(org.antlr.v4.runtime.Parser parser, ATN atn, DFA[] decisionToDFA,
PredictionContextCache sharedContextCache) {
super(parser, atn, decisionToDFA, sharedContextCache);
}
@Override
protected void closure(ATNConfig config,
ATNConfigSet configs,
Set<ATNConfig> closureBusy,
boolean collectPredicates,
boolean fullCtx,
boolean treatEofAsEpsilon)
{
Duration timeElapsed = Duration.between(start, Instant.now());
if (timeElapsed.toMinutes() >= 1 )
{
Exception e = new ParseCancellationException("Too long!!!");
throw new ParseCancellationException(e);
}
super.closure(config, configs, closureBusy,collectPredicates,fullCtx,treatEofAsEpsilon);
}
}
谢谢迈克的帮助。我按照您的建议尝试了,在正常的解析器中,我的侦听器被调用了好几次。但是当我输入特定的输入时,它不再被调用。解析器在ParserANTSimulator.clouse中循环,当我的侦听器进入那里时,它就不再被调用了。这听起来像是ANTLR4正在运行一个非常长的预测,这是由您的语法(可能还有缓慢的谓词)造成的。尝试在解析器规则中避免递归,并改用迭代。还要确保在解析运行之后执行大部分处理。不要试图在解析器中做太多的工作,而是更慷慨地接受输入,在语义阶段拒绝不符合特定条件(如标识符或数字的长度等)的输入。
public MyParser(TokenStream input) {
super(input);
_interp = new ParserATNSimulatorWithTimeOut(this,_ATN,_decisionToDFA,_sharedContextCache);
}