6.3解释器模式(5.3)

假设系统的一个文本框中,允许用户输入字符串表达式如"5除 2 乘3模4乘6",要求系统能够按照Java的整数乘除运算规则,计算出表达式结果,如12。

假设系统的一个文本框中,允许用户输入字符串表达式如“list 姓名 年龄 学号 sortby 学号”, 要求系统能够从数据库中提取学生信息并按照学号排序。

当应用程序中频繁使用某种文本形式的句子(不管是用户输入的还是从文件中读取的)时,我们需要为定义字符串表达式的语言设计一个解释器(interpreter)

【在实际的应用程序开发中,编写一个解释器的机会较少,而且编写复杂的解释器需要学习《编译原理》。因而yqj2065认为解释器模式(Interpreter Pattern) 是所有设计模式中最难学习的。但是,如果你大致了解了《编译原理》方面的知识,则学习解释器模式的难度系数,从7颗星直接降到4星。所以,下一节的内容难度也就4星。】

6.3.1 乘法解释器

 

本节以一种极其简化的表达式语言——乘法/Mul语言为例,介绍该语言解释器的设计。

Mul语言,描述“字符串”形式书写的表示乘除法的各种句子,如"5.0 / 2 * 4 % 3 * 6"(简化起见,不考虑优先级和括号的使用),要求该语句(或表达式)能够按照Java的double数据的乘除运算规则,计算出结果。

1. 语法元素

作为一门语言,Mul语言有一套规则,或称为文法/语法/Grammars。回顾《编译原理》介绍的知识,通常,文法由四个元素构成,表示为一个四元组:G = {ST,SN,S,P}。

①终结符(terminal)的集合ST(set of terminal)。终结符是语言的最小文法元素(token/语言符号)。Mul语言的终结符只考虑数字和3个操作符,暂时不考虑括号和空格。ST的集合表示为:{ Digit,*, /,%}。

②非终结符(语法变量)的集合SN(set of nonterminal)。非终结符是由终结符构成的表达式片段,Java的非终结符包括表达式、语句(if块)、函数、类等大量的语法概念;而Mul语言的非终结符仅仅只有表达式。

③产生式集合P。产生式定义什么是合法的非终结符,产生式格式为“α→β”,意为α定义为β。Expr类层次为每一种产生式规则定义一个类。

Exp→Digit| Digit op Digit | Exp op Exp

其中,op={* | / | %}。

④开始符号S。S是每个合法句子的最开始处使用的非终结符,为Digit。

上述四元组,可以用表达式的递归定义描述。Mul语言的表达式:

  • 数字是一个表达式;
  • 中缀操作符连接两个表达式是一个表达式;

因此在设计解释器的实现时,按照表达式的递归定义,首先设计接口Expr,它代表/封装表达式,它需要计算出表达式的结果,封装了一个eval()方法,对本表达式(Expr实例)进行解释(计算) 并返回计算的结果。

其次为3种操作的中缀表示,设计一个父类型Op,其子类Mul (Expr left,Expr right)描述乘法操作符*组成的表达式。Op可以不作为Expr的子类型,那么Mul需要多继承。

Digit封装字符串形式的数字,Digit对象必须是Double.parseDouble(value)能够解析的value。

例程 6 8封装文法
package chap6.interpreter.MulLang;
public interface Expr {//Expression
    public double eval();
}
abstract class Op implements Expr { // Op可以不作为Expr的子类型
    protected final Expr left, right;//
    public Op(Expr left, Expr right) {
        this.left = left;
        this.right = right;
    }
}
class Mul extends Op{
	public Mul(Expr left,Expr right){super(left,right);	}	
	@Override public double eval()	{return left.eval() * right.eval();}
}// Div 、 Mod略
class Digit implements Expr {
    private final double value;
    public Digit(String value) {
        this.value = Double.parseDouble(value);
    }
    @Override public double eval() {  return this.value;  }
}

 

Expr类层次描述了Mul语言的规则。Expr类层次为每一种产生式规则定义一个类。即

Exp→Digit| Digit op Digit | Exp op Exp

其中,Op ={Mul | Div | Mod}。

2. 抽象语法树

合法的表达式如"5.0 / 2 * 4 % 3 * 6",可以手工创建其Expr对象。这样纯手工的玩意,看上去蛮有趣和亲切的——因为傻傻的。

Expr expr = new Mul(
                new Mod(
                    new Mul(
                        new Div(new Digit("5.0"), new Digit("2") ),
                        new Digit("4")),
                    new Digit("3")),
                new Digit("6"));

expr.eval()的值为6.0。Expr对象可以用树形结构来描述,称为抽象语法树(Abstract Syntax Tree, AST)或简称语法树。手工创建Expr对象极其繁琐,通常使用栈构造语法树。(单纯看Expr与Digit和Op的类图,和组合模式、装饰模式完全一样的结构。区别和内在联系后面讨论。)

例程 7 4构造语法树
   public static Expr build(String statement){
        statement = statement.replace(" ", "");
        statement = statement.replace("*", " * ").replace("/", " / ").replace("%", " % ");
        String[] tokens=statement.split("\\s+");//"\\s+"     
        Expr left=null,right=null;
        Expr expr=null;
        Stack<Expr> stack=new Stack<>();    	
        for(int i=0;i<tokens.length;i++){	
            if(tokens[i].equals("*")){
                left= stack.pop(); 
                right=new Digit(tokens[++i]); 
                stack.push(new Mul(left,right));
            } else if(tokens[i].equals("/")){
                left= stack.pop();
                right=new Digit(tokens[++i]); 
                stack.push(new Div(left,right));    			
            }else if(tokens[i].equals("%")){
                left= stack.pop();
                right=new Digit(tokens[++i]); 
                stack.push(new Mod(left,right));   			
            }else {
                stack.push(new Digit(tokens[i]));
            }
        }
        return expr=(Expr)stack.pop();
    }

public class Client{
	public static void main(String args[])	{	    	
		String statement = "6 / 2 * 3 % 4 * 6";	
		statement = "62 / 3";	
		Calculator calculator = new Calculator();
		calculator.build(statement);		
		int result = calculator.compute();		
		System.out.println(statement + " = " + result);	
	}
	public static void test()	{
	    Expr e = new DivOp(new Digit(62),new Digit(3));
	    int result = e.interpret();
		System.out.println(" 62 / 3 = " + result);	
	}
}

 

[设计模式]中的解释器模式并未解释如何创建一个抽象的语法树。

方法build(String statement)将参数statement解析并构造一个Expr实例。


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值