author: Kevin Lynx email: zmhn320#163.com date: 3.10.2009
符号表
在上一节中,当我们的解释器解释执行age=age+1这个语法树时,会涉及到变量age的值
。实际上我们还需要个保存脚本中相关变量的模块,当我们的解释器获取到一个ID树节点时
,需要从这个模块中获取出该变量的值,并参与运算。
这个我称之为符号表。我想到这里,我所说的概念很可能和教科书有点不一样了。
什么是符号表?
符号表(symbol table)就如同其字面意思一样,是一个表,更宽泛地说是一个保存符号
的容器。
脚本中诸如变量函数之类的东西都算作符号,例如age。符号表就是保存这些符号的容
器。
在kl中,符号表保存着某一个作用域里的变量。其全局符号表还保存着函数符号,对于
函数符号而言,其值为语法树树节点的指针值。当调用一个函数时,将该值转换为树节点,
然后执行。当然,这应该算做解释执行一节的细节,不多说。
再明确下符号表的作用,举例,在上一节中,涉及到这么一个例子函数:
value factor( TreeNode *node )
{
switch( node->type )
{
case ID:
/* 在这里,发现一个树节点类型为ID,就需要根据ID对应的名字,也就
是age,在符号表中查找age的值 */
return age;
/* ... */
}
}
以上注释阐述了符号表的作用。
符号表的实现
其实不管符号表如何实现,对于其他模块而言,对符号表的唯一要求就是提供几个类似
这样的接口:
value sym_lookup( const char *name );
void sym_insert( const char *name, value val );
也就是说,提供查找符号值,以及插入新符号的接口。
在kl中,使用了<编译原理与实践>中相同的符号表数据结构实现。即使用了hash表,
hash数组中每个元素保存的是一个链表头节点。每一个符号字符串通过散列函数得到hash数
组索引,然后在该索引里进行一次线性查找。很典型的hash结构。
另一方面,因为kl支持全局和函数局部两个作用域。所以kl中有一个全局符号表,用于
保存全局变量以及所有的函数符号;同时每一次进入一个函数时,就会创建一个临时的局部
符号表,用于存储局部变量;后来,为了支持插件,插件函数被特定地保存在另一个全局符
号表里。
代码导读
kl中的符号表实现代码在klsymtab.h/klsymtab.c中,实现比较简单,无需多言。