AST解释执行

来源:互联网 发布:淘宝售后的服务流程 编辑:程序博客网 时间:2024/05/01 07:11
语法和语义分析的结果是抽象语法树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存在栈帧里面只是表个意思,实际不一定这么实现
0 0
原创粉丝点击