第2章
關於條件編譯
首先,解釋一下,為什麼題目叫做:條件編譯。其實很簡單,現在這一章要分析的是,if、
while
、repeat
、for
語句。這些語句有個什麼特點呢?那就是,都要有條件判斷。根據條件判斷的結果,以決定是否執行,該如何執行。我不知道該怎麼稱呼這樣的語句,就一致稱其為:條件編譯。因為這一章的重點是研究,條件對於中間碼的生成造成的影響,以及如何生成中間碼以實現這種條件判斷。
首先分析
if then elseif then else then end
句型。
這個很簡單,實際上是
lua處理的很簡單。 當
lua遇到了
if這個關鍵字的時候,就開始呼叫
ifstat()
,首先,這是進入乙個塊。這個塊這個東西和函式是相同的重要,因為它關係到乙個變數的作用域的問題。塊由
block()
函式處理。不過在此之前,要注意乙個重要的事情,那就是條件判斷語句。
lua是怎麼處理條件判斷的呢?
首先,lua會跳過
if關鍵字,然後,會進入乙個十分常用的函式:
cond()
,這個函式還是比較複雜的,我現在要再看一遍這個函式,還是心有餘悸的。
cond()
這個函式比較短,我還是貼出來吧:
static
intcond (lexstate *ls)
第一步,用
expr()
這個函式讀取條件表示式,條件表示式是乙個式子,可以是以下幾種情況: 1、
常數:這種情況比較好辦,如果是
false
或nil
就是條件不通過,其他情況都是條件通過; 2、
另一種情況是非常數:也就是說,不是編譯期就能判斷是否為
true
或false
,要到執行期來判斷。
對於這兩種情況,分別分析,當然,首先是簡單的情況,常數。
當遇到常數的時候,
expr()
比較。因為它處理起來比較簡單,也就是通過
******exp()
直接處理。比如,這個常數是個數字
100,
******exp()
就會把這個表示式
v賦值為:
v->k = tk_number
,將v->u.nval = ls->t.seminfo.r
,也就是把
100給
v->u.nval
。如果這個常數是乙個字串,
******exp()
會將這個字串註冊進常量表,並將
v->k = tk_string
,v->u.s.info
賦值為這個字串在常量表中的位置。如果這個常量是
false,nil,true這3
個保留字,
******exp()
就會直接將
v->k
賦值成tk_false
,tk_nil
,tk_true。
然後,lua
會呼叫luak_goiftrue()
函式。這個函式就是生成判斷指令的函式了。
首先,這個函式會把要用到的變數
discharge
到空閒暫存器上去。也就是生成對應的載入變數的指令。由於現在我們研究的是常量,就不需要這一步,直接跳過。
然後,lua會判斷這個常量是
false
呢還是true
呢。如果是
true
,則將pc = no_jump
,這個pc
就是說,如果要生成指令的話,就會用這個
pc指向這個指令。如果是
fasle
,則會生成一條無條件跳轉指令:通過
luak_jump()
。這條指令會跳到哪呢?下面會有兩種情況: 1、
後面是elseif
,這時,就要跳到這條
elseif
前面,來做下一次條件判斷; 2、
後面是else
,這時,就跳到這條
else
前面或後面,因為
else
本身不生成指令。
怎麼實現跳轉到這個地方呢?當解析程式解析到
if語句開始時,如果要跳轉,就要跳過這個
if塊,到達下乙個
elseif
或else
程式塊。但是,解析程式怎麼知道下乙個
elseif
或else
塊的開頭位址在哪呢?當然,剛開始解析程式是不知道的。只有解析程式把這個
if塊解析完了,才能知道原來要跳轉到這裡。所以,之前要把要跳轉的那條指令的位置記錄下來,到解析完了
if塊後,再將那條指令跳轉的位置修正到此處。
lua中就是這麼實現的。
具體就是,
lua會通過
luak_pathtohere()
函式,將要跳轉的那條指令儲存在
fs->jpc
裡,這個
fs->jpc
就是指上次要跳轉的指令。但是,
lua不會立即將跳轉指令修改到這裡。因為這個
elseif
或else
塊中可能沒有**,也就是說,就算跳到這裡也是沒有的,不會執行任何**,這時,
lua就進行了優化,只有當
lua在這個塊裡生成**的時候,也就是在
luak_code()
函式裡,呼叫
dischargejpc()
,將儲存在
fs->jpc
裡的指令修改為跳到這個地方。
基本的跳轉原理就是這樣的
了。明白了這些後,再來看複雜條件的跳轉是怎麼實現的。也就是,在編譯期間並不知道條件的結果,到底是真還是假,也就是說,要到執行期間才知道到底要不要跳轉。假設我們現在研究這個例子: 例:
if a > b then end
這裡,就要回到
cond()
函式裡了。看
cond()
函式是怎麼處理
a > b
這種執行時才能出結果的情況。
首先,cond()
會通過呼叫
******exp()讀取a
,然後會讀取下乙個操作符,發現是
>
,這個時候,還不能生成**,因為這裡關係到乙個操作符優先順序的問題,要接著讀取後面的操作符,先把優先順序高的操作符處理後,才能處理低優先順序的操作符,所以,這裡就會進行遞迴讀取。在此之前,還有乙個問題,那就是,以邏輯操作符連線的式子,比如:
and,
or等,這些操作符的話,其實前面就是乙個式子,比如
and,如果是
a and b
,如果a
錯的話,後面的就不用測試了,如果
a 對的話,還要繼續測試
b,所以,在這裡,要為
a生成乙個測試及跳轉的語句,這裡就用到乙個函式
jumponcond()。
這個函式是怎麼實現的呢?這個函式其實是通過呼叫
condjump()
實現的。具體來說,很簡單,
condjump()
生成了2
個指令,第一條指令是判斷指令:
op_test
或者op_testset
,第二條指令是
op_jump
,也就是說,如果測試的結果是
false
,那麼,
op_test
不做任何事情,繼續執行,那麼就會執行到
op_jump
指令了,也就是
false
的時候不執行
if塊,直接跳轉,如果測試成功了呢,
op_test
會執行跳過下一條指令的操作,也就是跳過了
op_jump
,繼續執行
if塊。
這就是條件跳轉的基本實現原理。
現在回到
******exp()
中來,繼續我們的例子:
a > b
。當讀取了
>
好,會先用
luak_infix()
函式進行邏輯
and,
or這兩個邏輯運算的**生成。在這個例子中,沒有邏輯運算,於是,
lua會繼續。這個時候,像前面所說的,要進行操作符優先順序的運算,於是,進行遞迴
******exp()
這個函式,在遞迴的時候,發現後面只有乙個
b,那麼,就會讀取
b ,然後,就進入關鍵的地方了,
luak_posfix()
函式。這個函式會根據操作符的不同,生成操作指令,比如我們現在的
>
,就會通過
codecomp()
函式,生成
op_lt
指令。這個
op_lt
指令的執行其實進行了優化,也就是說,如果測試結果為
false
的話,也就是不通過,理論上是要進入下一條指令跳轉的,在執行期間,這兩條指令會在乙個指令週期裡執行,如果測試不通過,直接從下一條跳轉指令中取出要跳轉的位置,然後跳轉到那裡。如果測試成功的話,就會跳過下一天條跳轉指令,進入
if塊。
這就是條件跳轉**編譯的過程。
3 條件 迴圈
usr bin perl w use feature qw say 列印內容自動換行,不用手動加 n my var ifif var if else if var else if elsif 注意是elsif,不是elif,sv中也是elsif if var 10 elsif var 5 else ...
Lua 原始碼分析 lprefix h
lua 原始碼裡面很小的乙個標頭檔案,沒什麼東西,會預先進行一些設定,每個 c 檔案都會引用.ifndef lprefix h define lprefix h posix xsi if defined lua use c89 有關移植性 if defined xopen source define...
Lua 簡單Lua直譯器原始碼分析
include include include lua.h include lauxlib.h include lualib.h int main void lua close l return 0 lua.h定義了lua提供的基礎函式,包括建立lua環境 呼叫lua函式,它的定義是以lua 開頭的...