1.定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
文法:
假设有如下ab开头ef结尾中间排列N(N>= 0)个cd的字符串:
| abcd...... cdef
随着N的值具体的字符串也不同,假如定义一个符号S,从符号S出发推导出上述字符串,那么推导式:
| S ::= abA*ef
| A ::= cd
其中符号" ::= " 表示推导;符号" * "表示闭包,意思就是符号A可以有0或N个重复;S和A称为非终结符号,因为它们能推导出式子右边的表达式,同时又因为整个推导式是从S出发的,因此S为初始符号,而abef和cd这些字符不能再被推导我们将之称为终结符号。像这样的从一个具体的符号出发,通过不断地应用一些产生式规则从而生成一个字符串的集合,我们将描述这个集合的文法称为形式文法。
解释器:
可以将解释器简单的理解为一个翻译机,就是用来翻译类似"abef"、"abcdef"和"abcdcdef"之类的字符串句子。
2.使用场景
1)如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象语法树,可以考虑。
2)在某些特定领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。
3.简单实现
对算术表达式"m+n-p"(加减法表达式)进行解释。如果使用解释器模式,那么代表数字的m、n、p三个字母可以看成终结符号,而"+"这个算术符号可以当成非终结符号。
//抽象算术运算解释器 abstract class ArithmeticExp{ abstract int interpret(); } //数字解释器 class NumExp extends ArithmeticExp{ private int num; NumExp(int num){ this.num = num; } @Override int interpret() { return num; } } //抽象运算符号解释器 abstract class OperatorExp extends ArithmeticExp{ //用于存储运算符号两边的数字解释器 protected ArithmeticExp exp1,exp2; public OperatorExp(ArithmeticExp exp1, ArithmeticExp exp2) { this.exp1 = exp1; this.exp2 = exp2; } } //加法运算符解释器 class AddOpratorExp extends OperatorExp{ public AddOpratorExp(ArithmeticExp exp1, ArithmeticExp exp2) { super(exp1, exp2); } @Override int interpret() { return exp1.interpret() + exp2.interpret(); } } //减法运算解释器 class SubtractionExp extends OperatorExp{ public SubtractionExp(ArithmeticExp exp1, ArithmeticExp exp2) { super(exp1, exp2); } @Override int interpret() { return exp1.interpret() - exp2.interpret(); } } //处理业务 class Calculator{ //声明一个Stack栈储存并操作所有相关的解释器 private Stack<ArithmeticExp> mStack = new Stack<ArithmeticExp>(); public Calculator(String exps) { //声明两个临时变量 储存运算符左右两边的数字解释器 ArithmeticExp exp1,exp2; //正则表达式分割字符串 String[] elems = exps.split("\\b"); for (int i=0 ;i< elems.length;i++){ switch (elems[i]){ case "+": //则将栈中的解释器弹出作为运算符号左边的解释器 exp1 = mStack.pop(); //将运算符号数组下标下一个元素构造为一个数字解释器 exp2 = new NumExp(Integer.valueOf(elems[++i])); //通过以上两个数字解释器构造加法运算解释器 mStack.push(new AddOpratorExp(exp1,exp2)); break; case "-": exp1 = mStack.pop(); exp2 = new NumExp(Integer.valueOf(elems[++i])); //构建减法解释器 mStack.push(new SubtractionExp(exp1,exp2)); break; default: //如果是数字则直接构造数字解释器并入栈 mStack.push(new NumExp(Integer.valueOf(elems[i]))); break; } } } //计算结果 public int calculate(){ return mStack.pop().interpret(); } }
public class ExpressionMode { public static void main(String[] args){ String exp = "76-12+9-1+10"; Calculator c = new Calculator(exp); System.out.println(exp + " = " + c.calculate()); } }
输出:
4.小结
优点:
代码灵活,拓展性高
缺点:
对于每一条文法都可以对应至少一个解释器,会生成大量的类,后期维护困难,同时对于过于复杂的文法,构建抽象语法树会显得异常繁琐。