author: Kevin Lynx email: zmhn320#163.com date: 3.12.2009
脚本与C语言交互
这其实是这一系列的最后一篇,因为我觉得没什么其他需要写的了。
一般而言,脚本语言同C语言交互,包括在C语言中注册C函数到脚本,从而扩展脚本的
功能,以及在C语言中调用脚本函数。
为了扩展脚本的功能,这里引入插件的概念。kl在这方面大致上实现得和lua相似。kl
支持静态插件和动态插件。
在C语言中调用脚本函数,kl中提供了一些简单的接口用于满足需求。
静态插件
静态插件其意思是在C代码中注册函数到脚本中,并随脚本库一起编译链接成最终执行
程序。因为其绑定是在开发一个程序的过程中,所以被称为静态的。
一个插件函数,指的是可以被注册进脚本的C函数。这种函数必须原型一样,在kl中这
个函数的原型为:typedef struct TValue (*kl_func)( ArgType arg_list );
当你定义了一个这样的原型的函数时,可以通过kl库提供的:
int kl_register( struct klState *kl, kl_func f, const char *name )来注册该
函数到kl脚本中。该函数参数很简单,第三个参数指定注册进脚本中时的名字。
原理比较简单:在解释器中保存着一个插件符号表,该符号表的符号名就是这个函数提
供的名字,符号对应的值就是第二个参数,也就是插件函数的函数地址。
解释器解释到函数调用时,先从插件符号表中查找,如果找到符号,就将符号的值转换
为插件函数,并调用之。
插件函数的参数其实是一个参数链表。脚本里调用插件函数时,所传递的参数将被解释
器整理成参数链表并传递给插件函数。kl库中(集中在kllib.h中)提供了一些方便的接口用
于获取每个参数。
插件函数的返回值也将被解释器转换为脚本内部识别的格式,并在必要的时候参与运算
。
动态插件
动态插件同静态插件的运作方式相同,所不同的是动态插件的插件函数被放在动态运行
时库里,例如windows下的dll。
kl插件编写标准里要求每个动态插件必须提供一个lib_open函数。kl解释器(或者kl库
--当被用作库时)载入一个动态插件时,会直接调用lib_open函数。lib_open函数的主要目
的就是把该插件中的所有函数都注册进脚本里。
因为动态插件在设计之初没有被考虑,所以我并没有为kl加入一些原生的关键字用于导
入动态插件,例如import、require之类。我在静态插件层次提供了这个功能。即我提供了
一个libloader静态插件,链接进kl解释器程序。该静态插件提供脚本一个名为import的函
数。该函数负责动态载入dll之类的动态库,并调用里面的lib_open函数完成动态插件的注
册。
C程序里调用脚本函数
这个比较简单,通常C语言想调用一个脚本函数时,会传入脚本函数名。因为脚本函数名
都保存在全局符号表里,kl库从全局符号表找到该函数符号,并转换其值为语法树节点指针
,然后传入解释器模块解释执行。
kl库提供struct TValue kl_call( struct klState *kl, const char *name, ArgType args );
用于在C里调用脚本函数。
代码导读
kllib.h/kllib.c作为一个桥接层,用于封装其他模块可以提供给外部模块使用的接口,
如果将kl作为一个库使用,用户代码大部分时候只需要使用kllib.h中提供出来的接口。
源码目录plugin下的kllibbase.c中提供了静态插件的例子,kllibloader.c提供了装载
动态插件的功能。
源码目录plugin/hge目录下是一个封装2D游戏引擎HGE部分接口到kl脚本中的动态插件
例子。
源码目录test/kl.c是一个简单的kl解释程序,它用于执行一段kl代码。这个程序同之前
说的解释器不是同一回事。当我说到解释器时,它通常指的是klinterpret.c中实现的解释
模块,而解释器程序则指的是一个使用了kl库的独立解释器可执行程序。