解释器模式
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。
这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
介绍
意图: 给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
主要解决: 对于一些固定文法构建一个解释句子的解释器。
何时使用: 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
如何解决: 构建语法树,定义终结符与非终结符。
关键代码: 构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。
应用实例: 编译器、运算表达式计算。
优点:
- 可扩展性比较好,灵活。
- 增加了新的解释表达式的方式。
- 易于实现简单文法。
缺点:
- 可利用场景比较少。
- 对于复杂的文法比较难维护。
- 解释器模式会引起类膨胀。
- 解释器模式采用递归调用方法。
使用场景:
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语言来进行表达。
- 一个简单语法需要解释的场景。
注意事项:
可利用场景比较少,JAVA 中如果碰到可以用Drools或aviator代替。
代码实现
这里实现的解释器
- 既可以直接计算
(1+2)*(3+4)
这样的表达式; - 也可以计算
(a+b)*(c+d)
然后通过执行上下文对a
、b
、c
、d
进行赋值,例如:a=3
,b=5
,c=9
,d=2
,然后计算; - 还可以执行
(a&b)|(c&d)
,然后通过执行上下文对a
、b
、c
、d
进行赋值执行的规则和规则匹配变量值。
例如:对a
赋值的规则x > 5?true:false
这样的规则,同时对a
的规则x
的变量赋予6
,也可以赋予4
。这样a
在表达式中(a&b)|(c&d)
中由于不同的变量,可能表现的值不一样。其他同样。最后求值。
算数表达式接口
public interface MathExpr {
int exec(ExprContext exprContext);
}
实现加、减、乘、除、乘方
/**
* 加法表达式
*/
public class AddExpr implements MathExpr {
private MathExpr left;
private MathExpr right;
public AddExpr(MathExpr left, MathExpr right)
{
this.left = left;
this.right = right;
}
public int exec(ExprContext exprContext)
{
return left.exec(exprContext) + right.exec(exprContext);
}
}
/**
* 除法表达式
*/
public class DivExpr implements MathExpr{
private MathExpr left;
private MathExpr right;
public DivExpr(MathExpr left, MathExpr right)
{
this.left = left;
this.right = right;
}
public int exec(ExprContext exprContext)
{
return left.exec(exprContext) / right.exec(exprContext);
}
}
/**
* 乘法表达式
*/
public class MulExpr implements MathExpr{
private MathExpr left;
private MathExpr right;
public MulExpr(MathExpr left, MathExpr right)
{
this.left = left;
this.right = right;
}
public int exec(ExprContext exprContext)
{
return left.exec(exprContext) * right.exec(exprContext);
}
}
/**
* 减法表达式
*/
public class SubExpr implements MathExpr{
private MathExpr left;
private MathExpr right;
public SubExpr(MathExpr left, MathExpr right)
{
this.left = left;
this.right = right;
}
public int exec(ExprContext exprContext)
{
return left.exec(exprContext) - right.exec(exprContext);
}
}
/**
* 乘方表达式
*/
public class PowerExpr implements MathExpr{
private MathExpr left;
private MathExpr right;
public PowerExpr(MathExpr left, MathExpr right)
{
this.left = left;
this.right = right;
}
public int exec(ExprContext exprContext)
{
return (int)Math.pow(left.exec(exprContext),right.exec(exprContext));
}
}
//算数表达式原子,如 a+b 中a和b
public class ValExpr implements MathExpr {
private final String val;
public ValExpr(String val)
{
this.val = val;
}
@Override
public int exec(ExprContext exprContext) {
Integer content = exprContext.getVal(val);
if (content == null){
throw new IllegalArgumentException("不支持的规则类型");
}
return content;
}
}
定义逻辑表达式接口
public interface LogicExpr {
boolean exec(ExprContext exprContext);
}
定义与、或、非逻辑运算
//逻辑与
public class AndExpr implements LogicExpr {
private final LogicExpr left,right;
public AndExpr(LogicExpr left, LogicExpr right) {
this.left = left;
this.right = right;
}
@Override
public boolean exec(ExprContext exprContext) {
return left.exec(exprContext) && right.exec(exprContext);
}
}
//逻辑非
public class NotExpr implements LogicExpr {
private final LogicExpr expr;
public NotExpr(LogicExpr expr)
{
this.expr = expr;
}
@Override
public boolean exec(ExprContext exprContext) {
return !expr.exec(exprContext);
}
}
//逻辑或
public class OrExpr implements LogicExpr {
private final LogicExpr left,right;
public OrExpr(LogicExpr left, LogicExpr right) {
this.left = left;
this.right = right;
}
@Override
public boolean exec(ExprContext exprContext) {
return left.exec(exprContext) || right.exec(exprContext);
}
}
//逻辑表达式原子,如 a & b 中a和b
public class RuleExpr implements LogicExpr {
private final String val;
public RuleExpr(String val)
{
this.val = val;
}
@Override
public boolean exec(ExprContext exprContext) {
Rule rule = exprContext.getRule(val);
if (rule instanceof NumbleRule){
Integer content = exprContext.getVal(val);
return rule.match(content);
}
if (rule instanceof StringRule){
String content = exprContext.getVal(val);
return rule.match(content);
}
throw new IllegalArgumentException("不支持的规则类型");
}
}
定义规则接口
public interface Rule<T> {
boolean match(T content);
}
定义字符串规则:支持正则、包含、以…开头、以…接口
public abstract class StringRule implements Rule<String>{
protected final String regex;
protected StringRule(String regex){
this.regex = regex;
}
@Override
public abstract boolean match(String content);
public static StringRule regex(String regex){
return new RegexRule(regex);
}
public static StringRule contains(String contains){
return new ContainsRule(contains);
}
public static StringRule startWith(String startWith){
return new StartWithRule(startWith);
}
public static StringRule endWith(String endWith){
return new EndWithRule(endWith);
}
private static class EndWithRule extends StringRule{
public EndWithRule(String endWith)
{
super(endWith);
}
public boolean match(String content)
{
return content.endsWith(this.regex);
}
}
private static class StartWithRule extends StringRule{
public StartWithRule(String startWith)
{
super(startWith);
}
public boolean match(String content)
{
return content.startsWith(this.regex);
}
}
private static class ContainsRule extends StringRule{
public ContainsRule(String contains)
{
super(contains);
}
public boolean match(String content)
{
return content.contains(this.regex);
}
}
private static class RegexRule extends StringRule{
private final Pattern pattern;
public RegexRule(String regex)
{
super(regex);
this.pattern = Pattern.compile(this.regex);
}
public boolean match(String content)
{
return pattern.matcher(content).matches();
}
}
}
定义数据规则:>、<、>=、<=、=、between
public abstract class NumbleRule implements Rule<Integer> {
protected int low;
protected int high;
private NumbleRule(){}
protected NumbleRule(int low, int high) {
this.low = low;
this.high = high;
}
public static NumbleRule lt(int numble,boolean eq)
{
return new LT(numble,eq);
}
public static NumbleRule gt(int numble,boolean eq)
{
return new GT(numble,eq);
}
public static NumbleRule eq(int numble)
{
return new EQ(numble);
}
public static NumbleRule between(int low,int high)
{
return new Between(low,high);
}
public static NumbleRule between(int low,int high,boolean include_low,boolean include_high)
{
return new Between(low,high,include_low,include_high);
}
@Override
public abstract boolean match(Integer content);
private static class LT extends NumbleRule{
private final boolean eq;
public LT(int numble,boolean eq)
{
super(numble,numble);
this.eq = eq;
}
@Override
public boolean match(Integer content) {
if (eq) {
return content <= low;
}else {
return content < low;
}
}
}
private static class GT extends NumbleRule{
private final boolean eq;
public GT(int numble,boolean eq)
{
super(numble,numble);
this.eq = eq;
}
@Override
public boolean match(Integer content) {
if (eq) {
return content >= low;
}else {
return content > low;
}
}
}
private static class EQ extends NumbleRule{
public EQ(int numble)
{
super(numble,numble);
}
@Override
public boolean match(Integer content) {
return content == super.low;
}
}
private static class Between extends NumbleRule{
private final boolean include_low;
private final boolean include_high;
public Between(int low,int high,boolean include_low,boolean include_high)
{
super(low,high);
this.include_low = include_low;
this.include_high = include_high;
}
public Between(int low,int high)
{
this(low,high,true,true);
}
@Override
public boolean match(Integer content) {
if (include_low){
return content >= low && (include_high?content <= high:content < high);
}else {
return content > low && (include_high?content <= high:content < high);
}
}
}
}
定义执行上下文,负责传递变量、规则
public class ExprContext {
private final Map<String,Object> map;
public ExprContext()
{
this.map = new HashMap<String,Object>();
}
public <T> void putVal(String key,T value){
this.map.put(makeValKey(key),value);
}
private String makeValKey(String key){
return "val_"+key;
}
private String makeRuleKey(String key){
return "rule_"+key;
}
public void putRule(String key, Rule rule){
this.map.put(makeRuleKey(key),rule);
}
public <T> T getVal(String key){
return (T)this.map.get(makeValKey(key));
}
public Rule getRule(String key){
return (Rule) this.map.get(makeRuleKey(key));
}
}
通过后缀表达式实现的算数表达式、逻辑表达式解释器。
public class Interpreter {
private static final char[] opChars= {'+','-','*','/','^'
,'&','|','!','(',')'};
private final String input;
public Interpreter(String input){
this.input = input;
}
public LogicExpr parseLogicExpr() {
String expr = input;
List<Object> exprStack = new ArrayList<>();
Stack<Character> opStack = new Stack<>();
int i = 0;
//按照后缀表达式解析
while (i<expr.length()) {
char c = expr.charAt(i);
if (isValChar(c)){
String val = valToken(expr,i);
exprStack.add(new RuleExpr(val));
i = i+ val.length();
continue;
}
if (isOp(c)){
if (c == '('){
opStack.push(c);
}else if (c == ')'){
while (!opStack.isEmpty()){
char op = opStack.pop();
if (op == '('){
break;
}
exprStack.add(op);
}
}else {
//'&','|','!'
while (!opStack.isEmpty()){
char op = opStack.peek();
if (op == '(' || priority(op) < priority(c)){
break;
}else {
exprStack.add(opStack.pop());
}
}
opStack.push(c);
}
}
i++;
}
while (!opStack.isEmpty()){
exprStack.add(opStack.pop());
}
Stack<LogicExpr> ruleExprStack = new Stack<>();
for (int j = 0; j < exprStack.size(); j++) {
Object o = exprStack.get(j);
if (o instanceof Character){
char c = (char)o;
if (c == '!'){
LogicExpr ruleExpr = ruleExprStack.pop();
ruleExprStack.push(new NotExpr(ruleExpr));
continue;
}
LogicExpr ruleExpr1 = ruleExprStack.pop();
LogicExpr ruleExpr2 = ruleExprStack.pop();
if (c == '&'){
ruleExprStack.push(new AndExpr(ruleExpr2,ruleExpr1));
}else if (c == '|'){
ruleExprStack.push(new OrExpr(ruleExpr2,ruleExpr1));
}
}else {
ruleExprStack.push((LogicExpr) o);
}
}
return ruleExprStack.pop();
}
private static int priority(char op){
switch (op){
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
case '&':
case '|':
return 4;
case '!':
return 5;
default:
throw new IllegalArgumentException("非法字符:"+op);
}
}
private boolean isValChar(char c){
return( c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')|| (c >= 'A' && c <= 'Z');
}
private boolean isOp(char c){
for (char opChar : opChars) {
if (opChar == c) {
return true;
}
}
return false;
}
private boolean isOp(String c){
if (c.length() == 1) {
char ch = c.charAt(0);
return isOp(ch);
}
return false;
}
private String valToken(String expr,int from){
String token = "";
for (int i = from; i < expr.length(); i++) {
char c = expr.charAt(i);
if (isValChar(c)){
token += c;
}else{
break;
}
}
return token;
}
public int eval(){
Stack<Character> opStack = new Stack<>();
List<String> result = new ArrayList<>();
String expr = input;
int i = 0;
while (i<expr.length()){
char c = expr.charAt(i);//从左到右扫描中缀表达式的每个元素
if (isValChar(c)){
String val = valToken(expr,i);
result.add(val);
i += val.length();
continue;
}
if(isOp(c)){
if (c == ')'){
while (!opStack.isEmpty()){
char o = opStack.pop();
if (o == '('){
break;
}
result.add(String.valueOf(o));
}
}else if (c == '('){
opStack.push(c);
}else{
//'+','-','*','/','^','&','|','!'
while (!opStack.isEmpty()){
char o = opStack.peek();
if (o == '('||priority(o) < priority(c)){
break;
}else{
//priority(o) >= priority(c)
result.add(String.valueOf(opStack.pop()));
}
}
opStack.push(c);
}
}
i++;
}
while (!opStack.isEmpty()){
result.add(String.valueOf(opStack.pop()));
}
Stack<Integer> numStack = new Stack<>();
for (int j = 0; j < result.size(); j++) {
String op = result.get(j);
if (isOp(op)){
int num1 = numStack.pop();//栈顶元素
int num2 = numStack.pop();//次栈顶元素
switch (op){
/**
* 对于后缀表达式,进行计算时,需要栈顶元素与次栈顶元素进行计算
* 如
* 次栈顶元素 + 栈顶元素
* 次栈顶元素 - 栈顶元素
* 次栈顶元素 * 栈顶元素
* 次栈顶元素 / 栈顶元素
* 次栈顶元素 ^ 栈顶元素
*/
case "+": {
numStack.push(num2+num1);
break;
}
case "-": {
numStack.push(num2-num1);
break;
}
case "*": {
numStack.push(num2*num1);
break;
}
case "/": {
numStack.push(num2 / num1);
break;
}
case "^": {
numStack.push((int) Math.pow(num2,num1));
break;
}
default:{
throw new IllegalArgumentException("非法字符:"+op);
}
}
}else {
numStack.push(Integer.parseInt(op));
}
}
return numStack.pop();
}
public MathExpr parseMathExpr() {
String expr = input;
List<String> exprStack = new ArrayList<>();
Stack<Character> opStack = new Stack<>();
int i = 0;
//按照后缀表达式解析
while (i<expr.length()) {
char c = expr.charAt(i);
if (isValChar(c)){
String val = valToken(expr,i);
exprStack.add(val);
i=i+ val.length();
continue;
}
if (isOp(c)){
if (c == '('){
opStack.push(c);
}else if (c == ')'){
while (!opStack.isEmpty()){
char op = opStack.pop();
if (op == '('){
break;
}
exprStack.add(String.valueOf(op));
}
}else {
//'+','-','*','/','^'
while (!opStack.isEmpty()){
char o = opStack.peek();
if (o == '(' || priority(o) < priority(c)){
break;
}
exprStack.add(String.valueOf(opStack.pop()));
}
opStack.push(c);
}
}
i++;
}
while (!opStack.isEmpty()){
exprStack.add(String.valueOf(opStack.pop()));
}
Stack<MathExpr> mathExprStack = new Stack<>();
for (int j = 0; j < exprStack.size(); j++) {
String t = exprStack.get(j);
if (isOp(t)){
MathExpr num1 = mathExprStack.pop();//栈顶元素
MathExpr num2 = mathExprStack.pop();//次栈顶元素
switch (t){
case "+":
mathExprStack.push(new AddExpr(num2,num1));
break;
case "-":
mathExprStack.push(new SubExpr(num2,num1));
break;
case "*":
mathExprStack.push(new MulExpr(num2,num1));
break;
case "/":
mathExprStack.push(new DivExpr(num2,num1));
break;
case "^":
mathExprStack.push(new PowerExpr(num2,num1));
break;
default:
throw new IllegalArgumentException("非法字符:"+t);
}
}else {
mathExprStack.push(new ValExpr(t));
}
}
return mathExprStack.pop();
}
}
客户端
public class Client {
public static void main(String[] args) {
testLogicInterpreter();
testMathInterpreter();
}
private static void testMathInterpreter() {
System.out.println("testMathInterpreter");
MathExpr mathExpr = new Interpreter("(a + b)*(c+d)").parseMathExpr();
//执行上下文,相当于计算(1+2)*(3+4) = 21
ExprContext ruleExprContext = new ExprContext();
ruleExprContext.putVal("a", 1);
ruleExprContext.putVal("b", 2);
ruleExprContext.putVal("c", 3);
ruleExprContext.putVal("d", 4);
int result = mathExpr.exec(ruleExprContext);
System.out.println(result);
//直接执行表达式(1+2)*(3+4),不需要执行上下文
//这里也可以把1、2、3、4当作变量,
//然后按照上面的通过执行上下文传递变量,计算结果
int abc = new Interpreter("(1+2)*(3+4)").eval();
System.out.println(abc);
}
private static void testLogicInterpreter() {
System.out.println("testLogicInterpreter");
LogicExpr logicExpr = new Interpreter("(a & b)|!(c&d)").parseLogicExpr();
/**
* 执行上下文
* (a & b)|!(c&d)
* 相当于
* (1<5 & 2<5)|!(6<5&7<5)
*/
Rule<Integer> rule = NumbleRule.lt(5, false);
ExprContext ruleExprContext = new ExprContext();
ruleExprContext.putRule("a", rule);
ruleExprContext.putRule("b", rule);
ruleExprContext.putRule("c", rule);
ruleExprContext.putRule("d", rule);
ruleExprContext.putVal("a", 1);
ruleExprContext.putVal("b", 2);
ruleExprContext.putVal("c", 6);
ruleExprContext.putVal("d", 7);
boolean result = logicExpr.exec(ruleExprContext);
System.out.println(result);
}
}