设计模式 - 解释器模式

解释器模式:给定一门语言,定义其词法规则及语法规则(即:文法表示),并提供一个用于解析与执行该语言的解释器。
核心:定义文法表示(词法规则和语法规则),并提供一个处理该文法表示的解释器 --- 从而允许用户端自行构造语句执行不同逻辑
角色:

  • 终结符表达式(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());;
    }
}

编译原理回顾

编译原理(包括编译器和解释器),用于将源语言程序翻译为目标语言程序

  1. 词法分析: 单词分析,对语句中的单词和符号进行分析和归类(输入源程序,输出单词符号)
  2. 语法分析:句子分析,判断语句是否符合文法规则(输入单词符号,输出语法单位)
  3. 语义分析与中间代码产生:语义检查,判断是否存在冲突的语义(如,public类的类名是否与文件名一致,是否存在方法签名相同的两个方法等),最后生成中间代码
  4. 优化
  5. 目标代码生成
posted @ 2020-12-03 00:57  祁奇  阅读(122)  评论(0编辑  收藏  举报