LUA教程
1 - 介紹
Lua 是一個擴展式程序設計語言,它被設計成支持通用的過程式編程,并有相關數據描述的設施。 Lua 也能對面向對象編程,函數式編程,數據驅動式編程提供很好的支持。它可以作為一個強大、輕量的腳本語言,供任何需要的程序使用。 Lua 以一個用 clean C 寫成的庫形式提供。(所謂 Clean C ,指的 ANSI C 和 C++ 中共通的一個子集)
作為一個擴展式語言,Lua 沒有 "main" 程序的概念:它只能 嵌入 一個宿主程序中工作,這個宿主程序被稱作 embedding program
或簡稱為 host
。宿主程序可以通過調用函數執行一小段 Lua 代碼,可以讀寫 Lua 變量,可以注入 C 函數讓 Lua 代碼調用。這些擴展的 C 函數,可以大大的擴展了 Lua 可以處理事務的領域,這樣就可以訂制出各種語言,而它們共享一個統一的句法格式的框架。 Lua 的官方發布版就包含了一個叫做 lua 的簡單的宿主程序,它用 Lua 庫提供了一個保證獨立的 Lua 解釋器。
Lua 是一個自由軟件,它的使用許可決定了對它的使用過程一般沒有任何保證。這份手冊中描述的東西的實現,可以在 Lua 的官方網站 http://www.lua.org/ 找到,
跟其它的許多參考手冊一樣,這份文檔有些地方比較枯燥。關于 Lua 的設計想法的探討,可以看看 Lua 網站上提供的技術論文。有關用 Lua 編程的細節介紹,可以讀一下 Roberto 的書,Programming in Lua (Second Edition)
2 - 語言
這一節從詞法、語法、句法上描述 Lua 。換句話說,這一節描述了哪些 token (符記)是有效的,它們如何被組合起來,這些組合方式有什么含義。
關于語言的構成概念將用常見的擴展 BNF 表達式寫出。也就是這個樣子: {a} 意思是 0 或多個 a , [a] 意思是一個可選的 a 。非最終的符號會保留原來的樣子,關鍵字則看起來像這樣 kword ,其它最終的符號則寫成 `=′ 。完整的 Lua 語法可以在本手冊最后找到。
2.1 - 詞法約定
Lua 中用到的 名字(也稱作 標識符)可以是任何非數字開頭的字母、數字、下劃線組成的字符串。這符合幾乎所有編程語言中關于名字的定義。(字母的定義依賴于當前環境:系統環境中定義的字母表中的字母都可以被用于標識符。)標識符用來命名變量,或作為表的域名。
下面的關鍵字是保留的,不能用作名字:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
Lua 是一個大小寫敏感的語言: and 是一個保留字,但是 And 和 AND 則是兩個不同的合法的名字。一般約定,以下劃線開頭連接一串大寫字母的名字(比如 _VERSION)被保留用于 Lua 內部全局變量。
下面這些是其它的 token :
+ - * / % ^ #
== ~= <= >= < > =
( ) { } [ ]
; : , . .. ...
字符串既可以用一對單引號引起,也可以是雙引號,里面還可以包含類似 C 的轉義符: '\a' (響鈴), '\b' (退格), '\f' (表單), '\n' (換行), '\r' (回車), '\t' (橫向制表), '\v' (縱向制表), '\\' (反斜杠), '\"' (雙引號),以及 '\'' (單引號)。而且,如果在一個反斜杠后跟了一個真正的換行符,其結果就是在字符串中產生一個換行符。我們還可以用反斜杠加數字的形式 \ddd 來描述一個字符。這里, ddd 是一串最多三位的十進制數字。(注意,如果需要在這種描述方法后接一個是數字的字符,那么反斜杠后必須寫滿三個數字。)Lua 中的字符串可以包含任何 8 位的值。包括用 '\0' 表示的零。
只有在你需要把不同的引號、換行、反斜杠、或是零結束符這些字符置入字符串時,你才必須使用轉義符。別的任何字符都可以直接寫在文本里。(一些控制符可以會影響文件系統造成某些問題,但是不會引起 Lua 的任何問題。)
字符串還可以用一種長括號括起來的方式定義。我們把兩個正的方括號間插入 n 個等號定義為第 n 級正長括號。就是說,0 級正的長括號寫作 [[ ,一級正的長括號寫作 [=[ ,如此等等。反的長擴展也作類似定義;舉個例子,4 級反的長括號寫作 ]====] 。一個長字符串可以由任何一級的正的長括號開始,而由第一個碰到的同級反的長括號結束。整個詞法分析過程將不受分行限制,不處理任何轉意符,并且忽略掉任何不同級別的長括號。這種方式描述的字符串可以包含任何東西,當然特定級別的反長括號除外。
另一個約定是,當正的長括號后面立即跟了一個換行符,這個換行符就不包含在這個字符串內。舉個例子,假設一個系統使用 ASCII 碼(這時,'a' 編碼為 97 ,換行符編碼為 10 ,'1' 編碼為 49 ),下面五種方式描述了完全相同的字符串:
a = 'alo\n123"'
a = "alo\n123\""
a = '\97lo\10\04923"'
a = [[alo
123"]]
a = [==[
alo
123"]==]
數字常量可以分兩部分寫,十進制底數部分和十進制的指數部分。指數部分是可選的。 Lua 也支持十六進制整數常量,只需要在前面加上前綴 0x 。下面是一些合法的數字常量的例子:
3 3.0 3.1416 314.16e-2 0.31416E1 0xff 0x56
注釋可以在除字符串內的任何地方是以兩橫 (--) 開始。如果跟在兩橫后面的不是一個長括號,這就是一個短注釋,它的作用范圍直到行末;否則就是一個長注釋,其作用范圍直到遇到反的長括號。長注釋通常被用來臨時屏蔽代碼塊。
2.2 - 值與類型
Lua 是一種 動態類型語言。這意味著變量沒有類型,只有值才有類型。語言中不存在類型定義。而所有的值本身攜帶它們自己的類型信息。
Lua 中的所有值都是一致 (first-class) 的。這意味著所有的值都可以被放在變量里,當作參數傳遞到另一個函數中,并被函數作為結果返回。
Lua 中有八種基本類型: nil, boolean, number, string, function, userdata, thread, and table. Nil 類型只有一種值 nil ,它的主要用途用于標表識和別的任何值的差異;通常,當需要描述一個無意義的值時會用到它。 Boolean 類型只有兩種值:false 和 true。 nil 和 false 都能導致條件為假;而另外所有的值都被當作真。 Number 表示實數(雙精度浮點數)。(編譯一個其它內部數字類型的 Lua 解釋器是件很容易的事;比如把內部數字類型改作單精度浮點數或長整型。參見文件 luaconf.h 。) String 表示一串字符的數組。 Lua 是 8-bit clean 的:字符串可以包含任何 8 位字符,包括零結束符 ('\0') (參見 §2.1)。
Lua 可以調用(和處理)用 Lua 寫的函數以及用 C 寫的函數(參見 §2.5.8).
userdata 類型用來將任意 C 數據保存在 Lua 變量中。這個類型相當于一塊原生的內存,除了賦值和相同性判斷,Lua 沒有為之預定義任何操作。然而,通過使用 metatable (元表) ,程序員可以為 userdata 自定義一組操作(參見 §2.8)。 userdata 不能在 Lua 中創建出來,也不能在 Lua 中修改。這樣的操作只能通過 C API。這一點保證了宿主程序完全掌管其中的數據。
thread 類型用來區別獨立的執行線程,它被用來實現 coroutine (協同例程)(參見 §2.11)。不要把 Lua 線程跟操作系統的線程搞混。 Lua 可以在所有的系統上提供對 coroutine 的支持,即使系統并不支持線程。
table 類型實現了一個關聯數組。也就是說,數組可以用任何東西(除了nil)做索引,而不限于數字。 table 可以以不同類型的值構成;它可以包含所有的類型的值(除 nil 外)。 table 是 lua 中唯一的一種數據結構;它可以用來描述原始的數組、符號表、集合、記錄、圖、樹、等等。用于表述記錄時,lua 使用域名作為索引。語言本身采用一種語法糖,支持以 a.name 的形式表示 a["name"]。有很多形式用于在 lua 中創建一個 table (參見 §2.5.7)。
跟索引一樣, table 每個域中的值也可以是任何類型(除 nil外)。特別的,因為函數本身也是值,所以 table 的域中也可以放函數。這樣 table 中就可以有一些 methods 了 (參見see §2.5.9)。
table, function ,thread ,和 (full) userdata 這些類型的值是所謂的對象:變量本身并不會真正的存放它們的值,而只是放了一個對對象的引用。賦值,參數傳遞,函數返回,都是對這些對象的引用進行操作;這些操作不會做暗地里做任何性質的拷貝。
庫函數 type 可以返回一個描述給定值的類型的字符串。
2.2.1 - 強制轉換
Lua 提供運行時字符串到數字的自動轉換。任何對字符串的數學運算操作都會嘗試用一般的轉換規則把這個字符串轉換成一個數字。相反,無論何時,一個數字需要作為字符串來使用時,數字都會以合理的格式轉換為字符串。需要完全控制數字怎樣轉換為字符串,可以使用字符串庫中的 format 函數(參見 string.format)。
2.3 - 變量
寫上變量的地方意味著當以其保存的值來替代之。 Lua 中有三類變量:全局變量,局部變量,還有 table 的域。
一個單一的名字可以表示一個全局變量,也可以表示一個局部變量 (或者是一個函數的參數,這是一種特殊形式的局部變量):
var ::= Name
Name 就是 §2.1 中所定義的標識符。
任何變量都被假定為全局變量,除非顯式的以 local 修飾定義 (參見 §2.4.7)。局部變量有其作用范圍:局部變量可以被定義在它作用范圍中的函數自由使用(參見 §2.6)。
在變量的首次賦值之前,變量的值均為 nil。
方括號被用來對 table 作索引:
var ::= prefixexp `[′ exp `]′
對全局變量以及 table 域之訪問的含義可以通過 metatable 來改變。以取一個變量下標指向的量 t 等價于調用 gettable_event(t,i)。(參見 §2.8 ,有一份完整的關于 gettable_event 函數的說明。這個函數并沒有在 lua 中定義出來,也不能在 lua 中調用。這里我們把它列出來只是方便說明。)
var.Name 這種語法只是一個語法糖,用來表示 var["Name"]:
var ::= prefixexp `.′ Name
所有的全局變量都是放在一個特定 lua table 的諸個域中,這個特定的 table 叫作 environment (環境)table 或者簡稱為 環境 (參見 §2.9)。每個函數都有對一個環境的引用,所以一個函數中可見的所有全局變量都放在這個函數所引用的環境表(environment table)中。當一個函數被創建出來,它會從創建它的函數中繼承其環境,你可以調用 getfenv 取得其環境。如果想改變環境,可以調用 setfenv。(對于 C 函數,你只能通過 debug 庫來改變其環境;參見 §5.9)。
對一個全局變量 x 的訪問等價于 _env.x,而這又可以等價于
gettable_event(_env, "x")
這里,_env 是當前運行的函數的環境。(函數 gettable_event 的完整說明參見 §2.8。這個函數并沒有在 lua 中定義出來,也不能調用。當然,_env 這個變量也同樣沒有在 Lua 中定義出來。我們在這里使用它們,僅僅只是方便解釋而已。)
2.4 - 語句段(Statement)
Lua 支持慣例形式的語句段,它和 Pascal 或是 C 很相象。這個集合包括賦值,控制結構,函數調用,還有變量聲明。
2.4.1 - Chunk(語句組)
Lua 的一個執行單元被稱作 chunk。一個 chunk 就是一串語句段,它們會被循序的執行。每個語句段可以以一個分號結束:
chunk ::= {stat [`;′]}
這兒不允許有空的語句段,所以 ';;' 是非法的。
lua 把一個 chunk 當作一個擁有不定參數的匿名函數(參見 §2.5.9)處理。正是這樣,chunk 內可以定義局部變量,接收參數,并且返回值。
chunk 可以被保存在一個文件中,也可以保存在宿主程序的一個字符串中。當一個 chunk 被執行,首先它會被預編譯成虛擬機中的指令序列,然后被虛擬機解釋運行這些指令。
chunk 也可以被預編譯成二進制形式;細節參考程序 luac。用源碼形式提供的程序和被編譯過的二進制形式的程序是可以相互替換的; Lua 會自動識別文件類型并做正確的處理。
2.4.2 - 語句塊
語句塊是一列語句段;從語法上來說,一個語句塊跟一個 chunk 相同:
block ::= chunk
一個語句塊可以被顯式的寫成一個單獨的語句段:
stat ::= do block end
顯式的語句塊對于控制變量的作用范圍很有用。有時候,顯式的語句塊被用來在另一個語句塊中插入 return 或是 break (參見 §2.4.4)。
2.4.3 - 賦值
Lua 允許多重賦值。因此,賦值的語法定義是等號左邊放一系列變量,而等號右邊放一系列的表達式。兩邊的元素都用逗號間開:
stat ::= varlist1 `=′ explist1
varlist1 ::= var {`,′ var}
explist1 ::= exp {`,′ exp}
表達式放在 §2.5 里討論。
在作賦值操作之前,那一系列的右值會被對齊到左邊變量需要的個數。如果右值比需要的更多的話,多余的值就被扔掉。如果右值的數量不夠需求,將會按所需擴展若干個 nil。如果表達式列表以一個函數調用結束,這個函數所返回的所有值都會在對齊操作之前被置入右值序列中。(除非這個函數調用被用括號括了起來;參見 §2.5)。
賦值段首先會做運算完所有的表達式,然后僅僅做賦值操作。因此,下面這段代碼
i = 3
i, a = i+1, 20
會把 a[3] 設置為 20,而不會影響到 a[4] 。這是因為 a 中的 i 在被賦值為 4 之前就被拿出來了(那時是 3 )。簡單說 ,這樣一行
x, y = y, x
可以用來交換 x 和 y 中的值。
對全局變量以及 table 中的域的賦值操作的含義可以通過 metatable 來改變。對變量下標指向的賦值,即 t = val 等價于 settable_event(t,i,val)。(關于函數 settable_event 的詳細說明,參見 §2.8。這個函數并沒有在 Lua 中定義出來,也不可以被調用。這里我們列出來,僅僅出于方便解釋的目的)
對于全局變量的賦值 x = val 等價于 _env.x = val,這個又可以等價于
settable_event(_env, "x", val)
這里,_env 指的是正在運行中的函數的環境。(變量 _env 并沒有在 Lua 中定義出來。我們僅僅出于解釋的目的在這里寫出來。)
2.4.4 - 控制結構
if、 while、以及 repeat 這些控制結構符合通常的意義,而且也有類似的語法:
stat ::= while exp do block end
stat ::= repeat block until exp
stat ::= if exp then block {elseif exp then block} [else block] end
Lua 也有一個 for 語句,它有兩種形式(參見 §2.4.5)。
控制結構中的條件表達式可以返回任何值。 false 和 nil 兩者都被認為是假條件。所有不同于 nil 和 false 的其它值都被認為是真(特別需要注意的是,數字 0 和空字符串也被認為是真)。
在 repeat–until 循環中,內部語句塊的結束點不是在 until 這個關鍵字處,它還包括了其后的條件表達式。因此,條件表達式中可以使用循環內部語句塊中的定義的局部變量。
return 被用于從函數或是 chunk(其實它就是一個函數)中返回值。 函數和 chunk 可以返回不只一個值,所以 return 的語法為
stat ::= return [explist1]
break 被用來結束 while、 repeat、或 for 循環,它將忽略掉循環中下面的語句段的運行:
stat ::= break
break 跳出最內層的循環。
return 和 break 只能被寫在一個語句塊的最后一句。如果你真的需要從語句塊的中間 return 或是 break ,你可以使用顯式的聲名一個內部語句塊。一般寫作 do return end 或是 do break end,可以這樣寫是因為現在 return 或 break 都成了一個語句塊的最后一句了。
2.4.5 - For 語句
for 有兩種形式:一種是數字形式,另一種是一般形式。
數字形式的 for 循環,通過一個數學運算不斷的運行內部的代碼塊。下面是它的語法:
stat ::= for Name `=′ exp `,′ exp [`,′ exp] do block end
block 將把 name 作循環變量。從第一個 exp 開始起,直到第二個 exp 的值為止,其步長為第三個 exp 。更確切的說,一個 for 循環看起來是這個樣子
for v = e1, e2, e3 do block end
這等價于代碼:
do
local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
if not (var and limit and step) then error() end
while (step > 0 and var <= limit) or (step <= 0 and var >= limit) do
local v = var
block
var = var + step
end
end
注意下面這幾點:
? 所有三個控制表達式都只被運算一次,表達式的計算在循環開始之前。這些表達式的結果必須是數字。
? var 、limit 、以及 step 都是一些不可見的變量。這里給它們起的名字都僅僅用于解釋方便。
? 如果第三個表達式(步長)沒有給出,會把步長設為 1 。
? 你可以用 break 來退出 for 循環。
? 循環變量 v 是一個循環內部的局部變量;當 for 循環結束后,你就不能在使用它。如果你需要這個值,在退出循環前把它賦給另一個變量。
一般形式的 for 通過一個叫作疊代器(iterators)的函數工作。每次疊代,疊代器函數都會被調用以產生一個新的值,當這個值為 nil 時,循環停止。一般形式的 for 循環的語法如下:
stat ::= for namelist in explist1 do block end
namelist ::= Name {`,′ Name}
for 語句好似這樣
for var_1, ???, var_n in explist do block end
它等價于這樣一段代碼:
do
local f, s, var = explist
while true do
local var_1, ???, var_n = f(s, var)
var = var_1
if var == nil then break end
block
end
end
注意以下幾點:
? explist 只會被計算一次。它返回三個值, 一個疊代器函數,一個狀態,一個疊代器的初始值。
? f、 s、 以及 var 都是不可見的變量。這里給它們起的名字都只是為了解說方便。
? 你可以使用 break 來跳出 for 循環。
? 循環變量 var_i 對于循環來說是一個局部變量;你不可以在 for 循環結束后繼續使用。如果你需要保留這些值,那么就在循環結束前賦值到別的變量里去。
2.4.6 - 把函數調用作為語句段
為了允許使用可能的副作用,函數調用可以被作為一個語句段執行:
stat ::= functioncall
在這種情況下,所有的返回值都被舍棄。函數調用在 §2.5.8 中解釋。
2.4.7 - 局部變量聲名
局部變量可以在語句塊中任何地方聲名。聲名可以包含一個初始化賦值操作:
stat ::= local namelist [`=′ explist1]
如果有的話,初始化賦值操作的行為等同于賦值操作(參見 §2.4.3)。否則,所有的變量將被初始化為 nil。
一個 chunk 同時也是一個語句塊(參見 §2.4.1),所以局部變量可以放在 chunk 中那些顯式注明的語句塊之外。這些局部變量的作用范圍從聲明起一直延伸到 chunk 末尾。
局部變量的可見規則在 §2.6 中解釋。
2.5 - 表達式
Lua 中有這些基本表達式:
exp ::= prefixexp
exp ::= nil | false | true
exp ::= Number
exp ::= String
exp ::= function
exp ::= tableconstructor
exp ::= `...′
exp ::= exp binop exp
exp ::= unop exp
prefixexp ::= var | functioncall | `(′ exp `)′
數字和字符串在 §2.1 中解釋;變量在 §2.3 中解釋;函數定義在 §2.5.9 中解釋;函數調用在 §2.5.8 中解釋; table 的構造在 §2.5.7 中解釋;可變參數的表達式寫作三個點 ('...') ,它只能被用在有可變參數的函數中;這些在 §2.5.9 中解釋。
二元操作符包含有數學運算操作符(參見 §2.5.1),比較操作符(參見 §2.5.2),邏輯操作符(參見 §2.5.3),以及連接操作符(參見 §2.5.4)。一元操作符包括負號(參見see §2.5.1),取反 not(參見 §2.5.3),和取長度操作符(參見 §2.5.5)。
函數調用和可變參數表達式都可以放在多重返回值中。如果表達式作為一個獨立語句段出現(參見 §2.4.6)(這只能是一個函數調用),它們的返回列表將被對齊到零個元素,也就是忽略所有返回值。如果表達式用于表達式列表的最后(或者是唯一)的元素,就不會有任何的對齊操作(除非函數調用用括號括起來)。在任何其它的情況下,Lua 將把表達式結果看成單一元素,忽略除第一個之外的任何值。
這里有一些例子:
f() -- 調整到 0 個結果
g(f(), x) -- f() 被調整到一個結果
g(x, f()) -- g 被傳入 x 加上所有 f() 的返回值
a,b,c = f(), x -- f() 被調整到一個結果 ( c 在這里被賦為 nil )
a,b = ... -- a 被賦值為可變參數中的第一個,
-- b 被賦值為第二個 (如果可變參數中并沒有對應的值,
-- 這里 a 和 b 都有可能被賦為 nil)
a,b,c = x, f() -- f() 被調整為兩個結果
a,b,c = f() -- f() 被調整為三個結果
return f() -- 返回 f() 返回的所有結果
return ... -- 返回所有從可變參數中接收來的值
return x,y,f() -- 返回 x, y, 以及所有 f() 的返回值
{f()} -- 用 f() 的所有返回值創建一個列表
{...} -- 用可變參數中的所有值創建一個列表
{f(), nil} -- f() 被調整為一個結果
被括號括起來的表達式永遠被當作一個值。所以, (f(x,y,z)) 即使 f 返回多個值,這個表達式永遠是一個單一值。((f(x,y,z)) 的值是 f 返回的第一個值。如果 f 不返回值的話,那么它的值就是 nil 。)
2.5.1 - 數學運算操作符
Lua 支持常見的數學運算操作符:二元操作 + (加法), - (減法),* (乘法), / (除法), % (取模),以及 ^ (冪);和一元操作 - (取負)。如果對數字操作,或是可以轉換為數字的字符串(參見 §2.2.1),所有這些操作都依賴它通常的含義。冪操作可以對任何冪值都正常工作。比如, x^(-0.5) 將計算出 x 的平方根。取模操作被定義為
a % b == a - math.floor(a/b)*b
這就是說,其結果是商相對負無窮圓整后的余數。(譯注:負數對正數取模的結果為正數)
2.5.2 - 比較操作符
Lua 中的比較操作符有
== ~= < > <= >=
這些操作的結果不是 false 就是 true。
等于操作 (==) 首先比較操作數的類型。如果類型不同,結果就是 false。否則,繼續比較值。數字和字符串都用常規的方式比較。對象 (table ,userdata ,thread ,以及函數)以引用的形式比較:兩個對象只有在它們指向同一個東西時才認為相等。每次你創建一個新對象(一個 table 或是 userdata ,thread 函數),它們都各不相同,即不同于上次創建的東西。
你可以改變 Lua 比較 table 和 userdata 的方式,這需要使用 "eq" 這個原方法(參見 §2.8)。
§2.2.1 中提及的轉換規則并不作用于比較操作。所以, "0"==0 等于 false,而且 t[0] 和 t["0"] 描述的是 table 中不同的域。
操作符 ~= 完全等價于 (==) 操作的反值。
大小比較操作以以下方式進行。如果參數都是數字,那么就直接做數字比較。否則,如果參數都是字符串,就用字符串比較的方式進行。再則,Lua 就試著調用 "lt" 或是 "le" 元方法(參見 §2.8)。
2.5.3 - 邏輯操作符
Lua 中的邏輯操作符有 and, or, 以及 not。和控制結構(參見 §2.4.4)一樣,所有的邏輯操作符把 false 和 nil 都作為假,而其它的一切都當作真。
取反操作 not 總是返回 false 或 true 中的一個。與操作符 and 在第一個參數為 false 或 nil 時返回這第一個參數;否則,and 返回第二個參數。或操作符 or 在第一個參數不為 nil 也不為 false 時,返回這第一個參數,否則返回第二個參數。 and 和 or 都遵循短路規則;也就是說,第二個操作數只在需要的時候去求值。這里有一些例子:
10 or 20 --> 10
10 or error() --> 10
nil or "a" --> "a"
nil and 10 --> nil
false and error() --> false
false and nil --> false
false or nil --> nil
10 and 20 --> 20
(在這本手冊中, --> 指前面表達式的結果。)
2.5.4 - 連接符
Lua 中字符串的連接操作符寫作兩個點 ('..')。如果兩個操作數都是字符串或都是數字,連接操作將以 §2.2.1 中提到的規則把其轉換為字符串。否則,會取調用元方法 "concat" (參見 §2.8)。
2.5.5 - 取長度操作符
取長度操作符寫作一元操作 #。字符串的長度是它的字節數(就是以一個字符一個字節計算的字符串長度)。
table t 的長度被定義成一個整數下標 n 。它滿足 t[n] 不是 nil 而 t[n+1] 為 nil;此外,如果 t[1] 為 nil ,n 就可能是零。對于常規的數組,里面從 1 到 n 放著一些非空的值的時候,它的長度就精確的為 n,即最后一個值的下標。如果數組有一個“空洞” (就是說,nil 值被夾在非空值之間),那么 #t 可能是任何一個是 nil 值的位置的下標(就是說,任何一個 nil 值都有可能被當成數組的結束)。
2.5.6 - 優先級
Lua 中操作符的優先級寫在下表中,從低到高優先級排序:
or
and
< > <= >= ~= ==
..
+ -
* / %
not # - (unary)
^
通常,你可以用括號來改變運算次序。連接操作符 ('..') 和冪操作 ('^') 是從右至左的。其它所有的操作都是從左至右。
2.5.7 - Table 構造
table 構造子是一個構造 table 的表達式。每次構造子被執行,都會構造出一個新的 table 。構造子可以被用來構造一個空的 table,也可以用來構造一個 table 并初始化其中的一些域。一般的構造子的語法如下
tableconstructor ::= `{′ [fieldlist] `}′
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= `[′ exp `]′ `=′ exp | Name `=′ exp | exp
fieldsep ::= `,′ | `;′
每個形如 [exp1] = exp2 的域向 table 中增加新的一項,其鍵值為 exp1 而值為 exp2。形如 name = exp 的域等價于 ["name"] = exp。最后,形如 exp 的域等價于 = exp , 這里的 i 是一個從 1 開始不斷增長的數字。這這個格式中的其它域不會破壞其記數。舉個例子:
a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
等價于
do
local t = {}
t[f(1)] = g
t[1] = "x" -- 1st exp
t[2] = "y" -- 2nd exp
t.x = 1 -- t["x"] = 1
t[3] = f(x) -- 3rd exp
t[30] = 23
t[4] = 45 -- 4th exp
a = t
end
如果表單中最后一個域的形式是 exp ,而且其表達式是一個函數調用或者是一個可變參數,那么這個表達式所有的返回值將連續的進入列表(參見 §2.5.8)。為了避免這一點,你可以用括號把函數調用(或是可變參數)括起來(參見 §2.5)。
初始化域表可以在最后多一個分割符,這樣設計可以方便由機器生成代碼。
2.5.8 - 函數調用
Lua 中的函數調用的語法如下:
functioncall ::= prefixexp args
函數調用時,第一步,prefixexp 和 args 先被求值。如果 prefixexp 的值的類型是 function,那么這個函數就被用給出的參數調用。否則 prefixexp 的元方法 "call" 就被調用,第一個參數就是 prefixexp 的值,跟下來的是原來的調用參數(參見 §2.8)。
這樣的形式
functioncall ::= prefixexp `:′ Name args
可以用來調用 "方法"。這是 Lua 支持的一種語法糖。像 v:name(args) 這個樣子,被解釋成 v.name(v,args),這里 v 只會被求值一次。
參數的語法如下:
args ::= `(′ [explist1] `)′
args ::= tableconstructor
args ::= String
所有參數的表達式求值都在函數調用之前。這樣的調用形式 f{fields} 是一種語法糖用于表示 f({fields});這里指參數列表是一個單一的新創建出來的列表。而這樣的形式 f'string' (或是 f"string" 亦或是 f[[string]])也是一種語法糖,用于表示 f('string');這里指參數列表是一個單獨的字符串。
因為表達式語法在 Lua 中比較自由,所以你不能在函數調用的 '(' 前換行。這個限制可以避免語言中的一些歧義。比如你這樣寫
a = f
(g).x(a)
Lua 將把它當作一個單一語句段, a = f(g).x(a) 。因此,如果你真的想作為成兩個語句段,你必須在它們之間寫上一個分號。如果你真的想調用 f,你必須從 (g) 前移去換行。
這樣一種調用形式:return functioncall 將觸發一個尾調用。 Lua 實現了適當的尾部調用(或是適當的尾遞歸):在尾調用中,被調用的函數重用調用它的函數的堆棧項。因此,對于程序執行的嵌套尾調用的層數是沒有限制的。然而,尾調用將刪除調用它的函數的任何調試信息。注意,尾調用只發生在特定的語法下,這時, return 只有單一函數調用作為參數;這種語法使得調用函數的結果可以精確返回。因此,下面這些例子都不是尾調用:
return (f(x)) -- 返回值被調整為一個
return 2 * f(x)
return x, f(x) -- 最加若干返回值
f(x); return -- 無返回值
return x or f(x) -- 返回值被調整為一個
2.5.9 - 函數定義
函數定義的語法如下:
function ::= function funcbody
funcbody ::= `(′ [parlist1] `)′ block end
另外定義了一些語法糖簡化函數定義的寫法:
stat ::= function funcname funcbody
stat ::= local function Name funcbody
funcname ::= Name {`.′ Name} [`:′ Name]
這樣的寫法:
function f () body end
被轉換成
f = function () body end
這樣的寫法:
function t.a.b.c.f () body end
被轉換成
t.a.b.c.f = function () body end
這樣的寫法:
local function f () body end
被轉換成
local f; f = function () body end
注意,并不是轉換成
local f = function () body end
(這個差別只在函數體內需要引用 f 時才有。)
一個函數定義是一個可執行的表達式,執行結果是一個類型為 function 的值。當 Lua 預編譯一個 chunk 的時候, chunk 作為一個函數,整個函數體也就被預編譯了。那么,無論何時 Lua 執行了函數定義,這個函數本身就被實例化了(或者說是關閉了)。這個函數的實例(或者說是 closure(閉包))是表達式的最終值。相同函數的不同實例有可能引用不同的外部局部變量,也可能擁有不同的環境表。
形參(函數定義需要的參數)是一些由實參(實際傳入參數)的值初始化的局部變量:
parlist1 ::= namelist [`,′ `...′] | `...′
當一個函數被調用,如果函數沒有被定義為接收不定長參數,即在形參列表的末尾注明三個點 ('...'),那么實參列表就會被調整到形參列表的長度,變長參數函數不會調整實參列表;取而代之的是,它將把所有額外的參數放在一起通過變長參數表達式傳遞給函數,其寫法依舊是三個點。這個表達式的值是一串實參值的列表,看起來就跟一個可以返回多個結果的函數一樣。如果一個變長參數表達式放在另一個表達式中使用,或是放在另一串表達式的中間,那么它的返回值就會被調整為單個值。若這個表達式放在了一系列表達式的最后一個,就不會做調整了(除非用括號給括了起來)。
我們先做如下定義,然后再來看一個例子:
function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
下面看看實參到形參數以及可變長參數的映射關系:
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4
f(r(), 10) a=1, b=10
f(r()) a=1, b=2
g(3) a=3, b=nil, ... --> (nothing)
g(3, 4) a=3, b=4, ... --> (nothing)
g(3, 4, 5, 8) a=3, b=4, ... --> 5 8
g(5, r()) a=5, b=1, ... --> 2 3
結果由 return 來返回(參見 §2.4.4)。如果執行到函數末尾依舊沒有遇到任何 return 語句,函數就不會返回任何結果。
冒號語法可以用來定義方法,就是說,函數可以有一個隱式的形參 self。因此,如下寫法:
function t.a.b.c:f (params) body end
是這樣一種寫法的語法糖:
t.a.b.c.f = function (self, params) body end
2.6 - 可視規則
Lua 是一個有詞法作用范圍的語言。變量的作用范圍開始于聲明它們之后的第一個語句段,結束于包含這個聲明的最內層語句塊的結束點。看下面這些例子:
x = 10 -- 全局變量
do -- 新的語句塊
local x = x -- 新的一個 'x', 它的值現在是 10
print(x) --> 10
x = x+1
do -- 另一個語句塊
local x = x+1 -- 又一個 'x'
print(x) --> 12
end
print(x) --> 11
end
print(x) --> 10 (取到的是全局的那一個)
注意這里,類似 local x = x 這樣的聲明,新的 x 正在被聲明,但是還沒有進入它的作用范圍,所以第二個 x 指向的是外面一層的變量。
因為有這樣一個詞法作用范圍的規則,所以可以在函數內部自由的定義局部變量并使用它們。當一個局部變量被更內層的函數中使用的時候,它被內層函數稱作 upvalue(上值),或是 外部局部變量。
注意,每次執行到一個 local 語句都會定義出一個新的局部變量。看看這樣一個例子:
a = {}
local x = 20
for i=1,10 do
local y = 0
a = function () y=y+1; return x+y end
end
這個循環創建了十個 closure(這指十個匿名函數的實例)。這些 closure 中的每一個都使用了不同的 y 變量,而它們又共享了同一份 x。
2.7 - 錯誤處理
因為 Lua 是一個嵌入式的擴展語言,所有的 Lua 動作都是從宿主程序的 C 代碼調用 Lua 庫(參見 lua_pcall)中的一個函數開始的。在 Lua 編譯或運行的任何時候發生了錯誤,控制權都會交還給 C ,而 C 可以來做一些恰當的措施(比如打印出一條錯誤信息)。
Lua 代碼可以顯式的調用 error 函數來產生一條錯誤。如果你需要在 Lua 中捕獲發生的錯誤,你可以使用 pcall 函數。
2.8 - Metatable(元表)
Lua 中的每個值都可以用一個 metatable。這個 metatable 就是一個原始的 Lua table ,它用來定義原始值在特定操作下的行為。你可以通過在 metatable 中的特定域設一些值來改變擁有這個 metatable 的值的指定操作之行為。舉例來說,當一個非數字的值作加法操作的時候, Lua 會檢查它的 metatable 中 "__add" 域中的是否有一個函數。如果有這么一個函數的話,Lua 調用這個函數來執行一次加法。
我們叫 metatable 中的鍵名為 事件 (event) ,把其中的值叫作 元方法 (metamethod)。在上個例子中,事件是 "add" 而元方法就是那個執行加法操作的函數。
你可以通過 getmetatable 函數來查詢到任何一個值的 metatable。
你可以通過 setmetatable 函數來替換掉 table 的 metatable 。你不能從 Lua 中改變其它任何類型的值的 metatable (使用 debug 庫例外);要這樣做的話必須使用 C API 。
每個 table 和 userdata 擁有獨立的 metatable (當然多個 table 和 userdata 可以共享一個相同的表作它們的 metatable);其它所有類型的值,每種類型都分別共享唯一的一個 metatable。因此,所有的數字一起只有一個 metatable ,所有的字符串也是,等等。
一個 metatable 可以控制一個對象做數學運算操作、比較操作、連接操作、取長度操作、取下標操作時的行為, metatable 中還可以定義一個函數,讓 userdata 作垃圾收集時調用它。對于這些操作,Lua 都將其關聯上一個被稱作事件的指定健。當 Lua 需要對一個值發起這些操作中的一個時,它會去檢查值中 metatable 中是否有對應事件。如果有的話,鍵名對應的值(元方法)將控制 Lua 怎樣做這個操作。
metatable 可以控制的操作已在下面列出來。每個操作都用相應的名字區分。每個操作的鍵名都是用操作名字加上兩個下劃線 '__' 前綴的字符串;舉例來說,"add" 操作的鍵名就是字符串 "__add"。這些操作的語義用一個 Lua 函數來描述解釋器如何執行更為恰當。
這里展示的用 Lua 寫的代碼僅作解說用;實際的行為已經硬編碼在解釋器中,其執行效率要遠高于這些模擬代碼。這些用于描述的的代碼中用到的函數( rawget , tonumber ,等等。)都可以在 §5.1 中找到。特別注意,我們使用這樣一個表達式來從給定對象中提取元方法
metatable(obj)[event]
這個應該被解讀作
rawget(getmetatable(obj) or {}, event)
這就是說,訪問一個元方法不再會觸發任何的元方法,而且訪問一個沒有 metatable 的對象也不會失敗(而只是簡單返回 nil)。
? "add": + 操作。
下面這個 getbinhandler 函數定義了 Lua 怎樣選擇一個處理器來作二元操作。首先,Lua 嘗試第一個操作數。如果這個東西的類型沒有定義這個操作的處理器,然后 Lua 會嘗試第二個操作數。
function getbinhandler (op1, op2, event)
return metatable(op1)[event] or metatable(op2)[event]
end
通過這個函數, op1 + op2 的行為就是
function add_event (op1, op2)
local o1, o2 = tonumber(op1), tonumber(op2)
if o1 and o2 then -- 兩個操作數都是數字?
return o1 + o2 -- 這里的 '+' 是原生的 'add'
else -- 至少一個操作數不是數字時
local h = getbinhandler(op1, op2, "__add")
if h then
-- 以兩個操作數來調用處理器
return h(op1, op2)
else -- 沒有處理器:缺省行為
error(???)
end
end
end
? "sub": - 操作。 其行為類似于 "add" 操作。
? "mul": * 操作。 其行為類似于 "add" 操作。
? "div": / 操作。 其行為類似于 "add" 操作。
? "mod": % 操作。 其行為類似于 "add" 操作,它的原生操作是這樣的 o1 - floor(o1/o2)*o2
? "pow": ^ (冪)操作。 其行為類似于 "add" 操作,它的原生操作是調用 pow 函數(通過 C math 庫)。
? "unm": 一元 - 操作。
? function unm_event (op)
? local o = tonumber(op)
? if o then -- 操作數是數字?
? return -o -- 這里的 '-' 是一個原生的 'unm'
? else -- 操作數不是數字。
? -- 嘗試從操作數中得到處理器
? local h = metatable(op).__unm
? if h then
? -- 以操作數為參數調用處理器
? return h(op)
? else -- 沒有處理器:缺省行為
? error(???)
? end
? end
? end
? "concat": .. (連接)操作,
? function concat_event (op1, op2)
? if (type(op1) == "string" or type(op1) == "number") and
? (type(op2) == "string" or type(op2) == "number") then
? return op1 .. op2 -- 原生字符串連接
? else
? local h = getbinhandler(op1, op2, "__concat")
? if h then
? return h(op1, op2)
? else
? error(???)
? end
? end
? end
? "len": # 操作。
? function len_event (op)
? if type(op) == "string" then
? return strlen(op) -- 原生的取字符串長度
? elseif type(op) == "table" then
? return #op -- 原生的取 table 長度
? else
? local h = metatable(op).__len
? if h then
? -- 調用操作數的處理器
? return h(op)
? else -- 沒有處理器:缺省行為
? error(???)
? end
? end
? end
關于 table 的長度參見 §2.5.5 。
? "eq": == 操作。 函數 getcomphandler 定義了 Lua 怎樣選擇一個處理器來作比較操作。元方法僅僅在參于比較的兩個對象類型相同且有對應操作相同的元方法時才起效。
? function getcomphandler (op1, op2, event)
? if type(op1) ~= type(op2) then return nil end
? local mm1 = metatable(op1)[event]
? local mm2 = metatable(op2)[event]
? if mm1 == mm2 then return mm1 else return nil end
? end
"eq" 事件按如下方式定義:
function eq_event (op1, op2)
if type(op1) ~= type(op2) then -- 不同的類型?
return false -- 不同的對象
end
if op1 == op2 then -- 原生的相等比較結果?
return true -- 對象相等
end
-- 嘗試使用元方法
local h = getcomphandler(op1, op2, "__eq")
if h then
return h(op1, op2)
else
return false
end
end
a ~= b 等價于 not (a == b) 。
? "lt": < 操作。
? function lt_event (op1, op2)
? if type(op1) == "number" and type(op2) == "number" then
? return op1 < op2 -- 數字比較
? elseif type(op1) == "string" and type(op2) == "string" then
? return op1 < op2 -- 字符串按逐字符比較
? else
? local h = getcomphandler(op1, op2, "__lt")
? if h then
? return h(op1, op2)
? else
? error(???);
? end
? end
? end
a > b 等價于 b < a.
? "le": <= 操作。
? function le_event (op1, op2)
? if type(op1) == "number" and type(op2) == "number" then
? return op1 <= op2 -- 數字比較
? elseif type(op1) == "string" and type(op2) == "string" then
? return op1 <= op2 -- 字符串按逐字符比較
? else
? local h = getcomphandler(op1, op2, "__le")
? if h then
? return h(op1, op2)
? else
? h = getcomphandler(op1, op2, "__lt")
? if h then
? return not h(op2, op1)
? else
? error(???);
? end
? end
? end
? end
a >= b 等價于 b <= a 。注意,如果元方法 "le" 沒有提供,Lua 就嘗試 "lt" ,它假定 a <= b 等價于 not (b < a) 。
? "index": 取下標操作用于訪問 table[key] 。
? function gettable_event (table, key)
? local h
? if type(table) == "table" then
? local v = rawget(table, key)
? if v ~= nil then return v end
? h = metatable(table).__index
? if h == nil then return nil end
? else
? h = metatable(table).__index
? if h == nil then
? error(???);
? end
? end
? if type(h) == "function" then
? return h(table, key) -- 調用處理器
? else return h[key] -- 或是重復上述操作
? end
? end
? "newindex": 賦值給指定下標 table[key] = value 。
? function settable_event (table, key, value)
? local h
? if type(table) == "table" then
? local v = rawget(table, key)
? if v ~= nil then rawset(table, key, value); return end
? h = metatable(table).__newindex
? if h == nil then rawset(table, key, value); return end
? else
? h = metatable(table).__newindex
? if h == nil then
? error(???);
? end
? end
? if type(h) == "function" then
? return h(table, key,value) -- 調用處理器
? else h[key] = value -- 或是重復上述操作
? end
? end
? "call": 當 Lua 調用一個值時調用。
? function function_event (func, ...)
? if type(func) == "function" then
? return func(...) -- 原生的調用
? else
? local h = metatable(func).__call
? if h then
? return h(func, ...)
? else
? error(???)
? end
? end
? end
2.9 - 環境
類型為 thread ,function ,以及 userdata 的對象,除了 metatable 外還可以用另外一個與之關聯的被稱作它們的環境的一個表,像 metatable 一樣,環境也是一個常規的 table ,多個對象可以共享同一個環境。
userdata 的環境在 Lua 中沒有意義。這個東西只是為了在程序員想把一個表關聯到一個 userdata 上時提供便利。
關聯在線程上的環境被稱作全局環境。全局環境被用作它其中的線程以及線程創建的非嵌套函數(通過 loadfile , loadstring 或是 load )的缺省環境。而且它可以被 C 代碼直接訪問(參見 §3.3)。
關聯在 C 函數上的環境可以直接被 C 代碼訪問(參見 §3.3)。它們會作為這個 C 函數中創建的其它函數的缺省環境。
關聯在 Lua 函數上的環境用來接管在函數內對全局變量(參見 §2.3)的所有訪問。它們也會作為這個函數內創建的其它函數的缺省環境。
你可以通過調用 setfenv 來改變一個 Lua 函數或是正在運行中的線程的環境。而想操控其它對象(userdata、C 函數、其它線程)的環境的話,就必須使用 C API 。
2.10 - 垃圾收集
Lua 提供了一個自動的內存管理。這就是說你不需要關心創建新對象的分配內存操作,也不需要在這些對象不再需要時的主動釋放內存。 Lua 通過運行一個垃圾收集器來自動管理內存,以此一遍又一遍的回收死掉的對象(這是指 Lua 中不再訪問的到的對象)占用的內存。 Lua 中所有對象都被自動管理,包括: table, userdata、 函數、線程、和字符串。
Lua 實現了一個增量標記清除的收集器。它用兩個數字來控制垃圾收集周期: garbage-collector pause 和 garbage-collector step multiplier 。
garbage-collector pause 控制了收集器在開始一個新的收集周期之前要等待多久。隨著數字的增大就導致收集器工作工作的不那么主動。小于 1 的值意味著收集器在新的周期開始時不再等待。當值為 2 的時候意味著在總使用內存數量達到原來的兩倍時再開啟新的周期。
step multiplier 控制了收集器相對內存分配的速度。更大的數字將導致收集器工作的更主動的同時,也使每步收集的尺寸增加。小于 1 的值會使收集器工作的非常慢,可能導致收集器永遠都結束不了當前周期。缺省值為 2 ,這意味著收集器將以內存分配器的兩倍速運行。
你可以通過在 C 中調用 lua_gc 或是在 Lua 中調用 collectgarbage 來改變這些數字。兩者都接受百分比數值(因此傳入參數 100 意味著實際值 1 )。通過這些函數,你也可以直接控制收集器(例如,停止或是重啟)。
2.10.1 - 垃圾收集的元方法
使用 C API ,你可以給 userdata (參見 §2.8)設置一個垃圾收集的元方法。這個元方法也被稱為結束子。結束子允許你用額外的資源管理器和 Lua 的內存管理器協同工作(比如關閉文件、網絡連接、或是數據庫連接,也可以說釋放你自己的內存)。
一個 userdata 可被回收,若它的 metatable 中有 __gc 這個域 ,垃圾收集器就不立即收回它。取而代之的是,Lua 把它們放到一個列表中。最收集結束后,Lua 針對列表中的每個 userdata 執行了下面這個函數的等價操作:
function gc_event (udata)
local h = metatable(udata).__gc
if h then
h(udata)
end
end
在每個垃圾收集周期的結尾,每個在當前周期被收集起來的 userdata 的結束子會以它們構造時的逆序依次調用。也就是說,收集列表中,最后一個在程序中被創建的 userdata 的結束子會被第一個調用。
2.10.2 - Weak Table(弱表)
weak table 是一個這樣的 table,它其中的元素都被弱引用。弱引用將被垃圾收集器忽略掉,換句話說,如果對一個對象的引用只有弱引用,垃圾收集器將回收這個對象。
weak table 的鍵和值都可以是 weak 的。如果一個 table 只有鍵是 weak 的,那么將運行收集器回收它們的鍵,但是會阻止回收器回收對應的值。而一個 table 的鍵和值都是 weak 時,就即允許收集器回收鍵又允許收回值。任何情況下,如果鍵和值中任一個被回收了,整個鍵值對就會從 table 中拿掉。 table 的 weak 特性可以通過在它的 metatable 中設置 __mode 域來改變。如果 __mode 域中是一個包含有字符 'k' 的字符串時, table 的鍵就是 weak 的。如果 __mode 域中是一個包含有字符 'v' 的字符串時, table 的值就是 weak 的。
在你把一個 table 當作一個 metatable 使用之后,就不能再修改 __mode 域的值。否則,受這個 metatable 控制的 table 的 weak 行為就成了未定義的。
2.11 - Coroutine (協同例程)
Lua 支持 coroutine ,這個東西也被稱為協同式多線程 (collaborative multithreading) 。 Lua 為每個 coroutine 提供一個獨立的運行線路。然而和多線程系統中的線程不同,coroutine 只在顯式的調用了 yield 函數時才會掛起。
創建一個 coroutine 需要調用一次 coroutine.create 。它只接收單個參數,這個參數是 coroutine 的主函數。 create 函數僅僅創建一個新的 coroutine 然后返回它的控制器(一個類型為 thread 的對象);它并不會啟動 coroutine 的運行。
當你第一次調用 coroutine.resume 時,所需傳入的第一個參數就是 coroutine.create 的返回值。這時,coroutine 從主函數的第一行開始運行。接下來傳入 coroutine.resume 的參數將被傳進 coroutine 的主函數。在 coroutine 開始運行后,它講運行到自身終止或是遇到一個 yields 。
coroutine 可以通過兩種方式來終止運行:一種是正常退出,指它的主函數返回(最后一條指令被運行后,無論有沒有顯式的返回指令); 另一種是非正常退出,它發生在未保護的錯誤發生的時候。第一種情況中, coroutine.resume 返回 true ,接下來會跟著 coroutine 主函數的一系列返回值。第二種發生錯誤的情況下, coroutine.resume 返回 false ,緊接著是一條錯誤信息。
coroutine 中切換出去,可以調用 coroutine.yield。當 coroutine 切出,與之配合的 coroutine.resume 就立即返回,甚至在 yield 發生在內層的函數調用中也可以(就是說,這不限于發生在主函數中,也可以是主函數直接或間接調用的某個函數里)。在 yield 的情況下,coroutine.resume 也是返回 true,緊跟著那些被傳入 coroutine.yield 的參數。等到下次你在繼續同樣的 coroutine ,將從調用 yield 的斷點處運行下去。斷點處 yield 的返回值將是 coroutine.resume 傳入的參數。
類似 coroutine.create , coroutine.wrap 這個函數也將創建一個 coroutine ,但是它并不返回 coroutine 本身,而是返回一個函數取而代之。一旦你調用這個返回函數,就會切入 coroutine 運行。所有傳入這個函數的參數等同于傳入 coroutine.resume 的參數。 coroutine.wrap 會返回所有應該由除第一個(錯誤代碼的那個布爾量)之外的由 coroutine.resume 返回的值。和 coroutine.resume 不同, coroutine.wrap 不捕獲任何錯誤;所有的錯誤都應該由調用者自己傳遞。
看下面這段代碼展示的一個例子:
function foo (a)
print("foo", a)
return coroutine.yield(2*a)
end
co = coroutine.create(function (a,b)
print("co-body", a, b)
local r = foo(a+1)
print("co-body", r)
local r, s = coroutine.yield(a+b, a-b)
print("co-body", r, s)
return b, "end"
end)
print("main", coroutine.resume(co, 1, 10))
print("main", coroutine.resume(co, "r"))
print("main", coroutine.resume(co, "x", "y"))
print("main", coroutine.resume(co, "x", "y"))
當你運行它,將得到如下輸出結果:
co-body 1 10
foo 2
main true 4
co-body r
main true 11 -9
co-body x y
main true 10 end
main false cannot resume dead coroutine
3 - 程序接口(API)
這個部分描述了 Lua 的 C API ,也就是宿主程序跟 Lua 通訊用的一組 C 函數。所有的 API 函數按相關的類型以及常量都聲明在頭文件 lua.h 中。
雖然我們說的是“函數”,但一部分簡單的 API 是以宏的形式提供的。所有的這些宏都只使用它們的參數一次(除了第一個參數,也就是 lua 狀態機),因此你不需擔心這些宏的展開會引起一些副作用。
在所有的 C 庫中,Lua API 函數都不去檢查參數的有效性和堅固性。然而,你可以在編譯 Lua 時加上打開一個宏開關來開啟 luaconf.h 文件中的宏 luai_apicheck 以改變這個行為。
3.1 - 堆棧
Lua 使用一個虛擬棧來和 C 傳遞值。棧上的的每個元素都是一個 Lua 值(nil,數字,字符串,等等)。
無論何時 Lua 調用 C,被調用的函數都得到一個新的棧,這個棧獨立于 C 函數本身的堆棧,也獨立于以前的棧。(譯注:在 C 函數里,用 Lua API 不能訪問到 Lua 狀態機中本次調用之外的堆棧中的數據)它里面包含了 Lua 傳遞給 C 函數的所有參數,而 C 函數則把要返回的結果也放入堆棧以返回給調用者(參見 lua_CFunction)。
方便起見,所有針對棧的 API 查詢操作都不嚴格遵循棧的操作規則。而是可以用一個索引來指向棧上的任何元素:正的索引指的是棧上的絕對位置(從一開始);負的索引則指從棧頂開始的偏移量。更詳細的說明一下,如果堆棧有 n 個元素,那么索引 1 表示第一個元素(也就是最先被壓入堆棧的元素)而索引 n 則指最后一個元素;索引 -1 也是指最后一個元素(即棧頂的元素),索引 -n 是指第一個元素。如果索引在 1 到棧頂之間(也就是,1 ≤ abs(index) ≤ top)我們就說這是個有效的索引。
3.2 - 堆棧尺寸
當你使用 Lua API 時,就有責任保證其堅固性。特別需要注意的是,你有責任控制不要堆棧溢出。你可以使用 lua_checkstack 這個函數來擴大可用堆棧的尺寸。
無論何時 Lua 調用 C ,它都只保證 LUA_MINSTACK 這么多的堆棧空間可以使用。 LUA_MINSTACK 一般被定義為 20 ,因此,只要你不是不斷的把數據壓棧,通常你不用關心堆棧大小。
所有的查詢函數都可以接收一個索引,只要這個索引是任何棧提供的空間中的值。棧能提供的最大空間是通過 lua_checkstack 來設置的。這些索引被稱作可接受的索引,通常我們把它定義為:
(index < 0 && abs(index) <= top) ||
(index > 0 && index <= stackspace)
注意,0 永遠都不是一個可接受的索引。(譯注:下文中凡提到的索引,沒有特別注明的話,都指可接受的索引。)
3.3 - 偽索引
除了特別聲明外,任何一個函數都可以接受另一種有效的索引,它們被稱作“偽索引”。這個可以幫助 C 代碼訪問一些并不在棧上的 Lua 值。偽索引被用來訪問線程的環境,函數的環境,注冊表,還有 C 函數的 upvalue (參見 §3.4)。
線程的環境(也就是全局變量放的地方)通常在偽索引 LUA_GLOBALSINDEX 處。正在運行的 C 函數的環境則放在偽索引 LUA_ENVIRONINDEX 之處。
你可以用常規的 table 操作來訪問和改變全局變量的值,只需要指定環境表的位置。舉例而言,要訪問全局變量的值,這樣做:
lua_getfield(L, LUA_GLOBALSINDEX, varname);
3.4 - C Closure
當 C 函數被創建出來,我們有可能會把一些值關聯在一起,也就是創建一個 C closure ;這些被關聯起來的值被叫做 upvalue ,它們可以在函數被調用的時候訪問的到。(參見 lua_pushcclosure)。
無論何時去調用 C 函數,函數的 upvalue 都被放在指定的偽索引處。我們可以用 lua_upvalueindex 這個宏來生成這些偽索引。第一個關聯到函數的值放在 lua_upvalueindex(1) 位置處,依次類推。任何情況下都可以用 lua_upvalueindex(n) 產生一個 upvalue 的索引,即使 n 大于實際的 upvalue 數量也可以。它都可以產生一個可接受但不一定有效的索引。
3.5 - 注冊表
Lua 提供了一個注冊表,這是一個預定義出來的表,可以用來保存任何 C 代碼想保存的 Lua 值。這個表可以用偽索引 LUA_REGISTRYINDEX 來定位。任何 C 庫都可以在這張表里保存數據,為了防止沖突,你需要特別小心的選擇鍵名。一般的用法是,你可以用一個包含你的庫名的字符串做為鍵名,或者可以取你自己 C 代碼中的一個地址,以 light userdata 的形式做鍵。
注冊表里的整數健被用于補充庫中實現的引用系統的工作,一般說來不要把它們用于別的用途。
3.6 - C 中的錯誤處理
在內部實現中,Lua 使用了 C 的 longjmp 機制來處理錯誤。(如果你使用 C++ 的話,也可以選擇換用異常;參見 luaconf.h 文件。)當 Lua 碰到任何錯誤(比如內存分配錯誤、類型錯誤、語法錯誤、還有一些運行時錯誤)它都會產生一個錯誤出去;也就是調用一個 long jump 。在保護環境下,Lua 使用 setjmp 來設置一個恢復點;任何發生的錯誤都會激活最近的一個恢復點。
幾乎所有的 API 函數都可能產生錯誤,例如內存分配錯誤。但下面的一些函數運行在保護環境中(也就是說它們創建了一個保護環境再在其中運行),因此它們不會產生錯誤出來: lua_newstate, lua_close, lua_load, lua_pcall, and lua_cpcall。
在 C 函數里,你也可以通過調用 lua_error 產生一個錯誤。
3.7 - 函數和類型
在這里我們按字母次序列出了所有 C API 中的函數和類型。
________________________________________
lua_Alloc
typedef void * (*lua_Alloc) (void *ud,
void *ptr,
size_t osize,
size_t nsize);
Lua 狀態機中使用的內存分配器函數的類型。內存分配函數必須提供一個功能類似于 realloc 但又不完全相同的函數。它的參數有 ud ,一個由 lua_newstate 傳給它的指針; ptr ,一個指向已分配出來或是將被重新分配或是要釋放的內存塊指針; osize ,內存塊原來的尺寸; nsize ,新內存塊的尺寸。如果且只有 osize 是零時,ptr 為 NULL 。當 nsize 是零,分配器必須返回 NULL;如果 osize 不是零,分配器應當釋放掉 ptr 指向的內存塊。當 nsize 不是零,若分配器不能滿足請求時,分配器返回 NULL 。當 nsize 不是零而 osize 是零時,分配器應該和 malloc 有相同的行為。當 nsize 和 osize 都不是零時,分配器則應和 realloc 保持一樣的行為。 Lua 假設分配器在 osize >= nsize 時永遠不會失敗。
這里有一個簡單的分配器函數的實現。這個實現被放在補充庫中,由 luaL_newstate 提供。
static void *l_alloc (void *ud, void *ptr, size_t osize,
size_t nsize) {
(void)ud; (void)osize; /* not used */
if (nsize == 0) {
free(ptr);
return NULL;
}
else
return realloc(ptr, nsize);
}
這段代碼假設 free(NULL) 啥也不影響,而且 realloc(NULL, size) 等價于 malloc(size)。這兩點是 ANSI C 保證的行為。
________________________________________
lua_atpanic
lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);
設置一個新的 panic (恐慌) 函數,并返回前一個。
如果在保護環境之外發生了任何錯誤, Lua 就會調用一個 panic 函數,接著調用 exit(EXIT_FAILURE),這樣就開始退出宿主程序。你的 panic 函數可以永遠不返回(例如作一次長跳轉)來避免程序退出。
panic 函數可以從棧頂取到出錯信息。
lua_call
void lua_call (lua_State *L, int nargs, int nresults);
調用一個函數。
要調用一個函數請遵循以下協議:首先,要調用的函數應該被壓入堆棧;接著,把需要傳遞給這個函數的參數按正序壓棧;這是指第一個參數首先壓棧。最后調用一下 lua_call; nargs 是你壓入堆棧的參數個數。當函數調用完畢后,所有的參數以及函數本身都會出棧。而函數的返回值這時則被壓入堆棧。返回值的個數將被調整為 nresults 個,除非 nresults 被設置成 LUA_MULTRET。在這種情況下,所有的返回值都被壓入堆棧中。 Lua 會保證返回值都放入棧空間中。函數返回值將按正序壓棧(第一個返回值首先壓棧),因此在調用結束后,最后一個返回值將被放在棧頂。
被調用函數內發生的錯誤將(通過 longjmp)一直上拋。
下面的例子中,這行 Lua 代碼等價于在宿主程序用 C 代碼做一些工作:
a = f("how", t.x, 14)
這里是 C 里的代碼:
lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* 將調用的函數 */
lua_pushstring(L, "how"); /* 第一個參數 */
lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table 的索引 */
lua_getfield(L, -1, "x"); /* 壓入 t.x 的值(第 2 個參數)*/
lua_remove(L, -2); /* 從堆棧中移去 't' */
lua_pushinteger(L, 14); /* 第 3 個參數 */
lua_call(L, 3, 1); /* 調用 'f',傳入 3 個參數,并索取 1 個返回值 */
lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* 設置全局變量 'a' */
注意上面這段代碼是“平衡”的:到了最后,堆棧恢復成原由的配置。這是一種良好的編程習慣。
________________________________________
lua_CFunction
typedef int (*lua_CFunction) (lua_State *L);
C 函數的類型。
為了正確的和 Lua 通訊,C 函數必須使用下列定義了參數以及返回值傳遞方法的協議: C 函數通過 Lua 中的堆棧來接受參數,參數以正序入棧(第一個參數首先入棧)。因此,當函數開始的時候, lua_gettop(L) 可以返回函數收到的參數個數。第一個參數(如果有的話)在索引 1 的地方,而最后一個參數在索引 lua_gettop(L) 處。當需要向 Lua 返回值的時候,C 函數只需要把它們以正序壓到堆棧上(第一個返回值最先壓入),然后返回這些返回值的個數。在這些返回值之下的,堆棧上的東西都會被 Lua 丟掉。和 Lua 函數一樣,從 Lua 中調用 C 函數也可以有很多返回值。
下面這個例子中的函數將接收若干數字參數,并返回它們的平均數與和:
static int foo (lua_State *L) {
int n = lua_gettop(L); /* 參數的個數 */
lua_Number sum = 0;
int i;
for (i = 1; i <= n; i++) {
if (!lua_isnumber(L, i)) {
lua_pushstring(L, "incorrect argument");
lua_error(L);
}
sum += lua_tonumber(L, i);
}
lua_pushnumber(L, sum/n); /* 第一個返回值 */
lua_pushnumber(L, sum); /* 第二個返回值 */
return 2; /* 返回值的個數 */
}
________________________________________
lua_checkstack
int lua_checkstack (lua_State *L, int extra);
確保堆棧上至少有 extra 個空位。如果不能把堆棧擴展到相應的尺寸,函數返回 false 。這個函數永遠不會縮小堆棧;如果堆棧已經比需要的大了,那么就放在那里不會產生變化。
________________________________________
lua_close
void lua_close (lua_State *L);
銷毀指定 Lua 狀態機中的所有對象(如果有垃圾收集相關的元方法的話,會調用它們),并且釋放狀態機中使用的所有動態內存。在一些平臺上,你可以不必調用這個函數,因為當宿主程序結束的時候,所有的資源就自然被釋放掉了。另一方面,長期運行的程序,比如一個后臺程序或是一個 web 服務器,當不再需要它們的時候就應該釋放掉相關狀態機。這樣可以避免狀態機擴張的過大。
________________________________________
lua_concat
void lua_concat (lua_State *L, int n);
連接棧頂的 n 個值,然后將這些值出棧,并把結果放在棧頂。如果 n 為 1 ,結果就是一個字符串放在棧上(即,函數什么都不做);如果 n 為 0 ,結果是一個空串。 連接依照 Lua 中創建語義完成(參見 §2.5.4 )。
________________________________________
lua_cpcall
int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);
以保護模式調用 C 函數 func 。 func 只有能從堆棧上拿到一個參數,就是包含有 ud 的 light userdata。當有錯誤時, lua_cpcall 返回和 lua_pcall 相同的錯誤代碼,并在棧頂留下錯誤對象;否則它返回零,并不會修改堆棧。所有從 func 內返回的值都會被扔掉。
________________________________________
lua_createtable
void lua_createtable (lua_State *L, int narr, int nrec);
創建一個新的空 table 壓入堆棧。這個新 table 將被預分配 narr 個元素的數組空間以及 nrec 個元素的非數組空間。當你明確知道表中需要多少個元素時,預分配就非常有用。如果你不知道,可以使用函數 lua_newtable。
________________________________________
lua_dump
int lua_dump (lua_State *L, lua_Writer writer, void *data);
把函數 dump 成二進制 chunk 。函數接收棧頂的 Lua 函數做參數,然后生成它的二進制 chunk 。若被 dump 出來的東西被再次加載,加載的結果就相當于原來的函數。當它在產生 chunk 的時候,lua_dump 通過調用函數 writer (參見 lua_Writer)來寫入數據,后面的 data 參數會被傳入 writer 。
最后一次由寫入器 (writer) 返回值將作為這個函數的返回值返回; 0 表示沒有錯誤。
這個函數不會把 Lua 返回彈出堆棧。
________________________________________
lua_equal
int lua_equal (lua_State *L, int index1, int index2);
如果依照 Lua 中 == 操作符語義,索引 index1 和 index2 中的值相同的話,返回 1 。否則返回 0 。如果任何一個索引無效也會返回 0。
________________________________________
lua_error
int lua_error (lua_State *L);
產生一個 Lua 錯誤。錯誤信息(實際上可以是任何類型的 Lua 值)必須被置入棧頂。這個函數會做一次長跳轉,因此它不會再返回。(參見 luaL_error)。
________________________________________
lua_gc
int lua_gc (lua_State *L, int what, int data);
控制垃圾收集器。
這個函數根據其參數 what 發起幾種不同的任務:
? LUA_GCSTOP: 停止垃圾收集器。
? LUA_GCRESTART: 重啟垃圾收集器。
? LUA_GCCOLLECT: 發起一次完整的垃圾收集循環。
? LUA_GCCOUNT: 返回 Lua 使用的內存總量(以 K 字節為單位)。
? LUA_GCCOUNTB: 返回當前內存使用量除以 1024 的余數。
? LUA_GCSTEP: 發起一步增量垃圾收集。步數由 data 控制(越大的值意味著越多步),而其具體含義(具體數字表示了多少)并未標準化。如果你想控制這個步數,必須實驗性的測試 data 的值。如果這一步結束了一個垃圾收集周期,返回返回 1 。
? LUA_GCSETPAUSE: 把 data/100 設置為 garbage-collector pause 的新值(參見 §2.10)。函數返回以前的值。
? LUA_GCSETSTEPMUL: 把 arg/100 設置成 step multiplier (參見 §2.10)。函數返回以前的值。
________________________________________
lua_getallocf
lua_Alloc lua_getallocf (lua_State *L, void **ud);
返回給定狀態機的內存分配器函數。如果 ud 不是 NULL ,Lua 把調用 lua_newstate 時傳入的那個指針放入 *ud 。
________________________________________
lua_getfenv
void lua_getfenv (lua_State *L, int index);
把索引處值的環境表壓入堆棧。
________________________________________
lua_getfield
void lua_getfield (lua_State *L, int index, const char *k);
把 t[k] 值壓入堆棧,這里的 t 是指有效索引 index 指向的值。在 Lua 中,這個函數可能觸發對應 "index" 事件的元方法(參見 §2.8)。
________________________________________
lua_getglobal
void lua_getglobal (lua_State *L, const char *name);
把全局變量 name 里的值壓入堆棧。這個是用一個宏定義出來的:
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)
________________________________________
lua_getmetatable
int lua_getmetatable (lua_State *L, int index);
把給定索引指向的值的元表壓入堆棧。如果索引無效,或是這個值沒有元表,函數將返回 0 并且不會向棧上壓任何東西。
________________________________________
lua_gettable
void lua_gettable (lua_State *L, int index);
把 t[k] 值壓入堆棧,這里的 t 是指有效索引 index 指向的值,而 k 則是棧頂放的值。
這個函數會彈出堆棧上的 key (把結果放在棧上相同位置)。在 Lua 中,這個函數可能觸發對應 "index" 事件的元方法(參見 §2.8)。
________________________________________
lua_gettop
int lua_gettop (lua_State *L);
返回棧頂元素的索引。因為索引是從 1 開始編號的,所以這個結果等于堆棧上的元素個數(因此返回 0 表示堆棧為空)。
________________________________________
lua_insert
void lua_insert (lua_State *L, int index);
把棧頂元素插入指定的有效索引處,并依次移動這個索引之上的元素。不要用偽索引來調用這個函數,因為偽索引不是真正指向堆棧上的位置。
________________________________________
lua_Integer
typedef ptrdiff_t lua_Integer;
這個類型被用于 Lua API 接收整數值。
缺省時這個被定義為 ptrdiff_t ,這個東西通常是機器能處理的最大整數類型。
________________________________________
lua_isboolean
int lua_isboolean (lua_State *L, int index);
當給定索引的值類型為 boolean 時,返回 1 ,否則返回 0 。
________________________________________
lua_iscfunction
int lua_iscfunction (lua_State *L, int index);
當給定索引的值是一個 C 函數時,返回 1 ,否則返回 0 。
________________________________________
lua_isfunction
int lua_isfunction (lua_State *L, int index);
當給定索引的值是一個函數( C 或 Lua 函數均可)時,返回 1 ,否則返回 0 。
________________________________________
lua_islightuserdata
int lua_islightuserdata (lua_State *L, int index);
當給定索引的值是一個 light userdata 時,返回 1 ,否則返回 0 。
________________________________________
lua_isnil
int lua_isnil (lua_State *L, int index);
當給定索引的值是 nil 時,返回 1 ,否則返回 0 。
________________________________________
lua_isnumber
int lua_isnumber (lua_State *L, int index);
當給定索引的值是一個數字,或是一個可轉換為數字的字符串時,返回 1 ,否則返回 0 。
________________________________________
lua_isstring
int lua_isstring (lua_State *L, int index);
當給定索引的值是一個字符串或是一個數字(數字總能轉換成字符串)時,返回 1 ,否則返回 0 。
________________________________________
lua_istable
int lua_istable (lua_State *L, int index);
當給定索引的值是一個 table 時,返回 1 ,否則返回 0 。
________________________________________
lua_isthread
int lua_isthread (lua_State *L, int index);
當給定索引的值是一個 thread 時,返回 1 ,否則返回 0 。
________________________________________
lua_isuserdata
int lua_isuserdata (lua_State *L, int index);
當給定索引的值是一個 userdata (無論是完整的 userdata 還是 light userdata )時,返回 1 ,否則返回 0 。
________________________________________
lua_lessthan
int lua_lessthan (lua_State *L, int index1, int index2);
如果索引 index1 處的值小于索引 index2 處的值時,返回 1 ;否則返回 0 。其語義遵循 Lua 中的 < 操作符(就是說,有可能調用元方法)。如果任何一個索引無效,也會返回 0 。
________________________________________
lua_load
int lua_load (lua_State *L,
lua_Reader reader,
void *data,
const char *chunkname);
加載一個 Lua chunk 。如果沒有錯誤, lua_load 把一個編譯好的 chunk 作為一個 Lua 函數壓入堆棧。否則,壓入出錯信息。 lua_load 的返回值可以是:
? 0: 沒有錯誤;
? LUA_ERRSYNTAX: 在預編譯時碰到語法錯誤;
? LUA_ERRMEM: 內存分配錯誤。
這個函數僅僅加栽 chunk ;而不會去運行它。
lua_load 會自動檢測 chunk 是文本的還是二進制的,然后做對應的加載操作(參見程序 luac)。
lua_load 函數使用一個用戶提供的 reader 函數來讀取 chunk (參見 lua_Reader)。 data 參數會被傳入讀取器函數。
chunkname 這個參數可以賦予 chunk 一個名字,這個名字被用于出錯信息和調試信息(參見 §3.8)。
________________________________________
lua_newstate
lua_State *lua_newstate (lua_Alloc f, void *ud);
創建的一個新的獨立的狀態機。如果創建不了(因為內存問題)返回 NULL 。參數 f 是一個分配器函數; Lua 將通過這個函數做狀態機內所有的內存分配操作。第二個參數 ud ,這個指針將在每次調用分配器時被直接傳入。
________________________________________
lua_newtable
void lua_newtable (lua_State *L);
創建一個空 table ,并將之壓入堆棧。它等價于 lua_createtable(L, 0, 0) 。
________________________________________
lua_newthread
lua_State *lua_newthread (lua_State *L);
創建一個新線程,并將其壓入堆棧,并返回維護這個線程的 lua_State 指針。這個函數返回的新狀態機共享原有狀態機中的所有對象(比如一些 table),但是它有獨立的執行堆棧。
沒有顯式的函數可以用來關閉或銷毀掉一個線程。線程跟其它 Lua 對象一樣是垃圾收集的條目之一。
________________________________________
lua_newuserdata
void *lua_newuserdata (lua_State *L, size_t size);
這個函數分配分配一塊指定大小的內存塊,把內存塊地址作為一個完整的 userdata 壓入堆棧,并返回這個地址。
userdata 代表 Lua 中的 C 值。完整的 userdata 代表一塊內存。它是一個對象(就像 table 那樣的對象):你必須創建它,它有著自己的元表,而且它在被回收時,可以被監測到。一個完整的 userdata 只和它自己相等(在等于的原生作用下)。
當 Lua 通過 gc 元方法回收一個完整的 userdata 時, Lua 調用這個元方法并把 userdata 標記為已終止。等到這個 userdata 再次被收集的時候,Lua 會釋放掉相關的內存。
________________________________________
lua_next
int lua_next (lua_State *L, int index);
從棧上彈出一個 key(鍵),然后把索引指定的表中 key-value(健值)對壓入堆棧(指定 key 后面的下一 (next) 對)。如果表中以無更多元素,那么 lua_next 將返回 0 (什么也不壓入堆棧)。
典型的遍歷方法是這樣的:
/* table 放在索引 't' 處 */
lua_pushnil(L); /* 第一個 key */
while (lua_next(L, t) != 0) {
/* 用一下 'key' (在索引 -2 處) 和 'value' (在索引 -1 處) */
printf("%s - %s\n",
lua_typename(L, lua_type(L, -2)),
lua_typename(L, lua_type(L, -1)));
/* 移除 'value' ;保留 'key' 做下一次疊代 */
lua_pop(L, 1);
}
在遍歷一張表的時候,不要直接對 key 調用 lua_tolstring ,除非你知道這個 key 一定是一個字符串。調用 lua_tolstring 有可能改變給定索引位置的值;這會對下一次調用 lua_next 造成影響。
lua_Number
typedef double lua_Number;
Lua 中數字的類型。確省是 double ,但是你可以在 luaconf.h 中修改它。
通過修改配置文件你可以改變 Lua 讓它操作其它數字類型(例如:float 或是 long )。
________________________________________
lua_objlen
size_t lua_objlen (lua_State *L, int index);
返回指定的索引處的值的長度。對于 string ,那就是字符串的長度;對于 table ,是取長度操作符 ('#') 的結果;對于 userdata ,就是為其分配的內存塊的尺寸;對于其它值,為 0 。
________________________________________
lua_pcall
lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);
以保護模式調用一個函數。
nargs 和 nresults 的含義與 lua_call 中的相同。如果在調用過程中沒有發生錯誤, lua_pcall 的行為和 lua_call 完全一致。但是,如果有錯誤發生的話, lua_pcall 會捕獲它,然后把單一的值(錯誤信息)壓入堆棧,然后返回錯誤碼。同 lua_call 一樣, lua_pcall 總是把函數本身和它的參數從棧上移除。
如果 errfunc 是 0 ,返回在棧頂的錯誤信息就和原始錯誤信息完全一致。否則,errfunc 就被當成是錯誤處理函數在棧上的索引。(在當前的實現里,這個索引不能是偽索引。)在發生運行時錯誤時,這個函數會被調用而參數就是錯誤信息。錯誤處理函數的返回值將被 lua_pcall 作為出錯信息返回在堆棧上。
典型的用法中,錯誤處理函數被用來在出錯信息上加上更多的調試信息,比如棧跟蹤信息 (stack traceback) 。這些信息在 lua_pcall 返回后,因為棧已經展開 (unwound) ,所以收集不到了。
lua_pcall 函數在調用成功時返回 0 ,否則返回以下(定義在 lua.h 中的)錯誤代碼中的一個:
? LUA_ERRRUN: 運行時錯誤。
? LUA_ERRMEM: 內存分配錯誤。對于這種錯,Lua 調用不了錯誤處理函數。
? LUA_ERRERR: 在運行錯誤處理函數時發生的錯誤。
________________________________________
lua_pop
void lua_pop (lua_State *L, int n);
從堆棧中彈出 n 個元素。
________________________________________
lua_pushboolean
void lua_pushboolean (lua_State *L, int b);
把 b 作為一個 boolean 值壓入堆棧。
________________________________________
lua_pushcclosure
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
把一個新的 C closure 壓入堆棧。
當創建了一個 C 函數后,你可以給它關聯一些值,這樣就是在創建一個 C closure (參見 §3.4);接下來無論函數何時被調用,這些值都可以被這個函數訪問到。為了將一些值關聯到一個 C 函數上,首先這些值需要先被壓入堆棧(如果有多個值,第一個先壓)。接下來調用 lua_pushcclosure 來創建出 closure 并把這個 C 函數壓到堆棧上。參數 n 告之函數有多少個值需要關聯到函數上。 lua_pushcclosure 也會把這些值從棧上彈出。
________________________________________
lua_pushcfunction
void lua_pushcfunction (lua_State *L, lua_CFunction f);
將一個 C 函數壓入堆棧。這個函數接收一個 C 函數指針,并將一個類型為 function 的 Lua 值壓入堆棧。當這個棧定的值被調用時,將觸發對應的 C 函數。
注冊到 Lua 中的任何函數都必須遵循正確的協議來接收參數和返回值(參見 lua_CFunction)。
lua_pushcfunction 是作為一個宏定義出現的:
#define lua_pushcfunction(L,f) lua_pushcclosure(L,f,0)
________________________________________
lua_pushfstring
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
把一個格式化過的字符串壓入堆棧,然后返回這個字符串的指針。它和 C 函數 sprintf 比較像,不過有一些重要的區別:
? 摸你需要為結果分配空間:其結果是一個 Lua 字符串,由 Lua 來關心其內存分配(同時通過垃圾收集來釋放內存)。
? 這個轉換非常的受限。不支持 flag ,寬度,或是指定精度。它只支持下面這些: '%%' (插入一個 '%'), '%s' (插入一個帶零終止符的字符串,沒有長度限制), '%f' (插入一個 lua_Number), '%p' (插入一個指針或是一個十六進制數), '%d' (插入一個 int), '%c' (把一個 int 作為一個字符插入)。
________________________________________
lua_pushinteger
void lua_pushinteger (lua_State *L, lua_Integer n);
把 n 作為一個數字壓棧。
________________________________________
lua_pushlightuserdata
void lua_pushlightuserdata (lua_State *L, void *p);
把一個 light userdata 壓棧。
userdata 在 Lua 中表示一個 C 值。 light userdata 表示一個指針。它是一個像數字一樣的值:你不需要專門創建它,它也沒有獨立的 metatable ,而且也不會被收集(因為從來不需要創建)。只要表示的 C 地址相同,兩個 light userdata 就相等。
________________________________________
lua_pushlstring
void lua_pushlstring (lua_State *L, const char *s, size_t len);
把指針 s 指向的長度為 len 的字符串壓棧。 Lua 對這個字符串做一次內存拷貝(或是復用一個拷貝),因此 s 處的內存在函數返回后,可以釋放掉或是重用于其它用途。字符串內可以保存有零字符。
________________________________________
lua_pushnil
void lua_pushnil (lua_State *L);
把一個 nil 壓棧。
________________________________________
lua_pushnumber
void lua_pushnumber (lua_State *L, lua_Number n);
把一個數字 n 壓棧。
________________________________________
lua_pushstring
void lua_pushstring (lua_State *L, const char *s);
把指針 s 指向的以零結尾的字符串壓棧。 Lua 對這個字符串做一次內存拷貝(或是復用一個拷貝),因此 s 處的內存在函數返回后,可以釋放掉或是重用于其它用途。字符串中不能包含有零字符;第一個碰到的零字符會認為是字符串的結束。
________________________________________
lua_pushthread
int lua_pushthread (lua_State *L);
把 L 中提供的線程壓棧。如果這個線程是當前狀態機的主線程的話,返回 1 。
________________________________________
lua_pushvalue
void lua_pushvalue (lua_State *L, int index);
把堆棧上給定有效處索引處的元素作一個拷貝壓棧。
________________________________________
lua_pushvfstring
const char *lua_pushvfstring (lua_State *L,
const char *fmt,
va_list argp);
等價于 lua_pushfstring,不過是用 va_list 接收參數,而不是用可變數量的實際參數。
________________________________________
lua_rawequal
int lua_rawequal (lua_State *L, int index1, int index2);
如果兩個索引 index1 和 index2 處的值簡單地相等(不調用元方法)則返回 1 。否則返回 0 。如果任何一個索引無效也返回 0 。
________________________________________
lua_rawget
void lua_rawget (lua_State *L, int index);
類似于 lua_gettable,但是作一次直接訪問(不觸發元方法)。
________________________________________
lua_rawgeti
void lua_rawgeti (lua_State *L, int index, int n);
把 t[n] 的值壓棧,這里的 t 是指給定索引 index 處的一個值。這是一個直接訪問;就是說,它不會觸發元方法。
________________________________________
lua_rawset
void lua_rawset (lua_State *L, int index);
類似于 lua_settable,但是是作一個直接賦值(不觸發元方法)。
________________________________________
lua_rawseti
void lua_rawseti (lua_State *L, int index, int n);
等價于 t[n] = v,這里的 t 是指給定索引 index 處的一個值,而 v 是棧定的值。
函數將把這個值彈出棧。賦值操作是直接的;就是說,不會觸發元方法。
________________________________________
lua_Reader
typedef const char * (*lua_Reader) (lua_State *L,
void *data,
size_t *size);
lua_load 用到的讀取器函數,每次它需要一塊新的 chunk 的時候, lua_load 就調用讀取器,每次都會傳入一個參數 data 。讀取器需要返回含有新的 chunk 的一塊內存的指針,并把 size 設為這塊內存的大小。內存塊必須在下一次函數被調用之前一直存在。讀取器可以通過返回一個 NULL 來指示 chunk 結束。讀取器可能返回多個塊,每個塊可以有任意的大于零的尺寸。
________________________________________
lua_register
void lua_register (lua_State *L,
const char *name,
lua_CFunction f);
把 C 函數 f 設到全局變量 name 中。它通過一個宏定義:
#define lua_register(L,n,f) \
(lua_pushcfunction(L, f), lua_setglobal(L, n))
________________________________________
lua_remove
void lua_remove (lua_State *L, int index);
從給定有效索引處移除一個元素,把這個索引之上的所有元素移下來填補上這個空隙。不能用偽索引來調用這個函數,因為偽索引并不指向真實的棧上的位置。
________________________________________
lua_replace
void lua_replace (lua_State *L, int index);
把棧定元素移動到給定位置(并且把這個棧定元素彈出),不移動任何元素(因此在那個位置處的值被覆蓋掉)。
________________________________________
lua_resume
int lua_resume (lua_State *L, int narg);
在給定線程中啟動或繼續一個 coroutine 。
要啟動一個 coroutine 的話,首先你要創建一個新線程(參見 lua_newthread );然后把主函數和若干參數壓到新線程的堆棧上;最后調用 lua_resume ,把 narg 設為參數的個數。這次調用會在 coroutine 掛起時或是結束運行后返回。當函數返回時,堆棧中會有傳給 lua_yield 的所有值,或是主函數的所有返回值。如果 coroutine 切換時,lua_resume 返回 LUA_YIELD ,而當 coroutine 結束運行且沒有任何錯誤時,返回 0 。如果有錯則返回錯誤代碼(參見 lua_pcall)。在發生錯誤的情況下,堆棧沒有展開,因此你可以使用 debug API 來處理它。出錯信息放在棧頂。要繼續運行一個 coroutine 的話,你把需要傳給 yield 作結果的返回值壓入堆棧,然后調用 lua_resume 。
________________________________________
lua_setallocf
void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
把指定狀態機的分配器函數換成帶上指針 ud 的 f 。
________________________________________
lua_setfenv
int lua_setfenv (lua_State *L, int index);
從堆棧上彈出一個 table 并把它設為指定索引處值的新環境。如果指定索引處的值即不是函數又不是線程或是 userdata , lua_setfenv 會返回 0 ,否則返回 1 。
________________________________________
lua_setfield
void lua_setfield (lua_State *L, int index, const char *k);
做一個等價于 t[k] = v 的操作,這里 t 是給出的有效索引 index 處的值,而 v 是棧頂的那個值。
這個函數將把這個值彈出堆棧。跟在 Lua 中一樣,這個函數可能觸發一個 "newindex" 事件的元方法(參見 §2.8)。
________________________________________
lua_setglobal
void lua_setglobal (lua_State *L, const char *name);
從堆棧上彈出一個值,并將其設到全局變量 name 中。它由一個宏定義出來:
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
________________________________________
lua_setmetatable
int lua_setmetatable (lua_State *L, int index);
把一個 table 彈出堆棧,并將其設為給定索引處的值的 metatable 。
________________________________________
lua_settable
void lua_settable (lua_State *L, int index);
作一個等價于 t[k] = v 的操作,這里 t 是一個給定有效索引 index 處的值, v 指棧頂的值,而 k 是棧頂之下的那個值。
這個函數會把鍵和值都從堆棧中彈出。和在 Lua 中一樣,這個函數可能觸發 "newindex" 事件的元方法(參見 §2.8)。
________________________________________
lua_settop
void lua_settop (lua_State *L, int index);
參數允許傳入任何可接受的索引以及 0 。它將把堆棧的棧頂設為這個索引。如果新的棧頂比原來的大,超出部分的新元素將被填為 nil 。如果 index 為 0 ,把棧上所有元素移除。
________________________________________
lua_State
typedef struct lua_State lua_State;
一個不透明的結構,它保存了整個 Lua 解釋器的狀態。 Lua 庫是完全可重入的:它沒有任何全局變量。(譯注:從 C 語法上來說,也不盡然。例如,在 table 的實現中用了一個靜態全局變量 dummynode_ ,但這在正確使用時并不影響可重入性。只是萬一你錯誤鏈接了 lua 庫,不小心在同一進程空間中存在兩份 lua 庫實現的代碼的話,多份 dummynode_ 不同的地址會導致一些問題。)所有的信息都保存在這個結構中。
這個狀態機的指針必須作為第一個參數傳遞給每一個庫函數。 lua_newstate 是一個例外,這個函數會從頭創建一個 Lua 狀態機。
________________________________________
lua_status
int lua_status (lua_State *L);
返回線程 L 的狀態。
正常的線程狀態是 0 。當線程執行完畢或發生一個錯誤時,狀態值是錯誤碼。如果線程被掛起,狀態為 LUA_YIELD 。
________________________________________
lua_toboolean
int lua_toboolean (lua_State *L, int index);
把指定的索引處的的 Lua 值轉換為一個 C 中的 boolean 值( 0 或是 1 )。和 Lua 中做的所有測試一樣, lua_toboolean 會把任何不同于 false 和 nil 的值當作 1 返回;否則就返回 0 。如果用一個無效索引去調用也會返回 0 。(如果你想只接收真正的 boolean 值,就需要使用 lua_isboolean 來測試值的類型。)
________________________________________
lua_tocfunction
lua_CFunction lua_tocfunction (lua_State *L, int index);
把給定索引處的 Lua 值轉換為一個 C 函數。這個值必須是一個 C 函數;如果不是就返回 NULL 。
________________________________________
lua_tointeger
lua_Integer lua_tointeger (lua_State *L, int idx);
把給定索引處的 Lua 值轉換為 lua_Integer 這樣一個有符號整數類型。這個 Lua 值必須是一個數字或是一個可以轉換為數字的字符串(參見 §2.2.1);否則,lua_tointeger 返回 0 。
如果數字不是一個整數,截斷小數部分的方式沒有被明確定義。
________________________________________
lua_tolstring
const char *lua_tolstring (lua_State *L, int index, size_t *len);
把給定索引處的 Lua 值轉換為一個 C 字符串。如果 len 不為 NULL ,它還把字符串長度設到 *len 中。這個 Lua 值必須是一個字符串或是一個數字;否則返回返回 NULL 。如果值是一個數字,lua_tolstring 還會把堆棧中的那個值的實際類型轉換為一個字符串。(當遍歷一個表的時候,把 lua_tolstring 作用在鍵上,這個轉換有可能導致 lua_next 弄錯。)
lua_tolstring 返回 Lua 狀態機中字符串的以對齊指針。這個字符串總能保證 ( C 要求的)最后一個字符為零 ('\0') ,而且它允許在字符串內包含多個這樣的零。因為 Lua 中可能發生垃圾收集,所以不保證 lua_tolstring 返回的指針,在對應的值從堆棧中移除后依然有效。
________________________________________
lua_tonumber
lua_Number lua_tonumber (lua_State *L, int index);
把給定索引處的 Lua 值轉換為 lua_Number 這樣一個 C 類型(參見 lua_Number )。這個 Lua 值必須是一個數字或是一個可轉換為數字的字符串(參見 §2.2.1 );否則,lua_tonumber 返回 0 。
________________________________________
lua_topointer
const void *lua_topointer (lua_State *L, int index);
把給定索引處的值轉換為一般的 C 指針 (void*) 。這個值可以是一個 userdata ,table ,thread 或是一個 function ;否則,lua_topointer 返回 NULL 。不同的對象有不同的指針。不存在把指針再轉回原有類型的方法。
這個函數通常只為產生 debug 信息用。
________________________________________
lua_tostring
const char *lua_tostring (lua_State *L, int index);
等價于 lua_tolstring ,而參數 len 設為 NULL 。
________________________________________
lua_tothread
lua_State *lua_tothread (lua_State *L, int index);
把給定索引處的值轉換為一個 Lua 線程(由 lua_State* 代表)。這個值必須是一個線程;否則函數返回 NULL 。
________________________________________
lua_touserdata
void *lua_touserdata (lua_State *L, int index);
如果給定索引處的值是一個完整的 userdata ,函數返回內存塊的地址。如果值是一個 light userdata ,那么就返回它表示的指針。否則,返回 NULL 。
________________________________________
lua_type
int lua_type (lua_State *L, int index);
返回給定索引處的值的類型,當索引無效時則返回 LUA_TNONE (那是指一個指向堆棧上的空位置的索引)。 lua_type 返回的類型是一些個在 lua.h 中定義的常量: LUA_TNIL , LUA_TNUMBER , LUA_TBOOLEAN , LUA_TSTRING , LUA_TTABLE , LUA_TFUNCTION , LUA_TUSERDATA , LUA_TTHREAD , LUA_TLIGHTUSERDATA 。
________________________________________
lua_typename
const char *lua_typename (lua_State *L, int tp);
返回 tp 表示的類型名,這個 tp 必須是 lua_type 可能返回的值中之一。
________________________________________
lua_Writer
typedef int (*lua_Writer) (lua_State *L,
const void* p,
size_t sz,
void* ud);
由 lua_dump 用到的寫入器函數。每次 lua_dump 產生了一塊新的 chunk ,它都會調用寫入器。傳入要寫入的緩存 (p) 和它的尺寸 (sz) ,還有 lua_dump 的參數 data 。
寫入器會返回一個錯誤碼: 0 表示沒有錯誤;別的值均表示一個錯誤,并且會讓 lua_dump 停止再次調用寫入器。
________________________________________
lua_xmove
void lua_xmove (lua_State *from, lua_State *to, int n);
傳遞 同一個 全局狀態機下不同線程中的值。
這個函數會從 from 的堆棧中彈出 n 個值,然后把它們壓入 to 的堆棧中。
________________________________________
lua_yield
int lua_yield (lua_State *L, int nresults);
切出一個 coroutine 。
這個函數只能在一個 C 函數的返回表達式中調用。如下:
return lua_yield (L, nresults);
當一個 C 函數這樣調用 lua_yield ,正在運行中的 coroutine 將從運行中掛起,然后啟動這個 coroutine 用的那次對 lua_resume 的調用就返回了。參數 nresults 指的是堆棧中需要返回的結果個數,這些返回值將被傳遞給 lua_resume 。
3.8 - 調試接口
Lua 沒有內建的調試設施。取而代之的是提供了一些函數接口和鉤子。利用這些接口,可以做出一些不同類型的調試器,性能分析器,或是其它一些需要從解釋器中取到“內部信息”的工具。
________________________________________
lua_Debug
typedef struct lua_Debug {
int event;
const char *name; /* (n) */
const char *namewhat; /* (n) */
const char *what; /* (S) */
const char *source; /* (S) */
int currentline; /* (l) */
int nups; /* (u) upvalue 個數 */
int linedefined; /* (S) */
int lastlinedefined; /* (S) */
char short_src[LUA_IDSIZE]; /* (S) */
/* 私有部分 */
其它域
} lua_Debug;
一個用來攜帶活動中函數的各種信息的結構。 lua_getstack 僅填寫這個結構中的私有部分,這些部分以后會用到。調用 lua_getinfo 則可以填上 lua_Debug 中有用信息的那些域。
lua_Debug 中的各個域有下列含義:
? source: 如果函數是定義在一個字符串中,source 就是這個字符串。如果函數定義在一個文件中, source 是一個以 '@' 開頭的文件名。
? short_src: 一個“可打印版本”的 source,用于出錯信息。
? linedefined: 函數定義開始處的行號。
? lastlinedefined: 函數定義結束處的行號。
? what: 如果函數是一個 Lua 函數,則為一個字符串 "Lua" ;如果是一個 C 函數,則為 "C";如果它是一個 chunk 的主體部分,則為 "main";如果是一個作了尾調用的函數,則為 "tail" 。別的情況下,Lua 沒有關于函數的別的信息。
? currentline: 給定函數正在執行的那一行。當提供不了行號信息的時候,currentline 被設為 -1 。
? name: 給定函數的一個合理的名字。因為 Lua 中的函數也是一個值,所以它們沒有固定的名字:一些函數可能是全局復合變量的值,另一些可能僅僅只是被保存在一個 table 中。 lua_getinfo 函數會檢查函數是這樣被調用的,以此來找到一個適合的名字。如果它找不到名字,name 就被設置為 NULL 。
? namewhat: 結實 name 域。 namewhat 的值可以是 "global", "local", "method", "field", "upvalue", 或是 "" (空串)。這取決于函數怎樣被調用。(Lua 用空串表示其它選項都不符合)
? nups: 函數的 upvalue 的個數。
________________________________________
lua_gethook
lua_Hook lua_gethook (lua_State *L);
返回當前的鉤子函數。
________________________________________
lua_gethookcount
int lua_gethookcount (lua_State *L);
返回當前鉤子記數。
________________________________________
lua_gethookmask
int lua_gethookmask (lua_State *L);
返回當前的鉤子掩碼 (mask) 。
________________________________________
lua_getinfo
int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
返回一個指定的函數或函數調用的信息。
當用于取得一次函數調用的信息時,參數 ar 必須是一個有效的活動的記錄。這條記錄可以是前一次調用 lua_getstack 得到的,或是一個鉤子 (參見 lua_Hook)得到的參數。
用于獲取一個函數的信息時,可以把這個函數壓入堆棧,然后把 what 字符串以字符 '>' 起頭。(這個情況下,lua_getinfo 從棧頂上彈出函數。) 例如,想知道函數 f 在哪一行定義的,你可以下下列代碼:
lua_Debug ar;
lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* 取到全局變量 'f' */
lua_getinfo(L, ">S", &ar);
printf("%d\n", ar.linedefined);
what 字符串中的每個字符都篩選出結構 ar 結構中一些域用于填充,或是把一個值壓入堆棧:
? 'n': 填充 name 及 namewhat 域;
? 'S': 填充 source, short_src, linedefined, lastlinedefined,以及 what 域;
? 'l': 填充 currentline 域;
? 'u': 填充 nups 域;
? 'f': 把正在運行中指定級別處函數壓入堆棧;(譯注:一般用于獲取函數調用中的信息,級別是由 ar 中的私有部分來提供。如果用于獲取靜態函數,那么就直接把指定函數重新壓回堆棧,但這樣做通常無甚意義。)
? 'L': 壓一個 table 入棧,這個 table 中的整數索引用于描述函數中哪些行是有效行。(有效行指有實際代碼的行,即你可以置入斷點的行。無效行包括空行和只有注釋的行。)
這個函數出錯會返回 0 (例如,what 中有一個無效選項)。
________________________________________
lua_getlocal
const char *lua_getlocal (lua_State *L, lua_Debug *ar, int n);
從給定活動記錄中獲取一個局部變量的信息。參數 ar 必須是一個有效的活動的記錄。這條記錄可以是前一次調用 lua_getstack 得到的,或是一個鉤子 (參見 lua_Hook)得到的參數。索引 n 用于選擇要檢閱哪個局部變量( 1 表示第一個參數或是激活的第一個局部變量,以此類推,直到最后一個局部變量)。 lua_getlocal 把變量的值壓入堆棧并返回它的名字。
以 '(' (正小括號)開始的變量指內部變量(循環控制變量,臨時變量,C 函數局部變量)。
當索引大于局部變量的個數時,返回 NULL (什么也不壓入)。
________________________________________
lua_getstack
int lua_getstack (lua_State *L, int level, lua_Debug *ar);
獲取解釋器的運行時棧的信息。
這個函數用正在運行中的給定級別處的函數的活動記錄來填寫 lua_Debug 結構的一部分。 0 級表示當前運行的函數,而 n+1 級處的函數就是調用第 n 級函數的那一個。如果沒有錯誤,lua_getstack 返回 1 ;當調用傳入的級別大于堆棧深度的時候,返回 0 。
________________________________________
lua_getupvalue
const char *lua_getupvalue (lua_State *L, int funcindex, int n);
獲取一個 closure 的 upvalue 信息。(對于 Lua 函數,upvalue 是函數需要使用的外部局部變量,因此這些變量被包含在 closure 中。) lua_getupvalue 獲取第 n 個 upvalue ,把這個 upvalue 的值壓入堆棧,并且返回它的名字。 funcindex 指向堆棧上 closure 的位置。( 因為 upvalue 在整個函數中都有效,所以它們沒有特別的次序。因此,它們以字母次序來編號。)
當索引號比 upvalue 數量大的時候,返回 NULL (而且不會壓入任何東西)對于 C 函數,這個函數用空串 "" 表示所有 upvalue 的名字。
________________________________________
lua_Hook
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
用于調試的鉤子函數類型。
無論何時鉤子被調用,它的參數 ar 中的 event 域都被設為觸發鉤子的事件。 Lua 把這些事件定義為以下常量: LUA_HOOKCALL, LUA_HOOKRET, LUA_HOOKTAILRET, LUA_HOOKLINE, and LUA_HOOKCOUNT。除此之外,對于 line 事件,currentline 域也被設置。要想獲得 ar 中的其他域,鉤子必須調用 lua_getinfo。對于返回事件,event 的正常值可能是 LUA_HOOKRET,或者是 LUA_HOOKTAILRET 。對于后一種情況,Lua 會對一個函數做的尾調用也模擬出一個返回事件出來;對于這個模擬的返回事件,調用 lua_getinfo 沒有什么作用。
當 Lua 運行在一個鉤子內部時,它將屏蔽掉其它對鉤子的調用。也就是說,如果一個鉤子函數內再調回 Lua 來執行一個函數或是一個 chunk ,這個執行操作不會觸發任何的鉤子。
________________________________________
lua_sethook
int lua_sethook (lua_State *L, lua_Hook f, int mask, int count);
設置一個調試用鉤子函數。
參數 f 是鉤子函數。 mask 指定在哪些事件時會調用:它由下列一組位常量構成 LUA_MASKCALL, LUA_MASKRET, LUA_MASKLINE,以及 LUA_MASKCOUNT。參數 count 只在 mask 中包含有 LUA_MASKCOUNT 才有意義。對于每個事件,鉤子被調用的情況解釋如下:
? call hook: 在解釋器調用一個函數時被調用。鉤子將于 Lua 進入一個新函數后,函數獲取參數前被調用。
? return hook: 在解釋器從一個函數中返回時調用。鉤子將于 Lua 離開函數之前的那一刻被調用。你無權訪問被函數返回出去的那些值。 (譯注:原文 (You have no access to the values to be returned by the function) 如此。但“無權訪問”一詞值得商榷。某些情況下你可以訪問到一些被命名為 (*temporary) 的局部變量,那些索引被排在最后的 (*temporary) 變量指的就是返回值。但是由于 Lua 對特殊情況做了一些優化,比如直接返回一個被命名的局部變量,那么就找不到對應的 (*temporary) 變量了。本質上,返回值一定存在于此刻的局部變量中,并且可以訪問它,只是無法確定是哪些罷了。至于這個時候函數體內的其它局部變量,是不保證有效的。進入 return hook 的那一刻起,實際已經退出函數內部的運行環節,返回值占用的局部變量空間以后的部分,都有可能因 hook 本身復用它們而改變。)
? line hook: 在解釋器準備開始執行新的一行代碼時,或是跳轉到這行代碼中時(即使在同一行內跳轉)被調用。(這個事件僅僅在 Lua 執行一個 Lua 函數時發生。)
? count hook: 在解釋器每執行 count 條指令后被調用。(這個事件僅僅在 Lua 執行一個 Lua 函數時發生。)
鉤子可以通過設置 mask 為零屏蔽。
________________________________________
lua_setlocal
const char *lua_setlocal (lua_State *L, lua_Debug *ar, int n);
設置給定活動記錄中的局部變量的值。參數 ar 與 n 和 lua_getlocal 中的一樣(參見 lua_getlocal)。 lua_setlocal 把棧頂的值賦給變量然后返回變量的名字。它會將值從棧頂彈出。
當索引大于局部變量的個數時,返回 NULL (什么也不彈出)。
總結
- 上一篇: 韩剧tv怎么不能投屏(最新最全韩剧网)
- 下一篇: Python 实现的随机森林