(接上篇)
--------------------------------------
7 調試接口
--------------------------------------
Lua 沒有內置的調試功能。而是藉助於函數和鉤子(hook)提供了一個特殊接口,能夠用來構建不一樣種類的調試器,分析器(profile)和一些其它的須要解釋器內部信息的工具。這個接口在 luadebug.h 文件中聲明。
-------------------
7.1 棧和函數信息
-------------------
得到解釋器棧信息的主要函數是:
int lua_getstack (lua_State *L, int level, lua_Debug *ar);
它用給定層級上正在執行的一個活動記錄標識填充部分 lua_Debug 結構。0 級是正在運行的函數,層級 n+1 是調用層級 n 的函數。一般,lua_getstack 返回 1 ;當以一個高於棧的深度的層次來調用時,它返回 0 。
結構 lua_Debug 用來攜帶一個活動函數的不一樣信息片斷:
typedef struct lua_Debug {
const char *event; /* "call", "return" */
int currentline; /* (l) */
const char *name; /* (n) */
const char *namewhat; /* (n) global, tag method, local, field */
int nups; /* (u) number of upvalues */
int linedefined; /* (S) */
const char *what; /* (S) "Lua" function, "C" function, Lua "main" */
const char *source; /* (S) */
char short_src[LUA_IDSIZE]; /* (S) */
/* private part */
...
} lua_Debug;
lua_getstack 只填充這個結構的私有部分,爲後來使用。用有用信息填充 lua_Debug 其它的字段,調用
int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
這個函數出錯時返回 0 (例如,一個 what 的無效選項)。字符串 what 中的每一個字符選擇一些被填充的 ar 的字段,如上面 lua_Debug 的定義中被一個括號中的字母標識出來的: `S' 填充字段 source, linedefined, 和 what; `l' 填充字段 currentline, 等。並且, `f' 壓棧在給定層級上正在執行的函數。
爲得到一個不活動函數的信息(即,不在棧裏),能夠把函數壓棧,並字符 > 放在字符串 what 的起頭。例如,爲了獲取一個函數 f 在哪裏定義,你能夠
lua_Debug ar;
lua_getglobal(L, "f");
lua_getinfo(L, ">S", &ar);
printf("%d\n", ar.linedefined);
lua_Debug 的字段有下列意思
source
若是函數被定義在字符串中,source 就是那個字符串;若是函數定義在一個文件中,source 由 @ 開始並後跟文件名。
short_src
一個 ``printable'' 版本的 source,用在錯誤信息中。
linedefined
函數定義開始處的等號。
what
字符串 "Lua" 若是這是一個 Lua 函數, "C" 若是這是一個 C 函數, 或者 "main" 若是這是一個塊的主要部分。
currentline
給定函數正在執行的當前行。若是沒有行信息, currentline 被設置爲 -1.
name
一個給定函數的合理的名字。由於函數在 Lua 中第一類值,它們沒有一個固定的名字:一些函數多是不少全局變量的值,而其它的可能只保存在一個表的字段中。lua_getinfo 函數檢查給定的函數是標籤方法或者是一個全局變量的名字。若是給定函數是一個標籤方法,那麼 name 指向相應的事件名。若是給定的函數是一個全局變量的值,那麼 name 指向變量名字。若是給定函數既不是一個標籤方法也不是一個全局變量,name 被設置爲 NULL。
namewhat
解釋以前的字段。若是函數是一個全局變量,namewhat 是 "global";若是函數是一個標籤方法,namewhat 是 "tag-method";不然,namewhat 是 "" (空字符串)。
nups
一個函數的 upvalue 的個數。
-------------------
7.2 局部變量的操做
-------------------
luadebug.h 使用索引來操縱局部變量:第一個參數或者局部變量的索引爲 1,以此類推,直到最後一個活動的局部變量。
下面的函數能夠用來操縱給定活動記錄的局部變量:
const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
參數 ar 必須是一個有效的活動記錄,被以前的 lua_getstack 調用填充或者做爲一個 hook 參數給定(參見 7.3 節)。函數 lua_getlocal 得到一個局部變量(n)的索引,把它的值壓棧,並返回它的名字。對於 lua_setlocal,你把新值壓棧,這個函數把新值賦給那個變量並返回它的名字。兩個函數失敗時均返回 NULL;若是索引大於活動局部變量的時候這處狀況就會發生。
做爲一個例子,下面的函數列出一個在棧的給定層次上的全部的局部變量的名字:
int listvars (lua_State *L, int level) {
lua_Debug ar;
int i = 1;
const char *name;
if (lua_getstack(L, level, &ar) == 0)
return 0; /* failure: no such level in the stack */
while ((name = lua_getlocal(L, &ar, i++)) != NULL) {
printf("%s\n", name);
lua_pop(L, 1); /* remove variable value */
}
return 1;
}
-------------------
7.3 鉤子 (Hooks)
-------------------
Lua 解釋器爲調試提供了兩個鉤子(Hook),一個 call hook, 一個 line hook。它兩個有相同的類型:
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
你可使用下面的函數來設置它們:
lua_Hook lua_setcallhook (lua_State *L, lua_Hook func);
lua_Hook lua_setlinehook (lua_State *L, lua_Hook func);
鉤子是無效的若是它的值爲 NULL ,NULL 也是兩個鉤子的默認值。函數 lua_setcallhook 和 lua_setlinehook 設置他們相應的鉤子並返回它們以前的值。
當解釋器進入或者離開一個函數時,call hook 會被調用。ar 的 event 字段的字符串是 "call" 或者 "return"。這個 ar 以後能夠被用在調用 lua_getinfo, lua_getlocal, 和 lua_setlocal 去得到更多的關於函數和操縱局部變量的信息。
當解釋器每次改變它執行的代碼行號時會調用 line hook。ar 的字符 event 的字符串是 "line" , 而且 currentline 字段有行號。再次強調,你可使用這個 ar 在其它的調用 API 的調用中。
當 Lua 在執行一個鉤子時,它禁用了其它的鉤子的調用。所以,若是一個鉤子調用 Lua 去執行一個函數或者一個塊,這個執行將不會再有其它的鉤子調用。
-------------------
7.3 自反的調用接口
-------------------
庫 ldblib 爲 Lua 程序提供調試接口的功能。若是你想使用這個庫,你的宿主應用程序必須打開它,經過調用 lua_dblibopen。
當使用這個庫時你應該萬分當心。這裏提供的函數應該僅僅用於調試或者相似的任務(例如,性能分析)。請抵制把他們做爲經常使用工具的誘惑。它們很慢而且違反了一些語言的安全性。(例如,局部變量的私密性)。做爲一個通常性規則,若是你的程序不須要這個庫,不要打開它。
getinfo (function, [what])
這個函數返回一個帶有函數信息的表。你能夠直接給出函數,或者你能夠給出一個數字做爲函數的值,它意味着函數正在棧上執行的函數的層次:Level 0 是當前的函數(getinfo 它本身);Level 1 是調用 getinfo 的函數;以此類推。若是函數的值比活動函數的個數多,那麼 getinfo 返回 nil。
返回的表包含全部由 lua_getinfo 返回的字段,用一個字符串 what 來描述獲取什麼。what 的默認值是得到全部的可用信息。
例如,表達式 getinfo(1, "n").name 返回當前函數的名字,若是能夠找到一個合理的名字,getinfo(print) 返回一個關於 print 函數的全部可用信息的表。
getlocal (level, local)
函數返回局部變量的名字和值,局部變量的 index 爲 local ,它位於棧上的層級 level 的函數中。(第一個參數或者局部變量的 index 爲 1,以此類推,直到最後一個活動的局部變量。)函數返回 nil 若是沒有給定索引的局部變量,併發生一個錯誤當以一個範圍以外的 level 調用時。(你能夠調用 getinfo 去檢查 level 是否有效。)
setlocal (level, local, value)
函數設置局部變量的值爲 value,局部變量的 index 爲 local ,它位於棧上的層級 level 的函數中。函數返回 nil 若是沒有給定索引的局部變量,併發生一個錯誤當以一個範圍以外的 level 調用時。
setcallhook (hook)
設置函數 hook 爲調用鉤子;這個鉤子在每次解釋器開始或者退出一個函數執行時將會被調用。調用鉤子惟一的參數是事件名稱("call" 或者 "return")。你能夠以 level 2 調用 getinfo 去得到更多的正在被調用或正在返回的函數信息(level 0 是 getinfo 函數,level1 是鉤子函數)。當無參調用時,這個函數關閉調用鉤子。setcallhook 返回老的鉤子。
setlinehook (hook)
設置函數 hook 爲行鉤子;這個鉤子在每次解釋器改變正在執行的代碼的行時將會被調用。行鉤子的惟一參數是解釋器將要執行的行號。當無參調用時,這個函數關閉調用鉤子。setlinehook 返回老的鉤子。
--------------------------------------
8 獨立執行的 Lua
--------------------------------------
儘管 Lua 被設計做爲一個擴展語言,被嵌入到一個 C 宿主程序,它也常常被作爲一個獨立的語言使用。一個做爲獨立語言使用的 Lua 解釋器,叫作簡單的 lua,在標準發佈版中被提供。這個程序能夠被如下的參數以任意順序調用:
-sNUM
設置棧的大小爲 NUM(若是存在,這必須是第一個選項);
-
把標準輸入做爲文件執行。
-c
調用 lua_close 在執行完全部參數以後;
-e \rmstat
執行字符串 stat;
-f filename
執行文件 filename 全部剩餘的參數在表 arg 中;
-i
進入交互模式,顯示一個提示符;
-q
進入交互模式,不顯示提示符。
-v
打印版本信息;
var=value
設置全局變量 var 爲字符串 "value";
filename
執行文件 filename。
當無參調用時,lua 的行爲和 lua -v -i 同樣當標準輸入是一個終端時,不然像 lua - 。
全部的參數按順序處理,除了 -c 。例如,一個調用
$ lua -i a=test prog.lua
將首先和用戶交互直到一個 EOF 出如今標準輸入中,而後設置 a 爲 "test",最後執行文件 prog.lua。(這裏,$ 是一個 shell 提示符。你的提示符可能與此不一樣。)
當選項 -f filename 被使用時,全部命令行中剩餘的參數放進一個叫作 arg 的表中被傳給 Lua 程序 filename 。在這個表中,字段 n 得到最後的參數的索引,字段 0 得到 "filename"。例如,在調用
$ lua a.lua -f b.lua t1 t3
解釋器首先執行文件 a.lua,而後新建一個表
arg = {"t1", "t3"; n = 2, [0] = "b.lua"}
最後,執行文件 b.lua。獨立的解釋器也提示了一個 getargs 函數能夠用來存取全部的命令行參數。例如,若是你這樣調用 Lua
$ lua -c a b
那麼一個 getargs 的調用在 a 或者 b 中將返回表
{[0] = "lua", [1] = "-c", [2] = "a", [3] = "b", n = 3}
在交互模式中,一個多行的句子能夠在行尾以反斜槓結束。若是全局變量 _PROMPT 被定義爲一個字符串,那麼它的值將被用作提示符。因此,提示符能夠直接在命令行中修改:
$ lua _PROMPT='myprompt> ' -i
或者在任意的 Lua 程序中給 _PROMPT 賦值。
在 Unix 系統中,Lua 腳本能夠被設置爲可執行的程序,使用 chmod +x 和 #! 格式,像在 #!/usr/local/bin/lua, 或者 #!/usr/local/bin/lua -f 去得到其它的參數。
--------------------------------------
鳴謝
--------------------------------------
做者要感謝 CENPES/PETROBROBAS 和 TeCGraf 一塊兒,使用該系統的早期版本,並提出寶貴意見。做者還要感謝 Carlos Henrique Levy,爲這個語言起了個名字。Lua 在葡萄牙語裏是月亮的意思。
--------------------------------------
其它(略)
--------------------------------------
和以前版本的不兼容及索引。
--------------------------------------
Lua 完整的語法
--------------------------------------
chunk ::= {stat [`;']}
block ::= chunk
stat ::= varlist1 `=' explist1
| functioncall
| do block end
| while exp1 do block end
| repeat block until exp1
| if exp1 then block {elseif exp1 then block} [else block] end
| return [explist1]
| break
| for `name' `=' exp1 `,' exp1 [`,' exp1] do block end
| for `name' `,' `name' in exp1 do block end
| function funcname `(' [parlist1] `)' block end
| local declist [init]
funcname ::= `name' | `name' `.' `name' | `name' `:' `name'
varlist1 ::= var {`,' var}
var ::= `name' | varorfunc `[' exp1 `]' | varorfunc `.' `name'
varorfunc ::= var | functioncall
declist ::= `name' {`,' `name'}
init ::= `=' explist1
explist1 ::= {exp1 `,'} exp
exp1 ::= exp
exp ::= nil | `number' | `literal' | var | function | upvalue
| functioncall | tableconstructor | `(' exp `)' | exp binop exp | unop exp
functioncall ::= varorfunc args | varorfunc `:' `name' args
args ::= `(' [explist1] `)' | tableconstructor | `literal'
function ::= function `(' [parlist1] `)' block end
parlist1 ::= `...' | `name' {`,' `name'} [`,' `...']
upvalue ::= `%' `name'
tableconstructor ::= `{' fieldlist `}' fieldlist ::= lfieldlist | ffieldlist | lfieldlist `;' ffieldlist | ffieldlist `;' lfieldlist lfieldlist ::= [lfieldlist1] ffieldlist ::= [ffieldlist1] lfieldlist1 ::= exp {`,' exp} [`,'] ffieldlist1 ::= ffield {`,' ffield} [`,'] ffield ::= `[' exp `]' `=' exp | `name' `=' exp
binop ::= `+' | `-' | `*' | `/' | `\^{ ' | `..'
| `<' | `<=' | `>' | `>=' | `==' | `\ { '=}
| and | or}
unop ::= `-' | not
shell