图解设计模式 - Interpreter 模式

读书笔记 仅供参考

简述

在 Interpreter 模式中,需要编写一个“翻译程序”,翻译自己定义的“迷你语言”,可以通过“迷你语言”编写“迷你程序”。

角色和 UML

AbstractExpression

定义了语法树节点的共同接口(API)。

TerminalExpression

对应 BNF 中的终结符表达式。

NonterminalExpression

对应 BNF 中的非终结符表达式。

Context

为解释器进行语法解析提供了必要的信息。

Client

为了推导语法树,Client 角色会调用 TerminalExpression 和 NonterminalExpression 角色。

UML

这里写图片描述

例子

例程是一个简单的语法解析器,能够识别文件中的“迷你代码”。迷你代码主要是操作机器人行走,程序都是由 “program”和“end”开始结尾。可以执行 go、left、right 等命令。

语法树

这里写图片描述

代码
public abstract class Node {
    public abstract void parse(Context context) throws ParseException;
}
public class Context {
    private StringTokenizer tokenizer;
    private String currentToken;

    public Context(String text) {
        tokenizer = new StringTokenizer(text);
        nextToken();
    }

    // 获取下一个标记
    public String nextToken() {
        if (tokenizer.hasMoreTokens()) {
            currentToken = tokenizer.nextToken();
        } else {
            currentToken = null;
        }
        return currentToken;
    }

    // 获取当前标记
    public String currentToken() {
        return currentToken;
    }

    //先检查当前标记,然后获取下一个标记
    public void skipToken(String token) throws ParseException {
        if (!token.equals(currentToken)) {
            throw new ParseException("Waring: " + token + " is expected, but " + currentToken + " is found");
        }
        nextToken();
    }

    //获取当前标记对应的数值
    public int currentNumber() throws ParseException {
        int number = 0;
        try {
            number = Integer.parseInt(currentToken);
        } catch (NumberFormatException e) {
            throw new ParseException("Waring: " + e);
        }
        return number;
    }
}
// 对应语法:<program> ::= program <command list>
public class ProgramNode extends Node{
    private Node commandListNode;
    @Override
    public void parse(Context context) throws ParseException {
        context.skipToken("program");
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    @Override
    public String toString() {
        return "[ program " + commandListNode +
                ']';
    }
}
// 对应语法 <command list> ::= <command>* end
public class CommandListNode extends Node {
    private List<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();
    }
}
// <repeat command> ::= repeat <number> <command list>
public class RepeatCommandNode extends Node {
    private int number;
    private Node commandListNode;

    @Override
    public void parse(Context context) throws ParseException {
        context.skipToken("repeat");
        number = context.currentNumber();
        context.nextToken();
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    @Override
    public String toString() {
        return "[repeat " + number + " " + commandListNode + "]";
    }
}
// <command> ::= <repeat command> | <primitive command>
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();
    }
}
// <primitive command> ::= go | right | left
public class PrimitiveCommandNode extends Node {
    private String name;

    @Override
    public void parse(Context context) throws ParseException {
        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;
    }
}
public class Main {
    public static void main(String[] args) {
        try {
            BufferedReader reader = new BufferedReader(
                    new FileReader("file/interpreter/program.txt"));
            String text;
            while ((text = reader.readLine()) != null) {
                System.out.println("text = \"" + text + "\"");
                Node node = new ProgramNode();
                node.parse(new Context(text));
                System.out.println("node = " + node);
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
program.txt
program end
program go end
program go right go right go right go right end
program repeat 4 go right end end
program repeat 4 repeat 3 go right go left end right end end
运行结果

这里写图片描述

UML

这里写图片描述

其他迷你语言

  • 正则表达式
  • 检索表达式
  • 批处理语言
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值