読者です 読者をやめる 読者になる 読者になる

cloverrose's blog

Python, Machine learning, Emacs, CI/CD, Webアプリなど

ANTLRで演算子をTOKENにしない方がいい

ANTLR ANTLRv4

この間ANTLRv4でどの演算子が読まれたか調べる綺麗な方法がないからTOKEN規則として吐き出せばいいと書きましたが、あれは間違いでした。

問題が発覚したのは数式の単項演算子の-と二項演算子の-のところでANTLRがパースできなかったからです。


以下、修正した文法ファイルとテスト用のListenerクラス

Sample.g4

expr:  INT
    |  '(' expr ')'
    |  '-' expr
    |  expr ('*' | '/' | '%') expr
    |  expr ('+' | '-') expr
    ;


TestListener extends SampleBaseListener

    // expr
    private String parseExpr(SampleParser.ExprContext ctx){
        String[] unOp = {"-"};
        String[] binOp = {"*", "/", "%", "+", "-"};

        if(ctx.INT() != null) {
            return ctx.INT().getText();
        } else if(ctx.getChildCount() == 2 && Arrays.asList(unOp).contains(ctx.getChild(0).getText())) {
            String op = ctx.getChild(0).getText();
            String expr = parseExpr(ctx.expr(0));
            return String.format("(%s %s)" , op, expr);
        } else if(ctx.getChildCount() == 3 && Arrays.asList(binOp).contains(ctx.getChild(1).getText())) {
            String op = ctx.getChild(1).getText();
            String e0 = parseExpr(ctx.expr(0));
            String e1 = parseExpr(ctx.expr(1));
            return String.format("%s %s %s)", e0, op, e1);
        } else { // '(' expr ')'
            String expr = parseExpr(ctx.expr(0));
            return String.format("%s)", expr);
        }
    }

という感じでTreeの子要素の数から演算子の位置を0か1で決めて、演算子として取得した文字列が本当に演算子(括弧のルールではない)か検査するというようにしました。