antlr4实现简单计算器功能

概述

通过简单示例,说明antlr4的用法,目标需要达到如下效果:
 1+2 => 1+2=3
 1+2*4 => 1+2*4=9
 1+2*4-5 => 1+2*4-5=4
 1+2*4-5+20/5 => 1+2*4-5+20/5=8
 (1+2)*4 => (1+2)*4=12

处理流程如下

Created with Raphaël 2.2.0 开始 输入:(1+2)*4 expr NEWLINE? expr:+-*/,id,int 结束 ID '=' expr (NEWLINE)? NEWLINE yes no yes no yes

通用词法规则文件,CommonLexerRules.g4

// 定义词法规则
lexer grammar CommonLexerRules;

//////// 定义此法
// 匹配ID
ID     : [a-zA-Z]+ ;
// 匹配INT
INT    : [0-9]+    ;
// 匹配换行符
NEWLINE: '\n'('\r'?);
// 跳过空格、跳格、换行符
WS     : [ \t\n\r]+ -> skip;

//////// 运算符
DIV:'/';
MUL:'*';
ADD:'+';
SUB:'-';
EQU:'=';

语法文件,LibExpr.g4

// 定于语法规则
grammar LibExpr;

// 导入词法规则
import CommonLexerRules;

// 词法根
prog:stat+ EOF?;

// 定义声明
stat:expr (NEWLINE)?         # printExpr
    | ID '=' expr (NEWLINE)? # assign
    | NEWLINE                # blank
    ;

// 定义表达式
expr:expr op=('*'|'/') expr # MulDiv
    |expr op=('+'|'-') expr # AddSub
    |'(' expr ')'           # Parens
    |ID                     # Id
    |INT                    # Int
    ;

逻辑实现类,LibExprVisitorImpl.java

// 导入Map包
import java.util.Map;
import java.util.HashMap;

/**
* 重写访问器规则,实现数据计算功能
* 目标: 
*     1+2 => 1+2=3
*     1+2*4 => 1+2*4=9
*     1+2*4-5 => 1+2*4-5=4
*     1+2*4-5+20/5 => 1+2*4-5+20/5=8
*     (1+2)*4 => (1+2)*4=12
*/
public class LibExprVisitorImpl extends LibExprBaseVisitor<Integer> {
	// 定义数据
	Map<String,Integer> data = new HashMap<String,Integer>();

	// expr (NEWLINE)?         # printExpr
	@Override 
	public Integer visitPrintExpr(LibExprParser.PrintExprContext ctx) { 
		System.out.println(ctx.expr().getText()+"="+visit(ctx.expr()));
		return visit(ctx.expr());
	}

	// ID '=' expr (NEWLINE)? # assign
	@Override 
	public Integer visitAssign(LibExprParser.AssignContext ctx) { 
		// 获取id
		String id = ctx.ID().getText();
		// // 获取value
		int value = Integer.valueOf(visit(ctx.expr()));

		// 缓存ID数据
		data.put(id,value);

		// 打印日志
		System.out.println(id+"="+value);

		return value; 
	}

	// NEWLINE                # blank
	@Override 
	public Integer visitBlank(LibExprParser.BlankContext ctx) { 
		return 0; 
	}

	// expr op=('*'|'/') expr # MulDiv
	@Override 
	public Integer visitMulDiv(LibExprParser.MulDivContext ctx) { 
		// 左侧数字
		int left = Integer.valueOf(visit(ctx.expr(0)));
		// 右侧数字
		int right = Integer.valueOf(visit(ctx.expr(1)));
		// 操作符号
		int opType = ctx.op.getType();

		// 调试
		// System.out.println("visitMulDiv>>>>> left:"+left+",opType:"+opType+",right:"+right);

		// 判断是否为乘法
		if(LibExprParser.MUL==opType){
			return left*right;
		}

		// 判断是否为除法
		return left/right;

	}

	// expr op=('+'|'-') expr # AddSub
	@Override 
	public Integer visitAddSub(LibExprParser.AddSubContext ctx) { 
		// 获取值和符号

		// 左侧数字
		int left = Integer.valueOf(visit(ctx.expr(0)));
		// 右侧数字
		int right = Integer.valueOf(visit(ctx.expr(1)));
		// 操作符号
		int opType = ctx.op.getType();

		// 调试
		// System.out.println("visitAddSub>>>>> left:"+left+",opType:"+opType+",right:"+right);

		// 判断是否为加法
		if(LibExprParser.ADD==opType){
			return left+right;
		}

		// 判断是否为减法
		return left-right;

	}

	// '(' expr ')'           # Parens
	@Override 
	public Integer visitParens(LibExprParser.ParensContext ctx) { 
		// 递归下调
		return visit(ctx.expr()); 
	}

	// ID                     # Id
	@Override 
	public Integer visitId(LibExprParser.IdContext ctx) { 
		// 获取id
		String id = ctx.ID().getText();
		// 判断ID是否被定义
		if(data.containsKey(id)){
			// System.out.println("visitId>>>>> id:"+id+",value:"+data.get(id));
			return data.get(id);
		}
		return 0; 
	}

	// INT                    # Int
	@Override 
	public Integer visitInt(LibExprParser.IntContext ctx) {
		// System.out.println("visitInt>>>>> int:"+ctx.INT().getText());
		return Integer.valueOf(ctx.INT().getText());
	}

}

语法集成类,LibExprPrint.java
用于获取输入流和集成访问器

//导入antlr runtime包
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
//导入ioException包

import java.io.FileNotFoundException;
import java.io.IOException;

/**
* 打印语法树
*/
public class LibExprPrint{

    // 打印语法树 input -> lexer -> tokens -> parser -> tree -> print
    public static void main(String args[]){
                // 判断是否传入文件名
        if(args.length>0){
            printTree(args[0]);
        }else{
            printTree(null);
        }



    }


    /**
     * 打印语法树 input -> lexer -> token -> parser -> tree
     * @param fileName
     */
    private static void printTree(String fileName){
        // 定义输入流
        ANTLRInputStream input = null;

        // 判断文件名是否为空,若不为空,则读取文件内容,若为空,则读取输入流
        if(fileName!=null){
            try{
                input = new ANTLRFileStream(fileName);
            }catch(FileNotFoundException fnfe){
                System.out.println("文件不存在,请检查后重试!");
            }catch(IOException ioe){
                System.out.println("文件读取异常,请检查后重试!");
            }
        }else{
            try{
                input = new ANTLRInputStream(System.in);
            }catch(FileNotFoundException fnfe){
                System.out.println("文件不存在,请检查后重试!");

            }catch(IOException ioe){
                System.out.println("文件读取异常,请检查后重试!");
            }
        }

        // 定义词法规则分析器
        LibExprLexer lexer = new LibExprLexer(input);

        // 生成通用字符流
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        // 语法解析
        LibExprParser parser = new LibExprParser(tokens);

        // 生成语法树
        ParseTree tree = parser.prog();

        // 打印语法树
        // System.out.println(tree.toStringTree(parser));

        // 生命访问器
        LibExprVisitorImpl visitor = new LibExprVisitorImpl();
        visitor.visit(tree);

    }

}

测试脚本,testCase.txt
验证输出效果

1+2
1+2*4
1+2*4-5
1+2*4-5+20/5
(1+2)*4

执行命令,测试效果

# 下载jar包
cd /usr/local/lib
curl -O https://www.antlr.org/download/antlr-4.8-complete.jar
# 添加环境变量
export CLASSPATH=".:/usr/local/lib/antlr-4.8-complete.jar:$CLASSPATH"
# 设置命令别名
alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.8-complete.jar:$CLASSPATH" org.antlr.v4.Tool'

# 生成解析器,指定访问者模式
antlr4 -no-listener -visitor LibExpr.g4

# 编译java文件
javac *.java

# 执行测试
java LibExprPrint testCase.txt

测试结果:

$ antlr_libexpr % java LibExprPrint testCase.txt
1+2=3
1+2*4=9
1+2*4-5=4
1+2*4-5+20/5=8
(1+2)*4=12
发布了44 篇原创文章 · 获赞 12 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/myhes/article/details/105657935