AST解释执行

语法和语义分析的结果是抽象语法树AST,再往后编译原理还有代码生成及优化的很大一部分,但如果只是做一个执行器,到AST为止就可以解释执行了,当然就算不生成AST,解析执行也可以,只是基于之前说过的原因,极少采用解析执行的方式  

目前的大多数解释执行的语言,都是在虚拟机解释字节码执行,这个后面再说,它只是把AST的解释串行化了而已,事实上ruby在1.9版本之前是解释AST执行的,到1.9整合了YARV才改成了解释字节码  

抽象地看,一个高级语言的AST就是一个stmt_list,语法分析过程只是把人能看懂的语言转成方便解释器看懂的罢了,具体的执行过程和人阅读代码没什么差别,就是一个个stmt执行过去,前面讲过stmt的接口类的样子:  
class Stmt 
{ 
    RetType execute(); 
} 

这个RetType后面再细说,形式上可以认为一个解释器的架构就是:  
RetType execute_stmt_list(StmtList stmt_list) 
{ 
    for stmt in stmt_list //就是遍历,这里用伪代码 
    { 
        RetType ret = stmt.execute(); 
        //处理RetType,后面讲 
    } 
} 

因此,核心部分在于实现各个stmt的execute部分,为简单起见,我们讨论动态类型语言,所有变量类型都是用Object,这里还需要一个expr的接口:  
class Expr 
{ 
    Object compute(); 
} 

静态类型的情况虽然会复杂点,但也不难想到  

于是很容易写出前面StmtPrint的execute方法(方便起见代码主要用类似java形式,不过不一定和java语法契合):  
Object a = this.e.compute(); 
System.out.println(a); 
return RET_TYPE_NORMAL; 

然后考虑StmtWhile的execute方法:  
while (this.condition.compute().as_bool()) //因为是动态类型,as_bool将Object转为布尔型,和宿主语言契合 
{ 
    RetType ret = execute_stmt_list(this.stmt_list); 
    //... 
} 

在这里,出现了嵌套语句列表,相应地递归调用execute_stmt_list解释执行,这里需要解释下RetType的含义,根据具体的语法,一个语句块返回一般有以下几种情况:运行结束,break,continue,return,异常。而RetType则对应这几种,那么显然上面//...的内容差不多是这样:  
switch (ret) 
{ 
    case RET_TYPE_NORMAL: 
    case RET_TYPE_CONTINUE: 
        continue; 
    case RET_TYPE_BREAK: 
        break; //这里的意思是跳出while循环,不是switch 
    case RET_TYPE_RETURN: 
        return RET_TYPE_RETURN; 
    case RET_TYPE_EXCEPTION: 
        return RET_TYPE_EXCEPTION; 
} 

return和异常直接往上面一层返回,因为while本身是在一个函数或try中,让上层来处理即可,另外,return的返回值和异常的内容应该已经存在某个地方了,不过这个在上面的伪代码里面没有体现,它属于运行时环境的范畴,后面再说  

expr的compute方法的实现,差不多都是类似的,先计算各个操作数,然后最后做当前对象的运算,比如加法:  
class ExprAdd 
{ 
    Expr a; 
    Expr b; 
    Object compute() 
    { 
        return a.compute().add(b.compute()); 
    } 
} 

递归调用元素的compute,当然这个递归不会无限下去,最后都会停止于一个load操作:  
class ExprLoad 
{ 
    LoadArg a; 
    Object compute() 
    { 
        return env.get(a); 
    } 
} 

其中env是运行时环境的一个抽象,简单说就是根据a的内容返回对应的对象即可,这个load操作一般会根据名字空间细化为几种,比如LoadLocal,LoadGlobal,LoadConst,表示从局部变量区,全局区,常量区  

特殊点的,函数调用expr:  
class ExprCallFunc 
{ 
    Func f; 
    ExprList arg_list; 
    Object compute() 
    { 
        frm = f.init_frame(compute_expr_list(arg_list)); //初始化函数调用栈 
        RetType ret = execute_stmt_list_with_frm(f.stmt_list, frm); 
        assert ret == RET_TYPE_RETURN; 
        return frm.ret_value; 
    } 
} 

这个实现稍微丑陋了点,其实可以改成f.execute(...)更直观些,另外这里的execute_stmt_list_with_frm以及ret_value存在栈帧里面只是表个意思,实际不一定这么实现

你可能感兴趣的:(AST解释执行)