解释器模式:给定一门语言,定义其词法规则及语法规则(即:文法表示),并提供一个用于解析与执行该语言的解释器。
核心:定义文法表示(词法规则和语法规则),并提供一个处理该文法表示的解释器 --- 从而允许用户端自行构造语句执行不同逻辑
角色:
- 终结符表达式(value):操作数
- 非终结符表达式(symbol):操作符
- 解释器(Calculator): 对用户传入的语句进行正确性校验,并进行词法解析和语法解析得到语法树,最后执行得出语句执行结果
- 环境上下文(Context):容纳用户输入的语句,或容纳用户输入语句解析后的语法树
PS: 解释器模式定义了一门语言并提供执行该语言的解释器,相当于一个简化版的编译原理实现
Calculate(四则运算带括号计算器实现)
// 抽象表达式,定义所有表达式均有interpret方法,操作数不定长
public interface IExpression {
int interpret(IExpression... params);
}
// 终结符表达式 - 值类型表达式,只返回值。
public class VarExpression implements IExpression {
private int value;
public VarExpression(int value) {
this.value = value;
}
@Override
public int interpret(IExpression... params) {
return value;
}
}
// 非终结符表达式 - 运算符表达式抽象父类,定义操作符的优先级
public abstract class SymbolExpression implements IExpression {
protected int level;
public SymbolExpression(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
}
// 非终结符表达式 - 加号运算表达式
public class AddExpression extends SymbolExpression {
public AddExpression(int baseLevel) {
super(baseLevel);
}
// 传入两个操作数,完成相加操作。
@Override
public int interpret(IExpression... params) {
return params[1].interpret() + params[0].interpret();
}
}
// 解释器,完成用户输入语句的解析与执行
public class Calculate {
// 使用栈类型容器存储 值 和 操作符
private Stack<SymbolExpression> symbolStack = new Stack<>();
private Stack<VarExpression> varExpress = new Stack<>();
// 基本优先级,当存在()时,()内操作符的优先级需大于()外操作符的优先级
private int baseLever = 0;
public Calculate(String expression){
this.parse(expression);
}
private void parse(String expression) {
expression = expression.replaceAll(" ",""); // 去空格
char[] express = expression.toCharArray();
int size = express.length;
StringBuilder temp = new StringBuilder(); // 缓存数值
for(int i = 0;i < size;i++){
// 当'('出现时,其之后的非终结符号等级提高,
// 当')'出现时,其之后的非终结符号等级降低。从而实现(expression)的功能。
if(express[i] == '('){
baseLever = baseLever + 10;
} else if(express[i] == ')'){
baseLever = baseLever - 10;
} else if(OperatorUtil.isOperator(express[i])){ // 如果是操作符,则将temp中的字符转换为数值存入var栈,同时清空temp
// 将之前的数值推入var,并清空var中缓存
varExpress.push(new VarExpression(Integer.parseInt(temp.toString())));
temp = new StringBuilder();
// 获取到当前表达式符号
SymbolExpression symbolExpress = OperatorUtil.getSymbolExpression(express[i],baseLever);
// 标记A:当前表达式符号等级不大于栈顶符号,则执行symbol栈顶操作符运算,直到栈顶元素比当前表达式符号优先级小
while (!symbolStack.isEmpty() && symbolExpress.getLevel() <= (symbolStack.peek()).getLevel()){
SymbolExpression expressA = symbolStack.pop();
varExpress.push(new VarExpression(expressA.interpret(varExpress.pop(),varExpress.pop())));
}
// 该操作符入栈
symbolStack.push(symbolExpress);
}else {
// 如果是数字,则加入数字缓存中,知道碰到第一个非数字位时,转换为值存入var
temp.append(express[i]);
}
}
// 将最后一个数值存入var
varExpress.push(new VarExpression(Integer.parseInt(temp.toString())));
// 所有表达式均已解析完成,将栈中符号依次计算(此时,经过标记A的代码,symbol栈中符号等级由栈顶到栈尾递减)
while (!symbolStack.isEmpty()){
SymbolExpression expressA = symbolStack.pop();
varExpress.push(new VarExpression(expressA.interpret(varExpress.pop(),varExpress.pop())));
}
}
// 返回结果
public int value(IExpression... params) {
return varExpress.pop().interpret();
}
}
Spring中集成的SpelExpressionParser
public class Test {
public static void main(String[] args) {
String express = "1+((1+2)*(3-1)+3)/2 + 8";
// 自行实现的计算表达式
System.out.println(new Calculate(express).getValue());
// Spring封装的表达式
System.out.println(new SpelExpressionParser().parseExpression(express).getValue());;
}
}
编译原理回顾
编译原理(包括编译器和解释器),用于将源语言程序翻译为目标语言程序
- 词法分析: 单词分析,对语句中的单词和符号进行分析和归类(输入源程序,输出单词符号)
- 语法分析:句子分析,判断语句是否符合文法规则(输入单词符号,输出语法单位)
- 语义分析与中间代码产生:语义检查,判断是否存在冲突的语义(如,public类的类名是否与文件名一致,是否存在方法签名相同的两个方法等),最后生成中间代码
- 优化
- 目标代码生成