解释器
3.5.9 GOTO语句
在传统的BASIC语言中,最重要的程序控制形式是GOTO语句。GOTO语句的目标必须是一个行号。在Small BASIC语言中也保留了这一传统处理方法。有些人可能知道,早期的BASIC版本要求程序中的每行代码都必须以一个行号开始。但是Small BASIC不再要求每行都有行号;只有当某一行是GOTO语句的目标语句时,它才需要一个行号。因此,在Small BASIC中,行号仅仅只是一个标签。GOTO语句的一般形式如下:
GOTO 行号
当解释器遇到一个GOTO语句时,执行过程必须跳转到与行号相对应的代码行。处理GOTO语句的主要问题在于:必须同时提供向前跳转和向后跳转。为了有效地解决这个问题,要求在执行之前就必须扫描整个程序,并将扫描到的每个标签的位置存储在一张表中。那么,以后每次执行GOTO语句时,就可以从这张表中获得目标代码行的位置,并将程序执行点转移到该位置。
TreeMap集合是一种存储标签及其位置的理想的数据结构,因为每个树图都有一个与特定值关联的键。在解释器中,可以将标签指定为键,将键值指定为标签索引。每个树图保存一个标签/索引对。程序声明了一个名为labelTable的实例变量,由它完成对树图的引用。labelTable的声明如下:
// A map for labels.
private TreeMap labelTable;
scanLabels()方法扫描整个程序,并将每个标签的位置插入TreeMap表中。在程序执行之前,由run()方法调用scanLabels()进行最后预处理。而findEOL()方法用来查找一行程序代码的结尾。下面是scanLabels()和findEOL()的代码:
// Find all labels.
private void scanLabels() throws InterpreterException
{
int i;
Object result;
// See if the first token in the file is a label.
getToken();
if(tokType==NUMBER)
labelTable.put(token, new Integer(progIdx));
findEOL();
do {
getToken();
if(tokType==NUMBER) { // must be a line number
result = labelTable.put(token,
new Integer(progIdx));
if(result != null)
handleErr(DUPLABEL);
}
// If not on a blank line, find next line.
if(kwToken != EOL) findEOL();
} while(!token.equals(EOP));
progIdx = 0; // reset index to start of program
}
// Find the start of the next line.
private void findEOL()
{
while(progIdx < prog.length &&
prog[progIdx] != '\n') ++progIdx;
if(progIdx < prog.length) progIdx++;
}
scanLabels()方法检查每行代码的第一个标识符。如果该标识符是一个数字,那么就认为这是一个行号(或者说,是一个标签)。发现标签后,scanLabels()就调用put()方法将其存储到labelTable中。接着调用TreeMap的put()方法返回一个引用,指向该标签以前的映射。如果先前没有任何映射,那么就返回null。因此,如果返回值不为null,那么说明该标签已经存储在树图中。这将导致一个标签重复的错误,因为在一个程序中不允许出现两个相同的标签。
解释器调用execGoto()方法处理程序中出现的GOTO语句。代码如下所示:
// Execute a GOTO statement.
private void execGoto() throws InterpreterException
{
Integer loc;
getToken(); // get label to go to
// Find the location of the label.
loc = (Integer) labelTable.get(token);
if(loc == null)
handleErr(UNDEFLABEL); // label not defined
else // start program running at that loc
progIdx = loc.intValue();
}
首先,通过下面这行代码获得与目标标签相关联的位置:
loc = (Integer) labelTable.get(token);
TreeMap的get()方法返回与键相关联的值。如前所述,这个键就是标签,而键值就是该标签在程序中的索引。如果找不到标签,get()方法将返回null。如果找到标签,那么标签的值将被赋给progIdx。之后,程序将从progIdx所指示的新位置继续执行。(请记住:progIdx是程序当前执行代码的索引。)
|