(1)概述
定义一个语言,定义它的文法表示,并定义一个解释器,根据文法规则来解释语言中的句子
文法(语法)规则:
用于描述语言的语法结构的形式规则
# 例:
expression ::= value | plus | minus # ::= 表示定义为
plus ::= expression '+' expression
minus ::= expression '-' expression
value ::= integer
# 表达式可以是一个值,也可以是plus、minus运算, 而plus、minus又由表达式结合运算符构成,值的类型为整数
抽象语法树
在计算机科学中,抽象语法树(AbstractSyntaxTree,ATS)简称语法树(Syntax Tree),是源代码语法的一种抽象表示,它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
如:1 + 2 + 3 - 4
(2)结构
- 抽象表达式:定义解释器的接口,约定解释器的解释操作,主要包含解释方法
interpret()
- 终结符表达式:抽象表达式的子类,实现文法与终结符相关的操作,文法中的每一个终结符都有与之对应的具体终结表达式
- 非终结符表达式:抽象表达式的子类,实现文法与非终结符相关的操作,文法中的每条规则都对应一个非终结符表达式
- 环境角色:包含各个解释器需要的数据或公共功能,用来传递被所有解释器共享的数据,后面的解释器可以从该角色获取相应的数据
- 客户端:将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,同时也可以通过环境角色间接访问解释器的解释方法
(3)案例
以加减运算为例
抽象表达式
public abstract class AbstractExpression {
/**
* 解释(解析)
*
* @param context 环境变量
* @return
*/
public abstract int interpret(Context context);
}
非终结符表达式
public class Minus extends AbstractExpression {
//减号左边的表达式
private AbstractExpression left;
//减号右边的表达式
private AbstractExpression right;
public Minus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + " - " + right.toString() + ")";
}
}
public class Plus extends AbstractExpression {
//加号左边的表达式
private AbstractExpression left;
//加号右边的表达式
private AbstractExpression right;
public Plus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + " + " + right.toString() + ")";
}
}
终结符表达式
public class Variable extends AbstractExpression {
//变量名
private String name;
public Variable(String name) {
this.name = name;
}
@Override
public int interpret(Context context) {
//直接返回变量值
return context.getVariable(this);
}
@Override
public String toString() {
return name;
}
}
环境角色
public class Context {
private Map<Variable, Integer> map = new HashMap<>();
/**
* 添加变量
*
* @param var key
* @param value value
*/
public void assign(Variable var, Integer value) {
map.put(var, value);
}
/**
* 获取变量
*
* @param var key
* @return
*/
public int getVariable(Variable var) {
return map.get(var);
}
}
Client
public class Client {
public static void main(String[] args) {
//创建环境对象
Context context = new Context();
//创建多个变量对象
Variable a = new Variable("a");
Variable b = new Variable("b");
Variable c = new Variable("c");
Variable d = new Variable("d");
//存储变量
context.assign(a, 1);
context.assign(b, 2);
context.assign(c, 3);
context.assign(d, 4);
//获取抽象语法树 a + b - c + d
AbstractExpression expression = new Plus(a, new Minus(b, new Plus(c, d)));
//解释(解析)
System.out.println(expression.interpret(context));
}
}
(4)优缺点
- 易于改变和扩展文法:由于解释器模式中使用类来表示语言的文法规则,因此可以使用继承等机制来扩展或改变文法
- 实现文法较为容易:在抽象语法树中,每一个表达式节点类的实现方式都是类似的,且不是特别复杂
- 增加新的解释表达式较为方便:在需要扩展时只需增加一个对应的终结符/非终结符表达式类,符合开闭原则
- 复杂文法难以维护:在该模式中,每一条规则至少需要定义一个类,类的个数会随着文法规则的增加而增加
- 执行效率低:该模式使用了大量的循环和递归调用,在解释较为复杂的句子时存在性能问题(变量类型重写抽象表达式的解释(解析)方法,直接获取对应
key
的值,非终结符表达式先调用其他表达式父类的解释(解析)方法,然后才到自身这边)
评论 (0)