快速上手ANTLR--在Windows环境下解析计算器指令语法


前言

提示:网上讲述ANTLR入门的教程太多了,本文是在Windows X86 64位机器下配置运行的,仅供参考使用。这里感谢@一棵树和一枝猪的博客!


提示:以下是本篇文章正文内容,下面案例可供参考

一、antlr是什么?

antlr是一个非常强大的语法分析生成工具,可用于读取、处理、执行和翻译结构化的文本或二进制文件。它被广泛应用于学术领域和工业生产领域,是众多语言、工具和框架的基石。

二、使用步骤

声明:这里假设已经在电脑上配置好Java和ANTLR环境了。
具体配置可参考这个链接

1.引入库

自建一个Calc.g4文件代码如下:

grammar Calc;

prog : stat+;

stat : expr             # printExpr
     | ID '=' expr      # assign
     | 'print(' ID ')'  # print
     ;

expr : <assoc=right> expr '^' expr # power
     | expr op=(MUL|DIV) expr   # MulDiv
     | expr op=(ADD|SUB) expr   # AddSub
     | sign=(ADD|SUB)?NUMBER       # number
     | ID                       # id
     | '(' expr ')'             # parens
     ;


ID   : [a-zA-Z]+;
NUMBER  : [0-9]+('.'([0-9]+)?)?
        | [0-9]+;
COMMENT : '/*' .*? '*/' -> skip;
LINE_COMMENT : '//' .*? '\r'? '\n' -> skip;
WS   : [ \t\r\n]+ -> skip;
MUL  : '*';
DIV  : '/';
ADD  : '+';
SUB  : '-';

2.写一个EvalVisitor.java类

代码如下(示例):

import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;

public class EvalVisitor extends CalcBaseVisitor<Double> {
    Map<String, Double> memory = new HashMap<String, Double>();

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

    // expr
    @Override
    public Double visitPrintExpr(CalcParser.PrintExprContext ctx) {
        Double value = visit(ctx.expr());
        //保留两位有数字的方法
        DecimalFormat df = new DecimalFormat("#.##");
        String s_value = df.format(value);
        System.out.println(s_value);
        return 0.0;
    }

    //print
    @Override
    public Double visitPrint(CalcParser.PrintContext ctx){
        String id = ctx.ID().getText();
        Double value=0.0;
        if(memory.containsKey(id)) value = memory.get(id);
        DecimalFormat df = new DecimalFormat("#.##");
        String s_value = df.format(value);
        System.out.println(s_value);
        return value;

    }

    //Number
    @Override
    public Double  visitNumber(CalcParser.NumberContext ctx){
        int size = ctx.getChildCount();
        if(size == 2){
            if(ctx.sign.getType() == CalcParser.SUB){
                return -1 *  Double.valueOf(ctx.getChild(1).getText());
            }else{
                return Double.valueOf(ctx.getChild(1).getText());
            }
        }else{
            return Double.valueOf(ctx.getChild(0).getText());
        }
    }

    //ID
    @Override
    public Double visitId(CalcParser.IdContext ctx){
        String id = ctx.ID().getText();
        if(memory.containsKey(id)) return memory.get(id);
        return 0.0;
    }

    //expr op=('*'|'/') expr
    @Override
    public Double visitMulDiv(CalcParser.MulDivContext ctx)  {
        Double left = visit(ctx.expr(0));
        Double right = visit(ctx.expr(1));

        if(ctx.op.getType() == CalcParser.MUL){
            return left * right;
        }else{
            if(right == 0 || right == 0.0){
                System.out.println("Divisor can not be zero");
                return 0.0;
            }else{
                return left / right;
            }
        }
    }

    //expr op=('+'|'-') expr
    @Override
    public Double visitAddSub(CalcParser.AddSubContext ctx){
        Double left = visit(ctx.expr(0));
        Double right = visit(ctx.expr(1));
        if(ctx.op.getType() == CalcParser.ADD)
            return left + right;
        return left - right;
    }

    // '(' expr ')'
    @Override
    public Double visitParens(CalcParser.ParensContext ctx){
        return visit(ctx.expr());
    }

    // '^'
    @Override
    public Double visitPower(CalcParser.PowerContext ctx){
        Double base = visit(ctx.expr(0));
        Double exponet = visit(ctx.expr(1));
        return Math.pow(base, exponet);
    }
}

3.写一个Calc.java类(主类)

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

import java.io.InputStream;


public class Calc {
    public static void main(String[] args) throws Exception {
        CharStream input;
        if(args.length == 1) {
            String fileName = String.valueOf(args[0]);
            input = CharStreams.fromFileName(fileName);
        }else if(args.length > 1 || args.length < 0){
            throw  new Exception("the number of arguments is false, Please only give the source file or nothing and then you input your text");
        }else {
            InputStream is = System.in;
            input = CharStreams.fromStream(is);
        }
        CalcLexer lexer = new CalcLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        CalcParser parser = new CalcParser(tokens);
        ParseTree tree = parser.prog();
        EvalVisitor eval = new EvalVisitor();
        eval.visit(tree);
        System.out.println(tree.toStringTree(parser));
    }
}

4.写一个自定义的calc.txt文件

3+6*8-9/(7-4)
2*3-6+4
9/3+(6/2)
5*4+((6*7)-(3*2))/4

4.执行命令

antlr4 -no-listener -visitor Calc.g4
javac *.java
java Calc 或 java Calc calc.txt
grun Calc prog -gui calc.txt #可看生成树

5.运行结果

  1. 执行java Calc calc.txt在这里插入图片描述
  2. 执行grun Calc prog -gui calc.txt
    在这里插入图片描述

在这里插入图片描述

  1. 3+6*8-9/(7-4)
    在这里插入图片描述
  2. 2*3-6+4
    在这里插入图片描述
  3. 9/3+(6/2)
    在这里插入图片描述
  4. 5*4+((6*7)-(3*2))/4
    在这里插入图片描述

总结

以上就是Antlr计算器入门案例,本文仅仅简单介绍了Antlr最基本的使用方法,而Antlr提供的大量功能还需继续学习。 愿这篇博客能帮助到你ヾ(≧▽≦*)o

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值