背景
在解释器(interpreter)模式中,程序要解决的问题会被用非常简单的“迷你语言”表述出来。即用“迷你语言”编写“迷你程序”把具体的问题表述出来。
迷你语言 无法单独工作,我们需要使用Java语言编写一个负责“翻译”的程序。
翻译程序会理解迷你语言,并解释和运行迷你程序,这段翻译程序称为翻译器,当需要解决的问题发送变化的时候,不需要修改Java语言程序,只需要修改迷你程序即可;
登场角色
AbstractExpression 抽象表达式
定义语法树节点的共同接口(API)
TerminalExpression 终结符表达式
TerminalExpression 对应于 BNF 的终结符表达式,如示例程序的 PrimitiveCommandndoe 类
NonterminalExpression 非终结符表达式
NonterminalExpression 对应于 BNF 的非终结符表达式,如示例程序的 CommandNode、RepeatCommand、CommandListNode 类
Context 文脉 上下文
Context 角色为解释器进行语法解析提供了必要的信息
Client 请求者
为了推导语法树,Client 角色会调用 TerminalExpression 角色 和 NonterminalExpression 角色
示例代码
基于 BNF 实现的表达式,底层解释器使用Java编写,递归的形式与组合模式有一定的类似
Node
public abstract class Node {
public abstract void parse(Context context) throws ParseException;
}
ProgramNode
public class ProgramNode extends Node {
private Node commandListNode;
@Override
public void parse(Context context) throws ParseException {
// <program> ::= program <command list>
context.skipToken("program");
commandListNode = new CommandListNode();
commandListNode.parse(context);
}
@Override
public String toString() {
return "[program " + commandListNode + "]";
}
}
CommandListNode
public class CommandListNode extends Node {
// <command list> ::= <command>* end
private ArrayList<Node> list = new ArrayList<>();
@Override
public void parse(Context context) throws ParseException {
while (true) {
if (context.currentToken() == null) {
throw new ParseException("Missing 'end'");
} else if (context.currentToken().equals("end")) {
context.skipToken("end");
break;
} else {
Node commandNode = new CommandNode();
commandNode.parse(context);
list.add(commandNode);
}
}
}
@Override
public String toString() {
return list.toString();
}
}
CommandNode
public class CommandNode extends Node {
private Node node;
@Override
public void parse(Context context) throws ParseException {
if (context.currentToken().equals("repeat")) {
node = new RepeatCommandNode();
node.parse(context);
} else {
node = new PrimitiveCommandNode();
node.parse(context);
}
}
@Override
public String toString() {
return node.toString();
}
}
PrimitiveCommandNode
public class PrimitiveCommandNode extends Node {
private String name;
@Override
public void parse(Context context) throws ParseException {
// <primitive command> ::= go | right | left
name = context.currentToken();
context.skipToken(name);
if (!name.equals("go") && !name.equals("right") && !name.equals("left")) {
throw new ParseException(name + " is undefined");
}
}
@Override
public String toString() {
return name;
}
}
RepeatCommandNode
public class RepeatCommandNode extends Node {
private int number;
private Node commandListNode;
@Override
public void parse(Context context) throws ParseException {
// <repeat command> ::=repeat <number> <command list>
context.skipToken("repeat");
number = context.currentNumber();
context.nextToken();
commandListNode = new CommandListNode();
commandListNode.parse(context);
}
@Override
public String toString() {
return "[repeat " + number + " " + commandListNode + "]";
}
}
类图
功能分析
- 正则表达式。检索表达式,批处理语言等也可以基于interpreter模式实现;
- 必须时刻注意“进入这个方法时已经读到哪个标记了?出了这个方法时应该读到哪个标记?”;