1.定义
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
2.主要角色
- 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
3.场景
假如需要计算一个字符串表达式:2+3*7/2-(2+6),我们可以根据优先级快速地计算出来,但是计算机是习惯以某个顺序读取数据的,所以这种结构不是特别适合它。为了更符合计算机的"口味"需要先将上面的表达式转换为后缀表达式(后缀表达式不是本篇重点,请自行百度)。
3.1 抽象表达式
public abstract class AbstractExpression {
public abstract double interpret();
}
3.2 定义加减乘除的非终结表达式
public class PlusExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
PlusExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public double interpret() {
return left.interpret() + right.interpret();
}
}
public class MinusExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
MinusExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public double interpret() {
return left.interpret() - right.interpret();
}
}
public class MultiplyExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
MultiplyExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public double interpret() {
return left.interpret() * right.interpret();
}
}
public class DivideExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
DivideExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public double interpret() {
return left.interpret() / right.interpret();
}
}
3.3 定义数值的终结表达式
public class NumExpression extends AbstractExpression {
private String val;
NumExpression(String val) {
this.val = val;
}
@Override
public double interpret() {
return Double.valueOf(val);
}
}
3.4 解析字符串为表达式对象
public enum OperatorsEnum {
PLUS(0, '+'), MINUS(0, '-'), MULTIPLY(1, '*'), DIVIDE(1, '/'), MODULAR(1, '%'), LEFT_BRACKET(2,
'('), RIGHT_BRACKET(2, ')');
public Integer prior;// 优先级
public Character operator;// 操作符
private OperatorsEnum(int prior, char operator) {
this.prior = prior;
this.operator = operator;
}
public String toString() {
return String.valueOf(operator);
}
}
public class ExpressionInterpreter {
public double result(String src) {
return getExpression(src).interpret();
}
private AbstractExpression getExpression(String src) {
String postFixExpression = toPostFix(src);
char[] chars = postFixExpression.toCharArray();
if (isNotDigit(chars[0]) || isNotDigit(chars[1])) {
// 前两位一定是数字
throw new RuntimeException("后缀表达式解析有误");
}
LinkedList<AbstractExpression> stack = new LinkedList<>();
// 遍历
for (char c : chars) {
if (Character.isDigit(c)) {
stack.push(new NumExpression(c + ""));
} else {
AbstractExpression[] abstractExpressions = getLeftAndRight(stack);
switch (operator(c)) {
case PLUS:
stack.push(new PlusExpression(abstractExpressions[0], abstractExpressions[1]));
break;
case DIVIDE:
stack.push(new DivideExpression(abstractExpressions[0], abstractExpressions[1]));
break;
case MINUS:
stack.push(new MinusExpression(abstractExpressions[0], abstractExpressions[1]));
break;
case MULTIPLY:
stack.push(new MultiplyExpression(abstractExpressions[0], abstractExpressions[1]));
break;
default:
throw new UnsupportedOperationException(c+"");
}
}
}
if (stack.size() > 1) {
throw new RuntimeException("解析后缀表达式失败");
}
return stack.pop();
}
private AbstractExpression[] getLeftAndRight(LinkedList<AbstractExpression> stack) {
AbstractExpression right = stack.pop();
AbstractExpression left = stack.pop();
return new AbstractExpression[]{left, right};
}
private boolean isNotDigit(char c) {
return !Character.isDigit(c);
}
/**
* @param infix
* @return
* @Title: toPostFix
* @Description: 将中缀表达式转换为后缀表达式
* @return: String
*/
public String toPostFix(String infix) {
// 算式字符数组
char[] ch = infix.trim().toCharArray();
LinkedList<OperatorsEnum> stack = new LinkedList<OperatorsEnum>();
StringBuilder sb = new StringBuilder();
OperatorsEnum op = null;
for (int i = 0; i < ch.length; i++) {
// 对每个算式字符,检查它是不是操作符
if ((op = operator(ch[i])) == null) {
sb.append(ch[i]);
} else {
// 右括号
// 持续弹出栈顶元素直到遇到左括号,但是不输出左括号
if (op.equals(OperatorsEnum.RIGHT_BRACKET)) {
// 如果不是左括号,持续弹出并输出
while (!stack.peek().equals(OperatorsEnum.LEFT_BRACKET)) {
sb.append(stack.pop());
}
// 此时栈顶元素为左括号,直接弹出,不输出
stack.pop();
} else {
// 非右括号
// 1、弹出并输出所有高优先级或者同等优先级,直到遇到低优先级或者左括号为止
// 上面的弹出语句有可能将栈弹空,检查stack的size避免NPE
while (stack.size() > 0 && stack.peek().prior >= op.prior
&& !stack.peek().equals(OperatorsEnum.LEFT_BRACKET)) {
sb.append(stack.pop());
}
// 2、将当前操作符入栈
stack.push(op);
}
}
}
// 弹出所有栈中剩余操作符
while (stack.size() > 0) {
sb.append(stack.pop());
}
return sb.toString();
}
/**
* @param ch
* @return
* @Title: isOperator
* @Description: 判断字符是否为操作符
* @return: OperatorsEnum
*/
private OperatorsEnum operator(char ch) {
for (OperatorsEnum op : OperatorsEnum.values()) {
if (ch == op.operator) {
return op;
}
}
return null;
}
}
toPostFix方法是将中缀表达式转换为后缀表达式(来源自后缀表达式的计算器Java实现),getExpression方法是将后缀表达式变成表达式对象,通过该对象就可以获取最终的结果
3.5 测试
public class TestInterpreter {
public static void main(String[] args) {
ExpressionInterpreter expressionInterpreter = new ExpressionInterpreter();
double result = expressionInterpreter.result("2+3*7/2-(2+6)");
System.out.println(result);
}
}
----------------------------console
4.5