解释器模式

1,700 阅读2分钟

应用场景

这个在编译器的语义分析过程中常用的一种模式,即分析虚拟语法树(AST),但它并不能解决如何生成语法树的问题。

在实际的工作中,有时也会遇到一些类似的工作,比如我们会使用DSL来解决一些复杂的业务问题,这时候就需要我们来定义一些简单的语法来实现。再比如

比如前段时间需要开发一个maven的插件,需要对一些依赖的版本大小限制(比如guava>18.0),以及一些依赖之间互斥(A|B),这些规则可以自定义(以方便扩展);所以我开发的插件的工作是解析配置文件,然后判断项目的依赖是否满足规则,如果不满足则退出编译。

模式图(UML)

  • Context:可以理解为变量值,用来替换表达式中的变量;比如a+b;则context应该包含的内容为a=10,b=2

  • AbstractExpression:也可以定义为interface,包含一个接口用来返回表达式的值

  • TerminalExpression:可以理解为变量a, b

  • NonterminalExpression:可以理解为操作符:+,-*,/等;由于操作符至少要包含一个操作数,所以它又包含了多个AbstractExpresion

  • Client 依赖于Context 和 Expression,即实现对语法树进行计算

示例

context

@AllArgsConstructor 
public class Context { 
    private Map<String, Expression> variables; 
 
    public int getValue(String key) { 
        if (variables.containsKey(key)) { 
            return variables.get(key).interpreter(this); 
        } 
        return 0; 
    } 
}

expression

public interface Expression { 
    public int interpreter(Context context); 
}

TerminalExpression

@AllArgsConstructor 
public class Number implements Expression { 
    private int number; 
 
    public int interpreter(Context context) { 
        return number; 
    } 
}
/** 
 * TerminalExpression 
 */ 
@AllArgsConstructor 
public class VariableExpression implements Expression { 
    private String variable; 
 
    public int interpreter(Context context) { 
        return context.getValue(variable); 
    } 
} 

NonterminalExpression

@AllArgsConstructor 
public class MinusExpression implements Expression { 
    // 左操作数 
    private Expression left; 
    // 右操作数 
    private Expression right; 
 
 
    public int interpreter(Context context) { 
        return left.interpreter(context) - right.interpreter(context); 
    } 
}
@AllArgsConstructor 
public class MinusExpression implements Expression { 
    // 左操作数 
    private Expression left; 
    // 右操作数 
    private Expression right; 
 
 
    public int interpreter(Context context) { 
        return left.interpreter(context) - right.interpreter(context); 
    } 
}

client

/** 
 *  
 * 这个严格来说并不能算是解释器的一部分,因为它还完成了token解析, 可以当成一个client来操作 
 */ 
@AllArgsConstructor 
public class Evaluator implements Expression { 
    private Expression syntaxTree; 
 
    public Evaluator(String expression) { 
        Stack<Expression> expressionStack = new Stack<Expression>(); 
        for (String token : expression.split(" ")) { 
            if (token.equals("+")) { 
                Expression subExpression = new PlusExpression(expressionStack.pop(), expressionStack.pop()); 
                expressionStack.push(subExpression); 
            } else if (token.equals("-")) { 
                // it's necessary remove first the right operand from the stack 
                Expression right = expressionStack.pop(); 
                // ..and after the left one 
                Expression left = expressionStack.pop(); 
                Expression subExpression = new MinusExpression(left, right); 
                expressionStack.push(subExpression); 
            } else 
                expressionStack.push(new VariableExpression(token)); 
        } 
        syntaxTree = expressionStack.pop(); 
    } 
 
    public int interpreter(Context context) { 
        return syntaxTree.interpreter(context); 
    } 

    public static void main(String[] args) { 
        String expression = "w x z - +"; 
        Evaluator sentence = new Evaluator(expression); 
        Map<String,Expression> variables = new HashMap<String,Expression>(); 
        variables.put("w", new Number(5)); 
        variables.put("x", new Number(10)); 
        variables.put("z", new Number(42)); 
 
        int result = sentence.interpreter(new Context(variables)); 
        System.out.println(result); 
    }
 
}

:网上的很多理解都是错误的,而且有很多将其翻译成“解析模式”,更是大错特错

参考

https://en.wikipedia.org/wiki/Interpreter_pattern