今天,腳本編譯器連同前段寫的虛擬機全部完工了,很有成就感。
跟 lua 一樣,復雜的數據類型我只支持了 table ,這個 table 即可以做 array 也可以做 hash_map 用。一般用 lua 的人都會用 table 去模擬 class 。lua 只對這個做了非常有限的擴展:在 lua 的文檔中,我們可以看到
function t.a.b.c:f (...) ... end 可以等同於 t.a.b.c.f = function (self, ...) ... end
就我用 lua 的經驗,這個轉換用途不是特別大,只是少寫個 self 而已。
這次我自己設計腳本語言,針對腳本支持 OO 的問題,特別做了些改進。
看一段腳本,這是用我自己定義的語言寫的:
A={};
function A.sum (_self)
{
return .a + .b;
}
function A.print (_self)
{
print(->sum());
return _self;
}
a={A,
.a=100,
.b=200,
};
a->print();
這是一個非常簡單的例子。全局函數 print 是我測試程序注冊的。我是這樣想的,把成員函數放到一張 table 裡。這裡是 A.sum 和 A.print 兩個,放進了 A 這個 table。 然後我創建了對象 a={A,.a=100,.b=200};
這裡默認 a[0] 放的是 vtbl, 就是 table 初始化隊列的第一項。 如果用 -> 來調用函數的話,比如 a->sum() 就在編譯時轉化成 a[0]->sum(a)
在函數裡,如果用 . 開頭的變量,就自動展開成 _self.x 。這個 _self 是函數的第一個參數。 需要在函數定義中明確寫出來,但是名字可以隨便。不過名字必須用 _ 開頭。 同理 ->sum() 就展開成 _self->sum()
所以,在一個成員函數中,我們可以看到所有以 . 開頭的變量操作都可以看成是成員變量。以 -> 開頭的函數調用,可以看成是成員函數調用。當然,這裡所有成員函數都和 C++ 中虛函數的概念相同。如果需要給 A 類寫個靜態函數,即不需要傳 self 的話,可以用 A.static_func() 這樣調用了。
我在這裡用名字來強制約定變量的類型。_ 開頭的全部是局部變量,否則就是全局。這樣可以方便很多編譯的工作,也讓代碼比較容易讀,在弱類型檢查的腳本裡,還可以避免一些筆失誤。