天天看點

antlr4使用

一、antlr的簡介以及相關的準備處理

antlr是指可以根據輸入自動生成文法樹并可視化的顯示出來的開源文法分析器。簡潔點說,antlr是用來解決編譯、解釋等相關的問題的。比如你要新定義一門語言,不管這語言複雜還是簡單,都可以通過antlr來寫這門語言的編譯器以及解釋器。

話不多說,使用antlr需要的準備工作有:

1.安裝插件

在IDEA裡Preference->Plugins裡搜尋antlr,下載下傳ANTLR V4 gammar plugin插件,并重新開機IDEA。

antlr4使用

2.添加依賴

在項目pom.xml檔案中添加antlr的依賴。

代碼塊

<dependency>
            <groupId>org.antlr</groupId>
            <artifactId>antlr4-runtime</artifactId>
            <version>4.7.1</version>
</dependency>
           

二、正式使用antlr

1.編寫antlr獨有的g4檔案

g4檔案是antlr用來定義文法的檔案,一個g4檔案例子如下:

antlr4使用

2.生成visitor等java類

在IDEA裡右鍵g4檔案,彈出的選項中有兩個選項:

antlr4使用

a.Configure Antlr:這個選項是用來設定要生成的java檔案的位址以及包名等等

antlr4使用

記得勾選generate parse tree visitor!

b.Generate Antlr Recognizer:這個選項是生成按鈕,點選則自動生成

生成完之後會在你設定的包那裡出現很多類:

antlr4使用

2.編寫visitor的實作類

現在需要實作一個calBaseVisitor的子類(calBaseVistor是自動生成的一個實作calVisitor的子類,也可以在這個類上直接去寫自己的實作)

下面是calBaseVisitor類的代碼:

antlr4使用

然後發現現在的每個visit方法帶上的辨別就是之前寫g4檔案的#帶的标簽。實作這些方法就能實作遇到特定标簽時需要做的特定事情。傳回值則為這個處理之後的表達式的值。

實作一個calBaseVisitor的子類:

antlr4使用

完整代碼如下:

代碼塊

package com.qieqie.springboot.antlr;

import com.qieqie.springboot.antlr.gen.CalBaseVisitor;
import com.qieqie.springboot.antlr.gen.CalParser;

import java.util.HashMap;
import java.util.Map;

public class CalMyVisitor extends CalBaseVisitor {
    private Map<Integer,Double> indexValueMap = new HashMap<>();


    @Override
    public Object visitProg(CalParser.ProgContext ctx) {
        return super.visitProg(ctx);
    }

    @Override
    public Object visitIF(CalParser.IFContext ctx) {
        if((Boolean)visit(ctx.bool())){
            return visit(ctx.expr(0));
        }else {
            return visit(ctx.expr(1));
        }
    }

    @Override
    public Object visitNUMBER(CalParser.NUMBERContext ctx) {
        String index = ctx.getText();
        Double number = Double.valueOf(index.substring(1,index.length()));
        return number;
    }

    @Override
    public Object visitParens(CalParser.ParensContext ctx) {
        return visit(ctx.expr());
    }

    @Override
    public Object visitMulDiv(CalParser.MulDivContext ctx) {
        Double left=(Double)visit(ctx.expr(0));
        Double right=(Double)visit(ctx.expr(1));
        if(ctx.op.getType()==CalParser.DIV){
            if(right == 0.0){
                System.out.println("divided by zero");
                return 0;
            }else {
                return left/right;
            }
        }
        else
            return left*right;
    }

    @Override
    public Object visitAddSub(CalParser.AddSubContext ctx) {
        Double left=(Double)visit(ctx.expr(0));
        Double right=(Double)visit(ctx.expr(1));
        if(ctx.op.getType()==CalParser.ADD)
            return left+right;
        else
            return left-right;
    }

    @Override
    public Object visitINDEX(CalParser.INDEXContext ctx) {
        String index = ctx.getText();
        Integer indexId = Integer.valueOf(index.substring(1,index.length()));
        return indexValueMap.get(indexId);
    }

    @Override
    public Object visitSimpleCompareE(CalParser.SimpleCompareEContext ctx) {
        Double left = (Double)visit(ctx.expr(0));
        Double right = (Double)visit(ctx.expr(1));
        if(ctx.op.getType() == CalParser.GREATEREQUALS){
            if(left>=right){
                return true;
            }
            return false;
        }else if(ctx.op.getType() == CalParser.SMALLEREQUALS){
            if(left<=right){
                return true;
            }
            return false;
        }else {
            if(left == right){
                return true;
            }
            return false;
        }
    }

    @Override
    public Object visitSimpleCompare(CalParser.SimpleCompareContext ctx) {
        Double left = (Double)visit(ctx.expr(0));
        Double right = (Double)visit(ctx.expr(1));
        if(ctx.op.getType() == CalParser.GREATER){
            if(left>right){
                return true;
            }
            return false;
        }else {
            if(left<right){
                return true;
            }
            return false;
        }
    }

    @Override
    public Object visitAND(CalParser.ANDContext ctx) {
        for(CalParser.BoolContext bool:ctx.bool()){
            if(! (Boolean)visit(bool)){
                return false;
            }
        }
        return true;
    }

    public Map<Integer, Double> getIndexValueMap() {
        return indexValueMap;
    }

    public void setIndexValueMap(Map<Integer, Double> indexValueMap) {
        this.indexValueMap = indexValueMap;
    }
}
           

這樣就能把各種情況需要的各種處理給實作完了。

3.使用自定義的visitor來解釋一個示例:“IF(#2>@24,#[email protected]*#3,#3)”

antlr4使用

運作結果:

antlr4使用

符合需要的結果。大功告成。