好久沒有寫程式了呢XD
說實話我一直掛記著BF編譯器的實行,也不斷想著要怎麼做到還可以接受的程度。不過,我想先作個小東東來練習下。
為了能夠有彈性的建立BF編譯器,就跟之前說的一樣,計畫要使用lua:用了lua,想要輸出成什麼樣的形式應該都可以達到。
最原始的構想中,我希望藉著lua程式碼來提供遭遇到某些虛擬碼時的應對方式。我的編譯器會先把BF程式轉換成虛擬碼,然後把虛擬碼送到編譯處理模組。
編譯處理模組會依據現在手上的虛擬碼來決定現在所要呼叫的lua程式,接著lua程式必須藉著傳回資料來告訴編譯處理模組應該要寫入的資料。然而這樣做的缺點便是lua編譯核心
只能夠提供特定型態的回傳資料,像是字串。
我們可以在編譯處理模組這端提供一組編譯時常常會用到的功能讓lua端使用。這樣的作法,我想可以大幅度的增強彈性。比方說,可以提供寫入二元資料以及字串資料的函數,或者其他 超乎現在的我所想像得到的功能。
由於想提供的功能還在規劃中,所以如果想要試試這個新技術,就只好隨便想個東西來開刀了。想來想去就想到...做個簡單的程式,提供簡單的繪圖功能,讓會lua的人可以使用它 來畫簡單的線條圖。換句話說,我的程式提供lua-繪圖功能的橋樑,讓使用者可以寫lua程式碼來產生圖檔。
為了讓程式簡潔,使用C語言,繪圖功能利用cairo提供,我的程式負責提供lua-cairo的橋樑,把lua程式碼讀進來並執行。程式放在這裡,歡迎自行參閱。
簡單的說,提供lua橋樑的話,要做到以下兩點
- 依照lua的期望提供正確的函數
- 告訴lua引擎這個函數的存在
所謂依照lua的期望,首先就是要有下面的函數原型(function prototype):
int function_name(lua_State* s);接著,利用
lua_toXXX(s, paramId)
來取得。paramId
從1開始。也就是說,
如果lua程式碼中有下面的陳述:
function_provided_by_c(a, b)那麼在C語言的部份就必須利用
lua_toXXX(s, 2)
來取得b的內容。XXX要填什麼取決於你對b的期望。
如果你認為b應該是可以轉成整數的資料,就用lua_tointeger(s, 2)
搞定輸入後我們來搞定輸出吧!利用C語言寫成的lua橋樑函數必須把資料堆到堆疊上,並且告訴lua直譯器這個橋樑函數所含有的回傳資料數量。
看個例子吧!如果lua函數func2
是由C語言寫成的橋樑函數func2_c
提供的,而且func2
回傳
2個資料,那麼func2_c
的最後幾行可能是:
lua_pushXXX(state, ret1); lua_pushXXX(state, ret2); return 2;這樣的話,對於下面的lua敘述
a, b = func2()
a
就會對應於ret1
,b
就會對應於ret2
。
最後要注意的一點就是,橋樑函數必須負起平衡lua堆疊的責任。在橋樑函數把控制權交回前,lua堆疊上必須只留下回傳值。也就是說,藉著lua堆疊傳入橋樑 函數的資料必須從堆疊上移除。Lua引擎對於堆疊的不平衡不見得會馬上抱怨,然而等到lua引擎因為堆疊爆滿而崩潰才來追蹤的時候往往會相當的棘手。(這點來說,lua 引擎和肝臟很相似呢!)
在寫好橋樑函數後,你必須告知lua引擎這個函數的存在。最簡便的方式是:
lua_pushcfunction(state, function_bridge_in_c); lua_setglobal(state, "func");接著就可以在lua程式中使用它(如下)
func()當然,我們也可以把許多橋樑函數包裝在一起:
lua_newtable(state); lua_pushstring(state, "func1"); lua_pushcfunction(state, function_bridge_in_c_1); lua_settable(state, -3); lua_pushstring(state, "func2"); lua_pushcfunction(state, function_bridge_in_c_2); lua_settable(state, -3); //... lua_setglobal(state, "bundle");那就可以在lua程式中這樣子:
bundle.func1() bundle.func2()或
bundle["func1"]() bundle["func2"]()
沒有留言:
張貼留言