`
duoerbasilu
  • 浏览: 1486916 次
文章分类
社区版块
存档分类
最新评论

自定义语言的实现——解释器模式(五)

 
阅读更多

18.5 再谈Context的作用

在解释器模式中,环境类Context用于存储解释器之外的一些全局信息,它通常作为参数被传递到所有表达式的解释方法interpret()中,可以在Context对象中存储和访问表达式解释器的状态,向表达式解释器提供一些全局的、公共的数据,此外还可以在Context中增加一些所有表达式解释器都共有的功能,减轻解释器的职责。

在上面的机器人控制程序实例中,我们省略了环境类角色,下面再通过一个简单实例来说明环境类的用途:

Sunny软件公司开发了一套简单的基于字符界面的格式化指令,可以根据输入的指令在字符界面中输出一些格式化内容,例如输入“LOOP 2 PRINT杨过 SPACE SPACE PRINT 小龙女 BREAK END PRINT郭靖 SPACE SPACE PRINT 黄蓉”,将输出如下结果:

杨过小龙女

杨过小龙女

郭靖黄蓉

其中关键词LOOP表示“循环”,后面的数字表示循环次数;PRINT表示“打印”,后面的字符串表示打印的内容;SPACE表示“空格”;BREAK表示“换行”;END表示“循环结束”。每一个关键词对应一条命令,计算机程序将根据关键词执行相应的处理操作。

现使用解释器模式设计并实现该格式化指令的解释,对指令进行分析并调用相应的操作执行指令中每一条命令。

Sunny软件公司开发人员通过分析,根据该格式化指令中句子的组成,定义了如下文法规则:

expression ::= command* //表达式,一个表达式包含多条命令

command ::= loop | primitive //语句命令

loop ::= 'loopnumber' expression 'end' //循环命令,其中number为自然数

primitive ::= 'printstring' | 'space' | 'break' //基本命令,其中string为字符串

根据以上文法规则,通过进一步分析,绘制如图18-6所示结构图:

18-6 格式化指令结构图

在图18-6中,Context充当环境角色,Node充当抽象表达式角色,ExpressionNodeCommandNodeLoopCommandNode充当非终结符表达式角色,PrimitiveCommandNode充当终结符表达式角色。完整代码如下所示:

import java.util.*;

//环境类:用于存储和操作需要解释的语句,在本实例中每一个需要解释的单词可以称为一个动作标记(Action Token)或命令
class Context {
	private StringTokenizer tokenizer; //StringTokenizer类,用于将字符串分解为更小的字符串标记(Token),默认情况下以空格作为分隔符
	private String currentToken; //当前字符串标记
	
	public Context(String text) {
		tokenizer = new StringTokenizer(text); //通过传入的指令字符串创建StringTokenizer对象
		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) {
		if (!token.equals(currentToken)) {
			System.err.println("错误提示:" + currentToken + "解释错误!");
			}
		nextToken();
	}
	
	//如果当前的标记是一个数字,则返回对应的数值
	public int currentNumber() {
		int number = 0;
		try{
			number = Integer.parseInt(currentToken); //将字符串转换为整数
		}
		catch(NumberFormatException e) {
			System.err.println("错误提示:" + e);
		}
		return number;
	}
}

//抽象节点类:抽象表达式
abstract class Node {
	public abstract void interpret(Context text); //声明一个方法用于解释语句
	public abstract void execute(); //声明一个方法用于执行标记对应的命令
}

//表达式节点类:非终结符表达式
class ExpressionNode extends Node {
	private ArrayList<Node> list = new ArrayList<Node>(); //定义一个集合用于存储多条命令
	
	public void interpret(Context context) {
        //循环处理Context中的标记
		while (true){
            //如果已经没有任何标记,则退出解释
			if (context.currentToken() == null) {
				break;
			}
            //如果标记为END,则不解释END并结束本次解释过程,可以继续之后的解释
			else if (context.currentToken().equals("END")) {
				context.skipToken("END");
				break;
			}
            //如果为其他标记,则解释标记并将其加入命令集合
			else {
				Node commandNode = new CommandNode();
				commandNode.interpret(context);
				list.add(commandNode);
			}
		}
	}
	
    //循环执行命令集合中的每一条命令
	public void execute() {
		Iterator iterator = list.iterator();
		while (iterator.hasNext()){
			((Node)iterator.next()).execute();
		}
	}
}

//语句命令节点类:非终结符表达式
class CommandNode extends Node {
	private Node node;
	
	public void interpret(Context context) {
        //处理LOOP循环命令
		if (context.currentToken().equals("LOOP")) {
			node = new LoopCommandNode();
			node.interpret(context);
		}
        //处理其他基本命令
		else {
			node = new PrimitiveCommandNode();
			node.interpret(context);
		}
	}
	
	public void execute() {
		node.execute();
	}
}

//循环命令节点类:非终结符表达式
class LoopCommandNode extends Node {
	private int number; //循环次数
	private Node commandNode; //循环语句中的表达式
	
    //解释循环命令
	public void interpret(Context context) {
		context.skipToken("LOOP");
		number = context.currentNumber();
		context.nextToken();
		commandNode = new ExpressionNode(); //循环语句中的表达式
		commandNode.interpret(context);
	}
	
	public void execute() {
		for (int i=0;i<number;i++)
			commandNode.execute();
	}
}

//基本命令节点类:终结符表达式
class PrimitiveCommandNode extends Node {
	private String name;
	private String text;
	
    //解释基本命令
	public void interpret(Context context) {
		name = context.currentToken();
		context.skipToken(name);
	    if (!name.equals("PRINT") && !name.equals("BREAK") && !name.equals ("SPACE")){
			System.err.println("非法命令!");
		}
		if (name.equals("PRINT")){
			text = context.currentToken();
			context.nextToken();
		}
	}
	
	public void execute(){
		if (name.equals("PRINT"))
			System.out.print(text);
		else if (name.equals("SPACE"))
			System.out.print(" ");
		else if (name.equals("BREAK"))
			System.out.println();
	}
}

在本实例代码中,环境类Context类似一个工具类,它提供了用于处理指令的方法,如nextToken()currentToken()skipToken()等,同时它存储了需要解释的指令并记录了每一次解释的当前标记(Token),而具体的解释过程交给表达式解释器类来处理。我们还可以将各种解释器类包含的公共方法移至环境类中,更好地实现这些方法的重用和扩展。

针对本实例代码,我们编写如下客户端测试代码:

class Client{
	public static void main(String[] args){
		String text = "LOOP 2 PRINT 杨过 SPACE SPACE PRINT 小龙女 BREAK END PRINT 郭靖 SPACE SPACE PRINT 黄蓉";
		Context context = new Context(text);
			
		Node node = new ExpressionNode();
		node.interpret(context);
		node.execute();
	}
}

编译并运行程序,输出结果如下:

杨过小龙女

杨过小龙女

郭靖黄蓉

思考

预测指令“LOOP 2 LOOP 2 PRINT杨过 SPACE SPACE PRINT 小龙女 BREAK END PRINT郭靖 SPACE SPACE PRINT 黄蓉 BREAK END”的输出结果。

【作者:刘伟http://blog.csdn.net/lovelion

分享到:
评论

相关推荐

    zkk950815#design-pattern-java-1#自定义语言的实现——解释器模式(二)1

    除了使用文法规则来定义一个语言,在解释器模式中还可以通过一种称之为抽象语法树(Abstract Syntax Tree, AST)的图形方式来直观地表示语言的构

    design-pattern-java.pdf

    请求发送者与接收者解耦——命令模式(六) 解释器模式-Interpreter Pattern 自定义语言的实现——解释器模式(一) 自定义语言的实现——解释器模式(二) 自定义语言的实现——解释器模式(三) 自定义语言的实现...

    Java设计模式 版本2

    设计模式之代理模式,请求的链式处理——职责链模式,请求发送者与接收者解耦——命令模式,自定义语言的实现——解释器模式,遍历聚合对象中的元素——迭代器模式,协调多个对象之间的交互——中介者模式,撤销功能...

    asp.net知识库

    关于能自定义格式的、支持多语言的、支持多数据库的代码生成器的想法 发布Oracle存储过程包c#代码生成工具(CodeRobot) New Folder XCodeFactory3.0完全攻略--序 XCodeFactory3.0完全攻略--基本思想 XCodeFactory...

    antlr4权威指南

     本书的读者对象本书尤其适用于对数据读取器、语言解释器和翻译器感兴趣的开发者。虽然本书主要利用ANTLR来完成这些工作,你仍然可以学到很多有关词法分析器和语法分析器的知识。初学者和专家都需要本书来高效地...

    sql试 题答案和试题

    1、根据关系数据基于的数据模型——关系模型的特征判定下列正确的一项:(___) A、只存在一对多的实体关系,以图形方式来表示。 B、以二维表格结构来保存数据,在关系表中不答应有重复行存在。 C、能体现一对多...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    基本信息 作者: 臧萌 ...12.2.4 使用接口仅需一步——实现接口 342 12.2.5 接口——让类集多重类型于一身 344 12.2.6 简化recordTransport()方法 347 12.3 再探接口 349 12.3.1 重温上节中的程序 349...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    基本信息 作者: 臧萌 ...12.2.4 使用接口仅需一步——实现接口 342 12.2.5 接口——让类集多重类型于一身 344 12.2.6 简化recordTransport()方法 347 12.3 再探接口 349 12.3.1 重温上节中的程序 349...

    php网络开发完全手册

    7.5.1 获得与模式匹配的数组单元—— 7.5.1 preg_grep 110 7.5.2 进行全局正则表达式的匹配—— 7.5.2 preg_match_all 111 7.5.3 进行正则表达式的匹配——preg_ 7.5.3 match 113 7.5.4 转义正则表达式字符——preg_...

    Grails权威指南

     7.9.3 使用拦截器实现验证  7.10 处理文件上传  7.10.1 使用multipart请求  7.10.2 上传和数据绑定  7.11 本章小结 第8章 groovycservercpages  8.1 基础知识  8.1.1 理解模型  ...

    JAVA上百实例源码以及开源项目源代码

    一个简单的CS模式的聊天软件,用socket实现,比较简单。 凯撒加密解密程序 1个目标文件 1、程序结构化,用函数分别实现 2、对文件的加密,解密输出到文件 利用随机函数抽取幸运数字 简单 EJB的真实世界模型(源代码...

    JAVA上百实例源码以及开源项目

    一个简单的CS模式的聊天软件,用socket实现,比较简单。 凯撒加密解密程序 1个目标文件 1、程序结构化,用函数分别实现 2、对文件的加密,解密输出到文件 利用随机函数抽取幸运数字 简单 EJB的真实世界模型(源代码...

    Java开发技术大全 电子版

    1.2.4解释执行命令的使用10 1.2.5UltraEdit的使用11 1.3一个简单的Java应用程序14 1.4一个简单的Java小程序16 1.5本章小结18 第2章Java语言基础19 2.1Java语言的特点19 2.2Java程序的构成21 2.3数据类 型23 ...

    在线客房预订系统源码

    脚本解释器 php 4.12以上 Web服务器 任何可以运行php和数据库的web服务器 数据引擎 mysql 3.23以上(后续版本支持多种数据库) 权限要求 可以生成和读取生成的文件 空间大小 初次安装至少5M可用空间 硬件要求 ...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

    第7章 自定义数据类型 293 7.1 C++中的结构 293 7.1.1 结构的概念 294 7.1.2 定义结构 294 7.1.3 初始化结构 294 7.1.4 访问结构的成员 295 7.1.5 伴随结构的智能感知帮助 298 7.1.6 RECT结构 299 7.1.7 ...

    Visual C++ 2005入门经典--源代码及课后练习答案

    他曾在IBM工作多年,能使用多种语言进行编程(在多种机器上使用汇编语言和高级语言),设计和实现了实时闭环工业控制系统。Horton拥有丰富的教学经验(教学内容包括C、C++、Fortran、PL/1、APL等),同时还是机械、加工...

    Microsoft SQL Server 2005技术内幕: T-SQ程序设计.pdf

    该书由Itzik Ben-Gan权威执笔,重点关注语言特性以及它们如何被SQL Server引擎解释和处理。  通过本书,你将深入了解T-SQL的高级用法,包括触发器、用户自定义函数、异常处理等。该书解释并比较了SQL Server 2000和...

Global site tag (gtag.js) - Google Analytics