2010年2月19日星期五

简单的数据脚本 - 篇二

这个...其实篇一不存在...只是觉得之前有一篇说简单的脚本解析,正好是内容上相衔接的。

其中内容涉及到算数表达式的解析,最基本的是加减乘除以及括号,要求是能对运算符的优先级正确处理。
在书TCPL中有一个很典型的范例,在表达式的文本中提取Token后,运用定义的几层不同级别的运算符处理函数间的调用,递归的进行多层表达式的运算。
函数的调用运用的了语言所实现的stack的使用,也可自己定义一个heap,表达式按又左至右处理,预读一个运算符,和之前的比较优先级来决定何处先执行运算。
更通常的方式是词法分析后,根据语法的定义产生优先级对应的解析树,再进行运算。
这是那篇中的相关内容,和下文只是目的有所不同。

数据有时需要单独的定义在一定格式的文本中,作用是
1.方便数据修改。这样不必为修改数据而重修改代码,免去编译时间,并减少修改时额外导致的错误。这样也实现了逻辑和数据之间的分离,而且有时候流程的逻辑也被划分为一种特殊的数据。
2.实现数据交换。固定格式的数据,可以被不同的程序或工具来处理。这既可以用来扩大数据的使用范围,也可以用来增加数据的处理手段。

数据文件的常见格式为二进制或文本文件。二进制文件一种简单的处理方式是通过C/Struct(以及其数组),可以很方便的在内存和文件之间读写。而Struct本身可用来放置不同的数据单元供使用,或者定义若干针对特定Struct的方法。变长类型或多层结构的定义,也可通过直接或间接的方法实现。不过这样定义的数据本身是不可执行的,但可以在程序中实现解析的方法。
相比而言使用文本的形式会更加友好和方便,至少是可以用通用的文本编辑器来查看和修改,进而文本数据的存放也有一些固定的格式比如csv,ini,xml,json。使用固定格式使人对文本结构可以有较直接的印象,而在实现上也有了通用的接口模块来减少实现和交换的成本。

在一个程序中提供插件或扩展,可以提供接口API或提供脚本引擎。
插件可以被编译为单独的dll或so,提供一个固定接口的初始化函数。我觉得geany和lua都在这种方式的实现上是简明实用的范例,而且都有详细独立的文档进行说明。
另一种就是解析一定格式的表示操作流程的文本,比如常见的BAT批处理文件,和一些程序中实现的宏语句。通常是被直接调用或触发来列出要执行的一系列操作,或得到需要的数据。
在这种文本在特性领域上的简明实用,而不是语言的通用或功能的完整。脚本自身特性的改变是随着程序的特性来定义的。(感觉自己在用的K-meleon也是不错的参考^_^,不过这类例子很广泛的说。)

上面扯远了,本是也要说一些具体的尝试的。
首先是类型与结构:因为是用C/C++实现的,所以就基于C类型了。
num(ber)类,包括整数int和浮点double两种数据储存的实现方式。str(ing),这是已知长度的char够成的二进制串。
结构上定义用vector/list等方式实现的sequence序列,以及map/hash这类用来实现的关联型数据。
除了和实现相关的,还可以定义:特殊类型空类型nil,和用以上基本实现的类型表示的布尔类bool/true/false,函数func(tion),对象obj(ect)。
函数可以用的序列表示,增加了可执行属性。此外有对stack的操作(至少是这种风格的吧)来实现函数参数与返回值的传入与返回,以及局部变量的环境。
对象可以用关联类型来表示,对象由数据(传统的Struct)和对其操作的成员方法(cpp中由函数和一个this指针来表示)构成,并可确定了自身实例于哪个类。对象关系可以是基于类继承,或也是基于原型继承的,即一个对象自身储存了指向自身原型的数据成员。
函数也可以看作包含invoke方法的对象,而自身存放局部变量的环境也可以用关联类型来实现。
(这个不同的方式貌似有很多种可能性的,再说一下,这里主要是参考了c/cpp,lisp,javascript,lua,dsl这些语言的风格)

实现代码中先是数据和结构的表示,下面实现的是通过解析文本来填充结构。
xml等类似的数据表示方式可以是直接接口和数据的。一个xml文件对应一个解析树固定的状态,可以使用接口来对这棵树操作而到一个新的状态。如果状态是只读的,解析树就只相当于XML结构的预处理了。
另一种用脚本来执行数据的定义操作,脚本自身有赋值或对象定义,同过一系列的操作获得一个新的状态,然后使用数据与结构相关的接口来获得数据或调用方法。
在自己使用的简单实现中,只提供了基本的词法解析。语句都按序列定义,为 对象+消息+参数 的形式。这样可以方便的使用运算式,但未表示优先级,需要定义多层的序列。
对于复杂类型可以通过脚本的执行来定义。为安全起见控制一个模块的作用范围和对次序的要求。

为以上的功能可以实现,包含了一个用于对解析树解析的模块。先简单的按对象分类然后根据消息判断操作的方法吧。不过动作加名词加副词的方式更传统和简明实用。

下面说lua的嵌入使用,这是自己实现脚本外的另一种选择。不过程序语言的组合使用和嵌入脚本BAT-Like是相似而不同的感觉。
1.在Lua中定义函数和数据供宿主使用(还涉及控制权是否交给脚本的问题,比如是否进行与用户间的交互)
2.编写模块做dll/so供lua使用
可以想到的实例嘛。。。

虽然有实现了脚本,但目前看其活动是随着宿主项目的。
题外:AVG脚本的双层结构感觉不错,json-like的纯数据做单独一个层。
恩...对于实现而言细节很重要的...全跳过了...
比如词法解析的方法啦,LUA接口的使用啦,扩展API的设计啦...
----
100319
QT内含了XML和JavaScript绑定

没有评论: