没有合适的资源?快使用搜索试试~ 我知道了~
首页使用Java实现语言解释器
使用Java实现语言解释器
2星 需积分: 20 14 下载量 26 浏览量
更新于2023-07-05
收藏 1.02MB DOC 举报
。。。。。。。。。。。。。。。。。使用Java实现语言解释器。。。使用Java实现语言解释器
资源详情
资源推荐
CHAPTER
3
使用Java实现语言解释器
大多数程序员都曾经梦想着创造自己的计算机语言。坦率地说,能够创造、控制
增强和修改属于自己的计算机语言,这种想法确实非常具有吸引力。然而,只有极少
数程序员认为,实现这个想法是一件非常容易和令人愉悦的事情。开发一个功能齐备
的编译器(例如Java编译器)的确是一项艰巨的任务。但是相比之下,创建一个语言解
释器却简单得多。
尽管解释器和编译器都以应用程序源代码作为输入内容,但是它们对这些源代码
的处理过程却截然不同。编译器将程序的源代码转化为可执行代码的形式。通常情况
下,这种可执行代码由计算机的CPU指令组成,因此可以直接在计算机上执行。例如
C++即采用这种编译方式。还有一种情况,编译器输出一种可移植的中间代码,它们
由运行时系统执行。Java采用的就是这种方式。在Java中,称这种中间代码为“字节码
”。
解释器的工作原理则完全不同。它顺序读入程序的源代码,然后依次执行每一条
语句。因此,解释器并不真正将源代码转化为目标代码,而是直接执行程序。尽管使
用解释器执行程序的速度比将相同的程序编译成目标代码后再执行的速度慢,但是解
释器仍然在编程中被广泛使用。原因有以下几个方面:
第一,解释器能够提供真正的交互式环境,由解释器执行的程序能够根据用户的
J a v a 编程 艺术
指令暂停或者恢复运行。这种交互式环境在机器人技术等方面用途很广。第二,语言
解释器的先天特性决定了它们特别适合于交互式的程序调试。第三,解释器最适合于
作为“脚本语言”,比如数据库查询语言等。第四,语言解释器使得同一个程序运行于
不同类型的平台成为可能。此时惟一的工作只是为每个新环境实现解释器的运行包。
在有些情况下,术语“解释器”的含义与刚才所描述的情况有所不同。例如,最初
的Java运行时系统被称为“字节码解释器”。但是这种解释器与本章中介绍的解释器的
类型并不完全相同。字节码是一组高度优化的可移植的机器指令,而Java运行时系统
则为字节码提供一个执行环境。然而Java运行时系统并不直接执行源代码,而是执行
可移植的机器代码。这也是Java运行时系统被称为Java虚拟机的原因。
本章将要介绍的解释器代码不仅有趣而且实用。同时,它还充分显示了Java语言
简单高效的特性。与第2章中介绍的解析器相同的是,这个语言解释器也使用“纯代
码”编写。同时,解释器也是一个相当复杂的程序,但是使用Java语言实现起来却非
常简单,这也是Java语言功能多样化的一个例证。此外,解析器代码的简洁还显示了
Java语法和库的强大表达能力。
3.1 解释何种计算机语言
在构造解释器之前,首先必须确定将要解释的语言类型。尽管Java似乎是个不错
的选择,但是它过于庞大和复杂。即使选取Java语言的一个小的子集也显得太大了,
因为我们只打算利用一章的篇幅进行讲解。而且,通常情况下也没有必要为一个像
Java那么强大的语言编写解释器;相反地,编写一个解释器处理某种相对简单的计算
机语言倒是可行的。因此,更好的做法是选择一种易于解释、较为简单的语言。
BASIC语言的早期版本就非常符合这些标准,因此在此选择了BASIC语言的一个子集
将 其 作 为 本 章 中 所 介 绍 的 解 释 器 的 解 释 语 言 。 在 下 文 中 将 称 这 个 子 集 为 S m a l l
BASIC。
本章选择了这个类BASIC语言有3方面原因。第一,BASIC语言最初正是为解释
执行而设计的。因此,实现一个BASIC解释器相对比较容易。例如,BASIC语言的早
期版本不支持局部变量、递归方法、语句块、类、重载等特征—— 但是以上所有这
些特性都将增加BASIC语言的复杂性。虽然缺少了许多功能,但是解释BASIC子集的
基本原理同样适用于其他语言。理解本章中介绍的解释器代码,能够为开发其他语言
解释器打下基础。选择BASIC的第二个原因是,可以用相对较小的代码量实现一个较
为合理的子集。第三,早期的BASIC语言语法简单、容易掌握,几乎不需要用额外的
时间来学习。因此,即使一点都不了解传统的BASIC语言,也能够毫无困难地使用
Small BASIC。
下面给出一个用Small BASIC编写的程序,可以看到,使用这种语言是多么的简
39
第 3 章 使用 Java 实现语言解释器
单。即使从来没有见过传统风格的BASIC程序,也能够轻松理解其操作过程。
PRINT "A Simple Small BASIC Program"
FOR X = 1 TO 10
GOSUB 100
NEXT
END
100 PRINT X
RETURN
该程序运行后得到如下的输出结果:
A Simple Small BASIC Program
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0
10.0
尽管在Small BASIC语言中关键字的含义几乎一目了然,但是本章仍将详细解释
每个关键字。
最后,Small BASIC仿造的是早期的BASIC版本,它不同于后来出现的 Visual
Basic。事实上,Visual Basic与原始的BASIC几乎没有多少共同点。当然,只要掌握
了这个解释器的工作原理,就可以改造它,使之能够解释所需要的任何语言或者变量
3.2 解释器概述
在开始之前有必要再次强调:本章介绍的解释器是一个源代码解释器。也就是说
解释器在执行时,每次读入一条语句,并且根据这条语句执行特定的操作;然后再读
40
J a v a 编程 艺术
入下一条语句,依次类推。这与伪代码解释器是有所区别的,例如早期的Java运行时
系统。两者的区别在于:源代码解释器直接对程序的源代码解释执行;而伪代码解释
器先将程序的源代码转化为某种与机器无关的中间代码,然后再执行中间代码。相比
之下,源代码解释器更易于创建,并且不需要一个独立的编译过程。
Small BASIC解释器包括两个主要的子系统:一个是表达式解析器,负责处理数
字表达式;另一个是解释器,负责程序的实际执行。对于前者,可采用本书第2章所
介绍的表达式解析器。但是在这里做了某些改进,使得解析器能够解析包含在程序语
句中的数字表达式,而不是只能解析孤立的表达式。
解释器子系统和解析器子系统包含在同一个解释器类中,该类名为SBasic。尽管
从理论上讲可以使用两个独立的类:一个包含解释器,另一个包含表达式解析器;但
是将两者用同一个类来实现的代效率会更高,因为表达式解析器和解释器的代码是密
不可分的。例如,两个子系统都操作保存着程序代码的同一个字符数组。如果将它们
分别安排在两个类中,将会增加可观的额外开销,并导致性能上的损失和功能上的重
复。此外,由于程序解释的任务繁重,而解析表达式只是其中的一部分,因此将整个
解释机制包含在单个类中是很有意义的。
解释器执行时,每次从程序的源代码中读入一个标识符。如果读入的是关键字,
解释器就 按照 该 关 键字的要 求 执 行 规 定的操作 。 举 例来说, 当解 释 器 读 入 一个
PRINT后,它将打印PRINT之后的字符;当读入一个GOSUB时,它就执行指定的子
程序。在到达程序的结尾之前,这个过程将反复进行。可以看到,解释器只是简单地
执行程序指定的动作。
3.3 Small BASIC解释器
Small BASIC解释器的代码相当长—— 一般情况下不会将这么长的代码放在书的
一章之中。但是不要被它的长度所吓倒。抛开其长度不谈,这个解释器的概念其实比
较简单,只要掌握了解释器的一般模式,就能轻松理解它的每个部分。
接下来给出Small BASIC解释器的完整代码。本章剩下的篇幅将详细解释它的工
作原理和使用方法。
// A Small BASIC Interpreter.
import java.io.*;
import java.util.*;
// Exception class for interpreter errors.
class InterpreterException extends Exception {
41
第 3 章 使用 Java 实现语言解释器
String errStr; // describes the error
public InterpreterException(String str) {
errStr = str;
}
public String toString() {
return errStr;
}
}
// The Small BASIC interpreter.
class SBasic {
final int PROG_SIZE = 10000; // maximum program size
// These are the token types.
final int NONE = 0;
final int DELIMITER = 1;
final int VARIABLE = 2;
final int NUMBER = 3;
final int COMMAND = 4;
final int QUOTEDSTR = 5;
// These are the types of errors.
final int SYNTAX = 0;
final int UNBALPARENS = 1;
final int NOEXP = 2;
final int DIVBYZERO = 3;
final int EQUALEXPECTED = 4;
final int NOTVAR = 5;
final int LABELTABLEFULL = 6;
final int DUPLABEL = 7;
final int UNDEFLABEL = 8;
final int THENEXPECTED = 9;
final int TOEXPECTED = 10;
final int NEXTWITHOUTFOR = 11;
42
剩余61页未读,继续阅读
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功