解释器
3.5.12 GOSUB
尽管Small BASIC不支持真正的独立子程序,但是程序员可以使用GOSUB关键字调用程序的某一部分。此外,从子程序返回时要用到另一个关键字RETURN。GOSUB和RETURN的一般形式如下所示:
GOSUB 行号
……
行号
子程序代码
RETURN
调用子程序,即使调用Small BASIC中有限的子程序,也需要使用堆栈。其原因与FOR语句要求使用堆栈的原因大体相同。Small BASIC允许子程序进行嵌套调用,也就是说一个子程序可能会调用另一个子程序。因此必须使用堆栈,保证将一个RETURN语句与其对应的GOSUB关联起来。GOSUB堆栈保存在另一个Stack对象中,这里定义了一个gStack变量保存该Stack对象的引用:
// Stack for gosubs.
private Stack gStack;
gStack中保存着记录程序位置的索引。在解释过程中,每次遇到GOSUB关键字时,就将程序的当前索引压入gStack的顶部;相应地,每次解释执行到RETURN语句时,就从堆栈中弹出返回位置的索引。
下面给出gosub()和return()方法的代码:
// Execute a GOSUB.
private void gosub() throws InterpreterException
{
Integer loc;
getToken();
// Find the label to call.
loc = (Integer) labelTable.get(token);
if(loc == null)
handleErr(UNDEFLABEL); // label not defined
else {
// Save place to return to.
gStack.push(new Integer(progIdx));
// Start program running at that loc.
progIdx = loc.intValue();
}
}
// Return from GOSUB.
private void greturn() throws InterpreterException
{
Integer t;
try {
// Restore program index.
t = (Integer) gStack.pop();
progIdx = t.intValue();
} catch(EmptyStackException exc) {
handleErr(RETURNWITHOUTGOSUB);
}
}
下面介绍GOSUB语句的工作流程。当遇到GOSUB关键字时,调用gosub()方法处理—— 首先查找目标代码行的行号,并将其保存在变量loc中。接着,progIdx的当前值被压入GOSUB堆栈的栈项(当子程序执行完毕时,程序将要返回到这个点上来执行)。最后,将loc中保存的索引赋给progIdx。于是程序就转移到子程序的开始位置,从这里继续执行。当遇到RETURN关键字时,调用return()方法处理——首先将GOSUB堆栈的栈项元素弹出,然后将弹出的值赋给progIdx,于是程序就从GOSUB语句的下一条语句开始继续执行。
由于返回地址在GOSUB堆栈中保存,因此子程序允许嵌套调用。当执行到RETURN语句时,返回的将是最后被调用的子程序(也就是说,最近被调用的子程序的返回地址处于gstack堆栈的顶部)。从理论上来说,这个过程允许GOSUB嵌套任意多层。
3.5.13 END语句
END关键字表示程序执行的结束。但并不是所有程序都需要END语句,因为程序的结尾也能终止程序的执行。程序使用END可以在文件结束之前终止程序的执行。它只是简单地将sbInterp()方法返回,从而终止程序的执行。
|