emmm一个比较复杂的模式,一般用来做表达式的解析,估计做DSL的时候都会用到?
假如现在有个算数表达式要解析出结果:a - ( ( 4 - 5 + 6 ) - b )
里面有常量也有变量
Context
存放变量的值,a=3,b=10
Expression接口
表达式的基本元素,例如:a,4,5,-,+,具体还要其他的类继承此接口实现
包含 interpret(Context context)
方法,此方法给出基本元素计算出的结果
TerminalExpression 继承
Expression
最基本的元素(所以叫Terminal),不依赖其他Expression即可算出结果,例如4,5,6
常量,可直接返回自身对应的Integer值,如果是表达式a,b
,从context里获取即可
NonterminalExpression 继承
Expression
可以理解为稍微复杂一些的元素例如-,+
,因为它的结果需要依赖TerminalExpression才能得到,这很容易理解,运算符的两边肯定是数值,你要知道两边的数值把它们做+
操作或者-
操作才能得到结果嘛
那现在上代码了,结合代码更好理解:
Context
package com.hugeo.interpreter;
import java.util.HashMap;
// 为了方便起见,直接继承了HashMap
public class Context extends HashMap<String, Expression> {
public int getValue(String key) {
if (this.containsKey(key)) {
return this.get(key).interpret(this);
}
return 0;
}
}
Expression 接口
package com.hugeo.interpreter;
public abstract class Expression {
// 除了interpret方法之外的东西都是给nonTerminalExpression使用的
protected Expression left;
protected Expression right;
// 赋值右子表达式并返回自身
public Expression right(Expression expression){
this.right = expression;
return this;
}
public abstract int interpret(Context context);
}
两个TerminalExpression
package com.hugeo.interpreter.terminal;
import com.hugeo.interpreter.Context;
import com.hugeo.interpreter.Expression;
public class TConstExpr extends Expression {
private int value;
public TConstExpr(int value){
this.value = value;
}
@Override
public int interpret(Context context) {
// 常量直接返回本身的值
return value;
}
}
package com.hugeo.interpreter.terminal;
import com.hugeo.interpreter.Context;
import com.hugeo.interpreter.Expression;
public class TVariableExpr extends Expression {
private String key;
public TVariableExpr(String key) {
this.key = key;
}
@Override
public int interpret(Context context) {
// 变量从Context中取值
return context.getValue(key);
}
}
两个NonterminalExpression
package com.hugeo.interpreter.nonterminal;
import com.hugeo.interpreter.Context;
import com.hugeo.interpreter.Expression;
public class NTMinusExpr extends Expression {
public NTMinusExpr(Expression l){
this.left = l;
}
@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
}
package com.hugeo.interpreter.nonterminal;
import com.hugeo.interpreter.Context;
import com.hugeo.interpreter.Expression;
public class NTPlusExpr extends Expression {
public NTPlusExpr(Expression l) {
this.left = l;
}
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
}
启动类(解析器)
package com.hugeo.interpreter;
import com.hugeo.interpreter.nonterminal.NTMinusExpr;
import com.hugeo.interpreter.nonterminal.NTPlusExpr;
import com.hugeo.interpreter.terminal.TConstExpr;
import com.hugeo.interpreter.terminal.TVariableExpr;
import java.util.Stack;
/**
* @description: 此类不是一个单纯的main方法了,应该作为一个解析器单独封装起来
* @author: hugeo
* @create: 2018-09-05 15:02
**/
public class Executor {
public static void main(String[] args) {
String expr = "a - ( ( 4 - 5 + 6 ) - b )";
// 分割表达式
String[] parts = expr.split(" ");
// 表达式栈
Stack<Expression> expressions = new Stack<>();
// 存放所有左括号,用于碰到右括号时找对应关系
Stack<Integer> leftBrackets = new Stack<>();
for (int i = 0; i < parts.length; i++) {
switch (parts[i]) {
case "+":
// 碰到运算符,把它前面的表达式弹出来,作为此+运算符的左表达式
expressions.push(new NTPlusExpr(expressions.pop()));
break;
case "-":
// 同上
expressions.push(new NTMinusExpr(expressions.pop()));
break;
case "(":
// 存左括号
leftBrackets.push(i);
break;
case ")": {
// 弹出对应的左括号的位置
int match = leftBrackets.pop();
// 如果这一对括号的前面没有别的括号了,或者没有紧靠在前面的左括号,并且此括号前面有别的表达式
// 那说明此括号括住的表达式和前面的表达式应该是连着的,而且前面的表达式的右孩子应该是空着的正在等此括号运算完毕
// 要做的就是把两个表达式弹出合并成一个
if ((leftBrackets.empty() || match > leftBrackets.peek() + 1) && expressions.size() > 1) {
Expression p1 = expressions.pop();
Expression p2 = expressions.pop();
expressions.push(p2.right(p1));
}
break;
}
default: {
// 碰到数值要看是不是第一个字符,或者它的前面刚好是个左括号,如果满足,说明应该新起一个Terminal表达式
// 否则应该将它作为前一个Nonterminal表达式的右孩子
// 具体还需要判断是变量还是常量
if (i == 0 || (!leftBrackets.empty() && leftBrackets.peek() == i - 1)) {
if (parts[i].equals("a") || parts[i].equals("b")) {
expressions.push(new TVariableExpr(parts[i]));
} else {
expressions.push(new TConstExpr(Integer.valueOf(parts[i])));
}
} else {
if (parts[i].equals("a") || parts[i].equals("b")) {
expressions.push(expressions.pop().right(new TVariableExpr(parts[i])));
} else {
expressions.push(expressions.pop().right(new TConstExpr(Integer.valueOf(parts[i]))));
}
}
}
}
}
// Context构造
Context context = new Context();
context.put("a", new TConstExpr(3));
context.put("b", new TConstExpr(30));
// 经过一系列压栈出栈之后,栈里面只会剩一个Expression,将Context传进去就可以算出结果了
int result = expressions.pop().interpret(context);
// 打印结果:28
System.out.println(result);
}
}