The Definitive Antlr 4 Reference 2nd Edition 第4章第二小节 学习笔记

本文详细介绍了如何使用ANTLR4框架构建一个简单的计算器。从定义算术表达式的文法开始,通过标记规则和引入访问者模式,实现了计算器的基本功能。包括对表达式的解析、赋值表达式的处理、以及利用Java常量引用操作符,最终通过访问者模式完成表达式的计算。
摘要由CSDN通过智能技术生成

 The Definitive Antlr 4 Reference 2nd Edition  第4章第二小节 学习笔记

为了使用4.1节所定义的算数表达式能够计算值,需要编写一些Java代码。Antlr v4鼓励使用者保持文法整洁(clean),同时使用分析树的访问器(visitors)和其它的遍历工具来实现语言应用。本节使用访问者模式(visitor pattern)来实现计算器。代码中将使用Antlr自动生成的visitor接口和空visitor实现来完成计算器。

首先需要对4.1节所定义文法进行修改,这里需要标记各个规则(标记所用标签是不与规则名相冲突的标识符)。如果没有标签标记规则,那么Antlr仅仅为每个规则生成一个visitor方法。而这里需要使用不同的visitor方法,在输入的每个阶段处理不同的事件。标签写在规则右侧,并以 # 符号开始。

因此文法变为:

 

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 ')'             # parents
       ;

 

接下来为操作符常量定义一些符号,以后就可以在visitor中以Java常量的形式引用这些符号了。

MUL : '*' ; 

DIV : '/' ;

ADD : '+' ;

SUB : '-' ;

 

定义好上述文件后,利用Antlr插件,生成Lexer与Parser,由于设置了 –visitor选项,生成的文件中会有LabeledExprBaseVisitor类,新建EvalVisitor类,继承LabeledExprBaseVisitor(继承自所生成的LabeledExprVisitor类,这是默认实现包含了未实现的visitXX空方法),实现各个visitXX方法完成表达式的计算。

LabeledExprLexer lexer = new LabeledExprLexer(input);  
CommonTokenStream tokens = new CommonTokenStream(lexer);  
LabeledExprParser parser = new LabeledExprParser(tokens);  
ParseTree tree = parser.prog();   
EvalVisitor eval = new EvalVisitor();  
eval.visit(tree);  

 

public class EvalVisitor extends LabeledExprBaseVisitor<Integer>{
        // 这个map用于赋值表达式和id表达式。ID '=' expr NEWLINE,ID
        //当遇到id = x 的表达式时, 计算表达式值并存储<id,计算结果>
        //若遇到id查表,寻找其对应的值。
	Map<String, Integer> memory = new HashMap<String, Integer>();

        // 处理ID表达式
	@Override
	public Integer visitId(IdContext ctx) {
              String id = ctx.ID().getText();
              if ( memory.containsKey(id) ) return memory.get(id);	
	          return 0;
        }
        // 处理括号表达式'(' expr ')',通过ctx得到表达式对象,然后通过
        // visit方法处理表达式并反回结果,调用visit后会通过使用者定义的
        // visitor处理(此处为EvalVisitor)
	@Override
	public Integer visitParents(ParentsContext ctx) {
		return visit(ctx.expr());
	}
        // 遇到赋值表达式,同样通过visit处理 = 号右侧的表达式
	@Override
	public Integer visitAssign(AssignContext ctx) {
		// TODO Auto-generated method stub
		String id = ctx.ID().getText();
		int value = visit(ctx.expr());
		memory.put(id, value);
		return value;
	}
        // 通过visit处理表达式
	@Override
	public Integer visitPrintExpr(PrintExprContext ctx) {
		Integer value = visit(ctx.expr());
		System.out.println(value);
		return 0;
	}
        // 遇到终结符,直接返回结果
	@Override
	public Integer visitInt(IntContext ctx) {
		return Integer.valueOf(ctx.INT().getText());
	}
        // 遇到加减表达式,判断符号,并进行运算。
        // 生成的parser中包含了所定义的词法符号,通过public static final
        // 修饰,所以可以直接通过类名来引用。Op定义为public Token op,通过getType获取类型;

	@Override
	public Integer visitAddSub(AddSubContext ctx) {
		int left = visit(ctx.expr(0));
		int right = visit(ctx.expr(1));
		if(ctx.op.getType() == LabeledExprParser.ADD) {
			return left + right;
		}
		return left - right;
	}
  
	@Override
	public Integer visitMulDiv(MulDivContext ctx) {
		int left = visit(ctx.expr(0));
		int right = visit(ctx.expr(1));
		if(ctx.op.getType() == LabeledExprParser.MUL) {
			return left * right;
		}
		return left / right;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值