loop_in_codes

低调做技术__欢迎移步我的独立博客 codemaro.com 微博 kevinlynx

实现一种解释性脚本语言(六)

author: Kevin Lynx email: zmhn320#163.com date: 3.11.2009

解释器

    整理出语法树后,我们就可以根据语法树,并配合符号表开始解释执行脚本代码。这就
是接下来要涉及到的解释器。

工作原理

    在第四节中讲语法树时,其实就已经提到解释器的大致工作原理。
    一个kl的hello world例子代码大致为:
    function main()
    {
        print( "hello world\n" );
    }
    在第二节中我描述了kl代码整体上的结构,是以函数为单位的。因此,对于一个完整的
kl脚本代码,其经过语法处理后,将建立一棵大的语法树,该语法树大致结构为:
    fn1_node
        stmt_node1
        stmt_node2
        ...
    fn2_node
        stmt_node1
        stmt_node2
        ...

    fn1_node和fn2_node同属于同一个作用域,fn1_node的sibling指针指向fn2_node,即在
整个树结构中,每一个node通过child[3]成员连接其子节点,通过sibling指针连接其相邻
的节点。   
    解释器解释执行时,就是从main函数所对应的节点开始递归执行的。对于每个节点,都
可以知道该节点对应了哪种程序逻辑:是加法运算、比较运算、还是一些控制语句等等。
    以这样的控制语句举例:
    if( 1 ) print( "true" );
    对if语句而言,其语法树结构为:
          if_node
         /   |    \
        /    |     \
    con_exp    then_stmt else_stmt

    即,if语句有最多有三个子节点(child[3]),child[0]指向if的条件表达式,child[1]
指向条件表达式为真时执行的语句序列,如果if有else部分,那么child[2]就指向else部分
的语句序列。
    那么,在发现某个节点是if节点时,就首先计算其条件表达式节点。这个节点的计算方
式同脚本中其他所有表达式的计算方式相同,当然,它也是一个递归操作。计算完后判断该
表达式的值是否为真,为真则递归执行if节点的child[1]节点,否则检查是否有else节点,
有的话就执行child[2]节点。

    其他所有节点的解释方式都是相同的。


解释器环境

    解释器环境指的是解释器在解释执行脚本代码时,所需要的运行时环境。kl中主要是符
号表信息。一个解释器环境会有三个符号表:全局符号表,主要保存全局变量以及脚本函数
符号;函数局部符号表,在解释调用一个脚本函数时,会建立临时的符号表;插件符号表,
用于保存插件注册的函数。

如何解释执行函数

    函数主要有两大类型:脚本内定义的函数以及插件注册进符号表的函数。无论是哪种函
数,都会在符号表中建立对应的符号。对于前者,符号被保存于全局符号表,其保存的内容
是该函数节点的节点指针;而对于后者,则保存的插件函数的函数地址值。

    每一次解释器解释到一个函数调用节点时,会优先在插件符号表中查找该函数符号。如
果找到,就将其值转换为约定的插件函数类型(如同lua里注册的C函数一样),然后整理参
数调用之。这个时候代码执行权转接到插件函数里。如果没找到,就在全局符号表里查找,
找到后就强转为语法树节点指针,并解释执行该节点下的语句。

代码导读

    解释器的代码位于klinterpret.h/klinterpret.c中。整体上而言没什么特别的地方,
主要是利用语法树的特点。
    完成了这一节后,kl就已经可以解释执行所有的脚本语句。当然,因为没有输出功能,
只能在调试器里看看计算结果。下一节里会讲到将脚本结合进C语言,从而可以让C语言注册
所谓的插件函数到脚本里,也就可以让脚本具有print这样的输出函数。

posted on 2009-03-11 09:12 Kevin Lynx 阅读(3604) 评论(0)  编辑 收藏 引用 所属分类: kl脚本实现编译原理


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理