Antlr4-如何在花费太长时间时停止解析器

时间:2019-06-25 20:23:02

标签: java antlr4

使用我的语法,当我分析以下条目时:

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堆栈为:

Antlr Stack

我现在无法对此语法进行重构。因此,我正在寻找一种通过超时停止执行解析器的方法。

我已经使用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.");

如果我使用耗时超过60秒的条目,则会正确显示该消息,但是即使使用shutdownNow,工作线程和池线程也会继续运行。

所以

有什么方法可以停止。吗?

我想到了不时调用一些回调函数,看看是否应该停止。

我不想使用Thread,而不想使用.stop(),因为它已被弃用且不安全。

有什么主意吗?

2 个答案:

答案 0 :(得分:0)

您可以使用与BailOutErrorStrategy相同的方法:万一发生错误,它会引发异常,而解析机制无法捕获该异常。

在运行解析器之前将其添加到解析器中。覆盖enterEveryRule方法,并在调用该方法时检查解析器到目前为止运行的时间。如果超时,则抛出您的停止异常,该异常将立即停止解析运行。您可以在代码中捕获此异常,以查看是否已达到超时。

答案 1 :(得分:0)

我肯定需要复习语法,但是我现在不能这样做。

我遵循了Mike的想法,得到了预期的行为。

我实现了对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);

    }
}

然后在MyParser中对其进行更改。

public MyParser(TokenStream input) {
        super(input);
        _interp = new ParserATNSimulatorWithTimeOut(this,_ATN,_decisionToDFA,_sharedContextCache);
    }