在C语言程序中,很多函数并不是执行全部语句后,才从最底部返回的,大多数情况下,当某些条件成立时就可以通过return 语句立即返回,而无需执行接下来的代码,本节,我们继续增强java开发的C语言解释器功能,使其能够处理return语句,完成本节代码后,我们的C语言解释器能够正常解析和执行下面的代码:
int f() {
int a;
a = 1;
return a;
a = 2;
}
int f2() {
return f();
}
void main() {
f2();
}
当我们的解释器执行上面代码时,解释器在解释执行f函数时,遇到return语句后,立刻把变量a的值返回,return 后面的语句,a=2;将不会被执行,因此调用f函数后得到的返回数值为1.
我们先看看return 语句对应的语法表达式:
STATEMENT -> RETURN EXPR SEMI
STATEMENT -> RETURN SEMI
return 后面可以直接跟一个分号,返回时不附带返回值,也可以跟一个数值,变量,或是一个表达式,而至一个函数调用。
首先,我们要做的仍然是根据表达式构造语法执行树,对应代码如下(codeTreeBuilder.java):
public ICodeNode buildCodeTree(int production, String text) {
ICodeNode node = null;
Symbol symbol = null;
switch (production) {
....
case CGrammarInitializer.Return_Semi_TO_Statement:
node = ICodeFactory.createICodeNode(CTokenType.STATEMENT);
break;
case CGrammarInitializer.Return_Expr_Semi_TO_Statement:
node = ICodeFactory.createICodeNode(CTokenType.STATEMENT);
node.addChild(codeNodeStack.pop());
break;
....
}
}
对return语句的解释执行,是在StatementExecutor中的:
public class StatementExecutor extends BaseExecutor{
private enum LoopType {
FOR,
WHILE,
DO_WHILE
};
@Override
public Object Execute(ICodeNode root) {
int production = (int)root.getAttribute(ICodeKey.PRODUCTION);
ICodeNode node;
switch (production) {
....
case CGrammarInitializer.Return_Semi_TO_Statement:
isContinueExecution(false);
break;
case CGrammarInitializer.Return_Expr_Semi_TO_Statement:
node = executeChild(root, 0);
Object obj = node.getAttribute(ICodeKey.VALUE);
setReturnObj(obj);
isContinueExecution(false);
break;
....
}
}
isContinueExecution 是来自父类BaseExecutor的调用,当return语句执行后,return语句后面的代码,无论有多少,都不能再继续执行了,这个调用的目的就是让BaseExecutor不要去执行return语句后面的代码,后面我们会详细解释。
如果return 后面跟着一个变量,数值,或是表达式的话,那么我们需要先执行这个表达式,以便得到要返回的数值,executeChild(root,0)目的就是执行跟着return后面的表达式,然后通过节点的VALUE属性获得表达式执行后的结果,并调用setReturnObj来设置返回值,setReturnObj 也是来自父类BaseExecutor的接口。
我们再看看BaseExecutor的改动:
public abstract class BaseExecutor implements Executor{
private static boolean continueExecute = true;
private static Object returnObj = null;
protected void setReturnObj(Object obj) {
this.returnObj = obj;
}
protected Object getReturnObj() {
return returnObj;
}
protected void clearReturnObj() {
this.returnObj = null;
}
protected void isContinueExecution(boolean execute) {
this.continueExecute = execute;
}
....
}
由于所有类型的Executor都继承自BaseExecutor,因此把数据存储到BaseExecutor能够使得其他Executor获得,当continueExecute设置成TRUE时,BaseExecutor必须停止执行return 后面的语句所对应的Executor:
protected void executeChildren(ICodeNode root) {
ExecutorFactory factory = ExecutorFactory.getExecutorFactory();
root.reverseChildren();
int i = 0;
while (i < root.getChildren().size()) {
if (continueExecute != true) {
break;
}
ICodeNode child = root.getChildren().get(i);
Executor executor = factory.getExecutor(child);
if (executor != null) {
executor.Execute(child);
}
else {
System.err.println("Not suitable Executor found, node is: " + child.toString());
}
i++;
}
}
执行函数调用的Executor是ExtDefExecutor,该执行器需要通过父类BaseExecutor的接口获得return 的返回值:
public class ExtDefExecutor extends BaseExecutor {
@Override
public Object Execute(ICodeNode root) {
int production = (Integer)root.getAttribute(ICodeKey.PRODUCTION);
switch (production) {
case CGrammarInitializer.OptSpecifiers_FunctDecl_CompoundStmt_TO_ExtDef:
executeChild(root, 0);
ICodeNode child = root.getChildren().get(0);
String funcName = (String)child.getAttribute(ICodeKey.TEXT);
root.setAttribute(ICodeKey.TEXT, funcName);
executeChild(root, 1);
Object returnVal = getReturnObj();
clearReturnObj();
if (returnVal != null) {
root.setAttribute(ICodeKey.VALUE, returnVal);
}
isContinueExecution(true);
break;
}
return root;
}
}
ExtDefExecutor 执行完毕后,相当于整个函数都执行结束了,因此需要调用isContinueExecution(true)。
父函数对子函数的调用是在UnaryExecutor里实现的,如果子函数有返回值,那么也必须进行对应处理:
public class UnaryNodeExecutor extends BaseExecutor{
@Override
public Object Execute(ICodeNode root) {
executeChildren(root);
int production = (Integer)root.getAttribute(ICodeKey.PRODUCTION);
String text ;
Symbol symbol;
Object value;
ICodeNode child;
switch (production) {
....
case CGrammarInitializer.Unary_LP_RP_TO_Unary:
case CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary:
//先获得函数名
String funcName = (String)root.getChildren().get(0).getAttribute(ICodeKey.TEXT);
if (production == CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary) {
ICodeNode argsNode = root.getChildren().get(1);
ArrayList<Object> argList = (ArrayList<Object>)argsNode.getAttribute(ICodeKey.VALUE);
FunctionArgumentList.getFunctionArgumentList().setFuncArgList(argList);
}
//找到函数执行树头节点
ICodeNode func = CodeTreeBuilder.getCodeTreeBuilder().getFunctionNodeByName(funcName);
if (func != null) {
Executor executor = ExecutorFactory.getExecutorFactory().getExecutor(func);
executor.Execute(func);
}
Object returnVal = func.getAttribute(ICodeKey.VALUE);
if (returnVal != null) {
System.out.println("function call with name " + funcName + " has return value that is " + returnVal.toString());
root.setAttribute(ICodeKey.VALUE, returnVal);
}
break;
....
}
}
通过以上代码的改进后,我们用java开发的C语言编译器就可以解释执行return语句了。由于文本描述的局限性,请大家通过观看视频的代码讲解和调试来获得更加直观的了解。
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号: