《電子技術(shù)應(yīng)用》
您所在的位置:首頁(yè) > 嵌入式技術(shù) > 設(shè)計(jì)應(yīng)用 > 輕量級(jí)腳本引擎的設(shè)計(jì)與實(shí)現(xiàn)
輕量級(jí)腳本引擎的設(shè)計(jì)與實(shí)現(xiàn)
來源:微型機(jī)與應(yīng)用2011年第15期
張貝克,,符盛寶
(北京化工大學(xué) 安全科學(xué)與監(jiān)控工程中心,北京 100029)
摘要: 通過嵌入腳本引擎為應(yīng)用程序提供腳本支持是實(shí)現(xiàn)應(yīng)用程序可定制和可擴(kuò)展的有效方法,,但現(xiàn)存的腳本語(yǔ)言難于掌握,,引擎龐大使應(yīng)用程序的效率降低,。為了解決該問題,設(shè)計(jì)了語(yǔ)法簡(jiǎn)單易學(xué)的腳本語(yǔ)言Vblet,,實(shí)現(xiàn)了Vblet的輕量級(jí)腳本引擎,。該引擎支持腳本無(wú)縫地使用應(yīng)用程序?qū)崿F(xiàn)的類和函數(shù),并具有很好的性能,。
Abstract:
Key words :

摘  要: 通過嵌入腳本引擎為應(yīng)用程序提供腳本支持是實(shí)現(xiàn)應(yīng)用程序可定制可擴(kuò)展的有效方法,但現(xiàn)存的腳本語(yǔ)言難于掌握,,引擎龐大使應(yīng)用程序的效率降低,。為了解決該問題,設(shè)計(jì)了語(yǔ)法簡(jiǎn)單易學(xué)的腳本語(yǔ)言Vblet,,實(shí)現(xiàn)了Vblet的輕量級(jí)腳本引擎,。該引擎支持腳本無(wú)縫地使用應(yīng)用程序?qū)崿F(xiàn)的類和函數(shù),,并具有很好的性能。
關(guān)鍵詞: 腳本引擎,;腳本語(yǔ)言,;可擴(kuò)展;可定制,;二次開發(fā)

 腳本語(yǔ)言憑借強(qiáng)大的描述能力和靈活的語(yǔ)法結(jié)構(gòu),,使得為應(yīng)用程序提供腳本支持從而進(jìn)行混合語(yǔ)言開發(fā)成為實(shí)現(xiàn)可擴(kuò)展和可定制的有效方案[1]。出于穩(wěn)定性和開發(fā)時(shí)間限制的考慮,,開發(fā)人員傾向于嵌入現(xiàn)有腳本引擎的方法為應(yīng)用程序提供腳本支持,,如嵌入Python引擎為應(yīng)用程序提供Python腳本支持,或使用Microsoft提供的ActiveX Scripting技術(shù)為應(yīng)用程序嵌入VBScript引擎或JavaScript引擎提供相應(yīng)的腳本支持,。但是這樣方法靈活性較差,,應(yīng)用程序必須接受現(xiàn)有腳本引擎的體積和性能要求,這對(duì)運(yùn)行在低硬件條件下的應(yīng)用程序,,或者是只要求進(jìn)行簡(jiǎn)單規(guī)則計(jì)算的小型應(yīng)用程序來說,,這種方法在效率上沒有優(yōu)勢(shì)[2]。而且有些現(xiàn)有腳本語(yǔ)言比較難學(xué),,使得用戶把太多時(shí)間花在語(yǔ)言的學(xué)習(xí)上,。因此,需要一個(gè)輕型的腳本引擎,,能夠解釋運(yùn)行一門語(yǔ)法簡(jiǎn)單易學(xué)的腳本語(yǔ)言,,該腳本語(yǔ)言對(duì)于工程應(yīng)用領(lǐng)域的非正式程序員,可以經(jīng)過短時(shí)間的學(xué)習(xí)培訓(xùn)或者不經(jīng)過學(xué)習(xí)就能掌握并使用,。
 針對(duì)以上問題,,本文在自行設(shè)計(jì)的腳本語(yǔ)言Vblet的基礎(chǔ)上,開發(fā)實(shí)現(xiàn)了Vblet的輕型腳本引擎,,支持腳本引擎被嵌入在C++實(shí)現(xiàn)的應(yīng)用程序上,。Vblet語(yǔ)言語(yǔ)法簡(jiǎn)單,繼承了在非專業(yè)程序員中具有較高聲譽(yù)的VBA語(yǔ)言,,并且借鑒了Python語(yǔ)言的部分功能,,使得用戶能夠?qū)W⒂趩栴}的解決而不是語(yǔ)法的學(xué)習(xí)上。
1 腳本引擎概述
 腳本引擎[3]是一個(gè)加載,、解釋執(zhí)行腳本,,并負(fù)責(zé)與外界進(jìn)行交互的程序。腳本引擎一般很少獨(dú)立存在,,而是要嵌入應(yīng)用程序中以擴(kuò)展應(yīng)用程序的行為,,這個(gè)被嵌入腳本引擎的應(yīng)用程序稱為宿主程序。
 嵌入的腳本引擎如圖1所示,。圖1中,,腳本引擎通過某種交互接口,,根據(jù)腳本源程序描述的邏輯來控制應(yīng)用系統(tǒng)。根據(jù)宿主程序和腳本引擎之間的緊密層次不同,,可將通信方式分為:(1)基于二進(jìn)制接口的通信,。(2)基于公共運(yùn)行時(shí)環(huán)境的通信。(3)基于源碼接口的通信,。本文實(shí)現(xiàn)的基于源碼的交互接口,,即通信雙方基于共同的實(shí)現(xiàn)語(yǔ)言,在源代碼級(jí)上相互調(diào)用,。

 除了交互接口,,作為腳本的解釋運(yùn)行平臺(tái),腳本引擎包含了一個(gè)編譯器前端程序,,前端程序負(fù)責(zé)將腳本源代碼經(jīng)過詞法分析,、語(yǔ)法語(yǔ)義分析后生成字節(jié)碼格式的指令序列,然而這些指令序列是不能在目標(biāo)機(jī)器上執(zhí)行的,。因此,,在腳本引擎的最底層還要一個(gè)執(zhí)行字節(jié)碼指令的程序,這個(gè)程序即稱為虛擬機(jī),。
2 腳本語(yǔ)言的設(shè)計(jì)
 在開發(fā)實(shí)現(xiàn)腳本引擎之前,,先要確定引擎要解釋執(zhí)行的對(duì)象,即腳本語(yǔ)言,。本文設(shè)計(jì)的腳本語(yǔ)言Vblet是VBA(Visual Basic for Applications)的子集,,而VBA的語(yǔ)法簡(jiǎn)單易學(xué),在非專業(yè)程序員中有很大的用戶量,,享有很高的聲譽(yù),。Vblet簡(jiǎn)化了VBA語(yǔ)法,去掉了VBA語(yǔ)法中的一些限制,,此外還根據(jù)需要擴(kuò)展了部分功能,。下面是Vblet不同于VBA的一些重要的語(yǔ)法特性:
 (1)交互執(zhí)行。這是借鑒了Python語(yǔ)言交互執(zhí)行的語(yǔ)法特點(diǎn),,使得程序員可以單行執(zhí)行語(yǔ)句或計(jì)算表達(dá)式,,而不限于一定要把代碼封裝在代碼塊中。
 (2)不需要變量和參數(shù)聲明,。VBlet是動(dòng)態(tài)語(yǔ)言,,變量的類型由腳本引擎從上下文中確定,變量可以不經(jīng)過聲明就可以使用,。
 (3)去掉了部分運(yùn)算符,,比如冒號(hào)運(yùn)算符和逗號(hào)運(yùn)算符。
 為了提高性能,,Vblet去除了VBA中一些庫(kù)函數(shù)的支持,,只保留一些在工程應(yīng)用領(lǐng)域比較常用的數(shù)學(xué)計(jì)算函數(shù)。
3 腳本引擎實(shí)現(xiàn)方案
 Vblet引擎除了IDE的開發(fā)使用了MFC類庫(kù)之外,,其他模塊的實(shí)現(xiàn)都是使用標(biāo)準(zhǔn)C++編寫的,,這使得Vblet引擎只需要重新編寫IDE,或者修改小部分的核心代碼就能夠移植到其他平臺(tái)上,。
3.1 前端編譯程序的實(shí)現(xiàn)
 前端編譯程序?qū)⒛_本源程序的字符流經(jīng)過詞法分析,、語(yǔ)法分析和語(yǔ)義分析后,生成字節(jié)碼表示的指令流,,同時(shí)進(jìn)行語(yǔ)法檢查,,對(duì)語(yǔ)法錯(cuò)誤給出提示信息。另外,,為了支持?jǐn)帱c(diǎn)調(diào)試和異常信息顯示,,每行源程序和生成的字節(jié)碼指令的對(duì)應(yīng)關(guān)系也要在這里建立。
 前端編譯器一般可以通過一些自動(dòng)生成工具生成,,但是這些自動(dòng)生成的代碼效率都不夠高或者不好閱讀,,因此本文采用手寫的方式實(shí)現(xiàn)前端編譯程序。前端程序由Scanner類和Compiler類兩大主要模塊組成,。Scanner類主要負(fù)責(zé)源程序的詞法分析,,它根據(jù)規(guī)定的詞法規(guī)則把源程序拆分成詞法單元,并進(jìn)行詞法檢查,。Compiler類則充當(dāng)語(yǔ)法分析,、語(yǔ)義分析和字節(jié)碼生成,而且這三者一步完成,,中間不產(chǎn)生任何數(shù)據(jù),。另外,語(yǔ)法分析和語(yǔ)義分析出現(xiàn)的錯(cuò)誤由類Parse_error負(fù)責(zé)處理,。前端編譯器序列圖如圖2所示,。

 

 

 Vblet語(yǔ)法分析采用自頂向下的預(yù)測(cè)分析法,驅(qū)動(dòng)Scanner對(duì)象的token()成員函數(shù)為其產(chǎn)生一個(gè)詞法單元,,當(dāng)需要后退時(shí)使用Scanner對(duì)象的stoken()方法保存一個(gè)不符合當(dāng)前產(chǎn)生式規(guī)則的詞法單元,,以便下一個(gè)產(chǎn)生式規(guī)則的分析。類Compiler只包含一個(gè)public權(quán)限的成員函數(shù)Compile(),,當(dāng)Compile()被調(diào)用時(shí),,將生成的對(duì)應(yīng)編譯單元的字節(jié)碼序列和符號(hào)信息、常量數(shù)據(jù)封裝在ByteCode對(duì)象中,,并返回給被調(diào)用者,。
3.2 虛擬機(jī)的實(shí)現(xiàn)
 Vblet虛擬機(jī)是一個(gè)模擬的運(yùn)行時(shí)環(huán)境,是對(duì)Vblet腳本邏輯做出響應(yīng)的地方,。根據(jù)體系結(jié)構(gòu)的不同,,虛擬機(jī)分為如下兩種類型:(1)寄存器虛擬機(jī),;(2)堆棧虛擬機(jī)。寄存器虛擬機(jī)具有相對(duì)較高的執(zhí)行效率,,但實(shí)現(xiàn)機(jī)制復(fù)雜,。而堆棧虛擬機(jī)實(shí)現(xiàn)起來則相對(duì)比較簡(jiǎn)單,但是需要付出一定的性能代價(jià),。堆棧虛擬機(jī)由于Java和Python的成功而被證明它在模擬計(jì)算平臺(tái)上的優(yōu)勢(shì)[4-5],。因此,本文也將采用堆棧虛擬機(jī)作為Vblet腳本引擎的計(jì)算平臺(tái),。
 如圖3所示,,除了模擬處理器執(zhí)行字節(jié)碼指令外,Vblet虛擬機(jī)還包含了1個(gè)堆棧和PC,、SP,、FP 3個(gè)寄存器。堆棧是虛擬機(jī)的運(yùn)行時(shí)棧,,是函數(shù)調(diào)用和保存中間變量的地方,,是整個(gè)虛擬機(jī)最核心的數(shù)據(jù)結(jié)構(gòu)。程序計(jì)數(shù)器寄存器PC是記錄下一條要執(zhí)行指令的序號(hào),;棧頂寄存器SP是在堆棧變化的過程中保存堆棧的頂部位置,;幀指針寄存器FP則相當(dāng)于真實(shí)處理機(jī)的基址寄存器BX,保存當(dāng)前函數(shù)工作棧的棧底位置,。這些數(shù)據(jù)結(jié)構(gòu)表示了整個(gè)Vblet虛擬機(jī)的運(yùn)行時(shí)環(huán)境,。

 Vblet是一種動(dòng)態(tài)語(yǔ)言,所有數(shù)據(jù)類型的數(shù)據(jù)值在Vblet虛擬機(jī)中只以一種數(shù)據(jù)結(jié)構(gòu)VALUE存在,,VALUE的定義如下:
struct VALUE
{
        short int v_type,;//數(shù)據(jù)類型
        union V
        {
            bool v_bool;
            int v_integer,;
            double v_float,;
            string* v_string;//指向字符串類型數(shù)據(jù)
            ARRAY v_array,;
//指向數(shù)組數(shù)據(jù),,ARRAY是封裝VALUE數(shù)組的類
            CODE* v_code;
//指向字節(jié)碼,;CODE是封裝字節(jié)碼的類
            VlExtObject* v_extobj,;
//指向用戶注冊(cè)類的對(duì)象
        } v;
        …//操作VALUE和各種數(shù)據(jù)類型間轉(zhuǎn)換的函數(shù)
}
 在VALUE結(jié)構(gòu)體中,,成員v_type表示了當(dāng)前VALUE對(duì)象保存的數(shù)據(jù)類型,。VALUE不僅封裝了Vblet的所有基本數(shù)據(jù)類型,也封裝了數(shù)組和字節(jié)碼指令流等。實(shí)際上,,虛擬機(jī)的堆棧和SP,、FP寄存器所保存的就是VALUE對(duì)象或是VALUE對(duì)象的引用。
 Vblet虛擬機(jī)在執(zhí)行字節(jié)碼指令的過程中,,要經(jīng)常讀寫堆棧數(shù)據(jù),。因此,為了提高堆棧讀寫操作的速度,,從而提高虛擬機(jī)性能,在實(shí)現(xiàn)過程中,,定義了如下宏來進(jìn)行堆棧操作:
#define PUSH(v)     (*(++sp))=v    //將值v壓入棧頂
#define POP         (--sp)        //彈s出棧頂
#define PUSHN(n)     (sp+=(n)) //將n個(gè)未定義的值壓棧
#define POPN(n)     (sp-=(n))//從棧中彈出n個(gè)元素
#define SP(n)                   (*(sp-(n)))
//取棧頂一個(gè)第n個(gè)元素的值
#define FP(n)                 (*(fp-(n)))
//取幀基地址以下第n個(gè)元素的值
 Vitual虛擬機(jī)指令系統(tǒng)共有16條數(shù)據(jù)傳輸指令,、21條運(yùn)算指令、5條轉(zhuǎn)移指令和5條支持調(diào)試,、異常和錯(cuò)誤處理的指令,,而執(zhí)行指令的機(jī)構(gòu)——處理器則由函數(shù)interpret()來模擬。interpret()函數(shù)從指令序列中逐條取得操作碼指令,,根據(jù)操作碼的不同調(diào)用各自的處理函數(shù),。整個(gè)虛擬機(jī)的實(shí)現(xiàn)封裝在VVM類中。
3.3 集成接口的實(shí)現(xiàn)
 腳本引擎的集成接口是指將腳本引擎嵌入應(yīng)用程序中擴(kuò)展后者功能時(shí),,負(fù)責(zé)兩者之間通信的API,。所謂腳本引擎與應(yīng)用程序的通信,是指腳本引擎以動(dòng)態(tài)庫(kù)或靜態(tài)庫(kù)的形式被加載進(jìn)應(yīng)用程序中,,應(yīng)用程序向腳本引擎開放特定的全局函數(shù)和類及其屬性,、方法,使腳本引擎可以調(diào)用這些全局函數(shù),、創(chuàng)建這些類的實(shí)例,,并且通過該實(shí)例實(shí)現(xiàn)屬性的訪問和方法的調(diào)用。
 為了實(shí)現(xiàn)全局函數(shù)和類的注冊(cè),,需要一些數(shù)據(jù)結(jié)構(gòu)來表示注冊(cè)對(duì)象的信息,,以便腳本引擎能夠識(shí)別并使用注冊(cè)對(duì)象。相對(duì)來說,,描述函數(shù)的信息比較簡(jiǎn)單,,只需要一個(gè)函數(shù)名和一個(gè)函數(shù)指針,函數(shù)指針的定義如下:
 #define VALUE(*extfuncptr)(ARRAY)
 可見,,extfuncptr是指向以VALUE數(shù)組為參數(shù),、返回一個(gè)VALUE值的函數(shù),extfuncptr函數(shù)指針統(tǒng)一了參數(shù)類型,、個(gè)數(shù)和返回值類型不同的所有函數(shù)聲明,。因此,需要把用一個(gè)能夠被extfuncptr指向的全局函數(shù)將注冊(cè)函數(shù)“包裝”起來。在包裝函數(shù)中,,需要將Vblet腳本傳遞過來的VALUE參數(shù)轉(zhuǎn)換為C++數(shù)據(jù)類型的參數(shù),,并調(diào)用注冊(cè)函數(shù)取得C++數(shù)據(jù)類型的返回值,再將返回值轉(zhuǎn)換成VALUE值返回給腳本引擎,。
 一個(gè)類的類信息包括類名,、大小、初始化函數(shù)指針,、類注冊(cè)的方法和屬性,,用結(jié)構(gòu)體VLEXTCLASSINFO表示,如圖4所示,。

 此外,,還需要一些機(jī)制使得腳本引擎能夠引用腳本創(chuàng)建的注冊(cè)類的實(shí)例對(duì)象。把這個(gè)表示所有注冊(cè)類對(duì)象的“始祖”稱為VlExtObject,。VlExtObject包含一個(gè)表示引用計(jì)數(shù)的成員變量refcnt和兩個(gè)純虛函數(shù)GetExtClassInfo()和RegisterConstructor(),。任何應(yīng)用程序需要向腳本引擎注冊(cè)的自定義類都必須繼承自類VlExtObject,每個(gè)注冊(cè)類包含一個(gè)靜態(tài)的VLEXTCLASSINFO實(shí)例,,函數(shù)GetExtClassInfo()返回該VLEXTCLASSINFO實(shí)例,,函數(shù)RegisterConstructor()將向該VLEXTCLASSINFO實(shí)例指定類實(shí)例的初始化函數(shù)——構(gòu)造器的包裝函數(shù)。此外,,應(yīng)用程序注冊(cè)類還可以有自己的函數(shù)來向VLEXTCLASSINFO實(shí)例對(duì)象添加自己的方法和屬性,。在腳本引擎內(nèi)部,通過使用指針來操作注冊(cè)類的實(shí)例,。因此,,在腳本中當(dāng)這樣的對(duì)象被復(fù)制時(shí),實(shí)際上復(fù)制的是對(duì)象的指針,,并且該對(duì)象的引用計(jì)數(shù)refcnt加1,,而當(dāng)對(duì)象的引用在腳本中離開其作用域時(shí),并不立即銷毀對(duì)象,,而是將refcnt減1后如果為零才會(huì)使用delete將其銷毀,。
 整個(gè)腳本引擎被封裝成單件模式的CVbletEngine類中,該類包含了腳本引擎的啟動(dòng),、初始化,、腳本的運(yùn)行和停止等操作。CVbletEngine和集成接口的聲明對(duì)應(yīng)用程序可見,,而集成接口始終與腳本引擎的虛擬機(jī)部分連接,,在虛擬機(jī)指令集足夠完善的情況下,前端編譯器和集成接口的分離使得前端編譯器對(duì)應(yīng)用程序是透明的,,這樣,,當(dāng)需要增加腳本功能的時(shí)候,應(yīng)用程序可以不做修改。
4 與Python的性能比較
 為了測(cè)試Vblet腳本引擎是否達(dá)到輕量級(jí)的要求,,在Intel 586 PC機(jī)上用該腳本引擎解釋執(zhí)行如下這段Vblet腳本代碼:
function main(void)
  i=0
  a=121
  b=212
  while i<100000000
      a=a+b
      a=a-b
      a=a*b
      a=a/b
      i=i+1
  ewhile
end funtion
 同時(shí)在同一平臺(tái)上用Cpython2.6解釋執(zhí)行對(duì)應(yīng)的Python程序,,最后通過對(duì)兩者的初始化時(shí)間、編譯時(shí)間,、運(yùn)行時(shí)間,、內(nèi)存使用量和生成字節(jié)碼個(gè)數(shù)進(jìn)行了比較,結(jié)果如表1所示,。

 從表1可以看出,,雖然Vblet的編譯時(shí)間高于CPython,但是初始化時(shí)間要遠(yuǎn)遠(yuǎn)優(yōu)于Cpython,。這是因?yàn)镻ython包含了強(qiáng)大的功能模塊,,引擎運(yùn)行前需要加載這些模塊,并初始化復(fù)雜的運(yùn)行時(shí)環(huán)境和類型環(huán)境,。另外,由于Vblet的類型機(jī)制比Python簡(jiǎn)單,,虛擬機(jī)在數(shù)據(jù)的存取上比CPython更快,,即使生成的字節(jié)碼個(gè)數(shù)略多于CPython,也能達(dá)到更優(yōu)的運(yùn)行時(shí)間,。再加上Vblet在內(nèi)存上的優(yōu)勢(shì),,表明Vblet完全可以作為一個(gè)輕型的腳本引擎嵌入在應(yīng)用程序中。
 基于嵌入或擴(kuò)展腳本的混合語(yǔ)言編程是實(shí)現(xiàn)可定制工程應(yīng)用系統(tǒng)的有效方法,。本文通過借鑒VBA和Python的語(yǔ)法特點(diǎn)設(shè)計(jì)了語(yǔ)法簡(jiǎn)單易學(xué)的腳本語(yǔ)言Vblet,,設(shè)計(jì)并實(shí)現(xiàn)了Vblet的基于堆棧虛擬機(jī)的輕量級(jí)腳本引擎。測(cè)試結(jié)果表明,,腳本引擎能夠正確運(yùn)行Vblet代碼,,占有較小的內(nèi)存空間,在進(jìn)行簡(jiǎn)單的規(guī)則計(jì)算時(shí)具有明顯的執(zhí)行效率,。同時(shí),,腳本引擎對(duì)被嵌入C++應(yīng)用程序的支持,使得腳本能夠透明地使用應(yīng)用程序注冊(cè)的類和函數(shù),,從而達(dá)到增強(qiáng)應(yīng)用系統(tǒng)靈活性,、可定制性和擴(kuò)展性的目的。
參考文獻(xiàn)
[1] JOHN K. Ousterhout scripting: higher-level programming for the 21st century[J]. IEEE Computer Magazine,,1998,,31(3).
[2] XIE Q, LIU J,, CHOU P H. Tapper: a lightweight scripting engine for highly constrained wireless sensor nodes[C].Information Processing in Sensor Networks,, 2006. IPSN 2006. The Fifth International Conference on, 2006:342-349.
[3] Alex Varanese.Game Scripting Mastery[M]. Premier Press,2003.
[4] LINDHOLM T,, YELLIN F. Java virtual machine specification[M].Boston,, MA, USA:Addison-Wesley Longman Publishing Co.,, Inc,,1999.
[5] ALFRED R S, AHO V,, JEFFREY D. Ullman. compilers: principles,, techniques, and tools[M]. 2rd. Boston: Pearson/Addison Wesley,, 2007.
 

此內(nèi)容為AET網(wǎng)站原創(chuàng),,未經(jīng)授權(quán)禁止轉(zhuǎn)載。