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);
    }