Antlr4實現計算器程序

本文介紹使用Antlr4實現一個支持四則運算的程序,關於Antlr4的安裝可以參考上一篇文章。

工具:Intellij IDEA + antlr4 plugin

1. 編輯antlr4的語法文件

grammar LabelExpr;
/** 起始規則 語法分析器起點 */
prog:	stat+ ;

stat:   expr NEWLINE        # printExpr
    |   ID '=' expr NEWLINE # assign
    |   NEWLINE             # blank
    ;

expr:	expr op=('*'|'/') expr # MulDiv
    |	expr op=('+'|'-') expr # AddSub
    |	INT                 # int
    |   ID                  # id
    |	'(' expr ')'        # parens
    ;

ID      : [a-zA-Z]+ ;   // 匹配標識符
INT     : [0-9]+ ;      // 匹配整數
NEWLINE : '\r'? '\n' ;     // 新行 即語句終止標誌
WS      : [ \t]+ -> skip ; // 丟棄空白字符

MUL     : '*' ;
DIV     : '/' ;
Add     : '+' ;
SUB     : '-' ;

在IDEA中可以直接使用ANTLR Preview窗口測試語法文件是否正確

然後生成Java的解析代碼,繼承其中的額LabelExprBaseVisitor類,實現處理邏輯

package labelExpr;

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

public class MyEvalVisitor extends LabelExprBaseVisitor<Integer> {

    /**
     * 保存運行中的結果
     */
    Map<String, Integer> memory = new HashMap<>();

    @Override
    public Integer visitAssign(LabelExprParser.AssignContext ctx) {
        String id = ctx.ID().getText();
        int value = visit(ctx.expr());
        memory.put(id, value);
        return value;
    }

    @Override
    public Integer visitPrintExpr(LabelExprParser.PrintExprContext ctx) {
        Integer value = visit(ctx.expr());
        System.out.println(value);
        return 0;
    }

    @Override
    public Integer visitInt(LabelExprParser.IntContext ctx) {
        return Integer.valueOf(ctx.INT().getText());
    }

    @Override
    public Integer visitId(LabelExprParser.IdContext ctx) {
        String id = ctx.ID().getText();
        if (memory.containsKey(id)) {
            return memory.get(id);
        }
        return 0;
    }

    @Override
    public Integer visitMulDiv(LabelExprParser.MulDivContext ctx) {
        int left = visit(ctx.expr(0));
        int right = visit(ctx.expr(1));
        if (ctx.op.getType() == LabelExprParser.MUL) {
            return left * right;
        } else {
            return left / right;
        }
    }

    @Override
    public Integer visitAddSub(LabelExprParser.AddSubContext ctx) {
        int left = visit(ctx.expr(0));
        int right = visit(ctx.expr(1));
        if (ctx.op.getType() == LabelExprParser.Add) {
            return left + right;
        } else {
            return left - right;
        }
    }

    @Override
    public Integer visitParens(LabelExprParser.ParensContext ctx) {
        return visit(ctx.expr());
    }
}

測試內容如下:

193
a = 5
b = 6
a + b * 3
1 + 2 * 3

下面是測試代碼和結果

package labelExpr;

import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

import java.io.IOException;
import java.io.InputStream;

public class Main {

    public static void main(String[] args) throws IOException {
        InputStream is = Main.class.getClassLoader().getResourceAsStream("expr1.txt");

        LabelExprLexer lexer = new LabelExprLexer(CharStreams.fromStream(is));

        CommonTokenStream tokens = new CommonTokenStream(lexer);

        LabelExprParser parser = new LabelExprParser(tokens);

        ParseTree tree = parser.prog();

        MyEvalVisitor visitor = new MyEvalVisitor();
        visitor.visit(tree);

        is.close();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章