4 - 調(diào)試接口 The Debug Interface
Lua 沒有內(nèi)置的調(diào)試設(shè)施。它使用一種特殊的接口,這種接口依賴函數(shù)和 鉤子(hooks)。該接口允許構(gòu)造不同種類的調(diào)試器,分析器以及其他工具用以從解釋器得到所需的信息。
4.1 - 堆棧及函數(shù)信息 Stack and Function Information
得到解釋程序運(yùn)行時(shí)堆棧信息的主要函數(shù)是:
int lua_getstack (lua_State *L, int level, lua_Debug *ar);
這個(gè)函數(shù)用一個(gè)指定等級(jí)的函數(shù)的 activation record 的標(biāo)示符填充一個(gè) lua_Debug 結(jié)構(gòu),等級(jí) 0 是當(dāng)前運(yùn)行函數(shù),然而等級(jí) n+1 是在等級(jí) n 上調(diào)用的函數(shù)。當(dāng)沒有錯(cuò)誤發(fā)生時(shí),lua_getstack 返回 1;當(dāng)在比棧更深的等級(jí)上調(diào)用的時(shí)候,它返回 0;
lua_Debug 結(jié)構(gòu)被用來攜帶一個(gè)處于活動(dòng)狀態(tài)的函數(shù)的各種信息:
typedef struct lua_Debug { int event; const char *name; /* (n) */ const char *namewhat; /* (n) `global', `local', `field', `method' */ const char *what; /* (S) `Lua' function, `C' function, Lua `main' */ const char *source; /* (S) */ int currentline; /* (l) */ int nups; /* (u) number of upvalues */ int linedefined; /* (S) */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ ... } lua_Debug;
lua_getstack 只填充結(jié)構(gòu)的私有部分以備之后使用。要填充 lua_Debug 其他有用信息,調(diào)用
int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
這個(gè)函數(shù)發(fā)生錯(cuò)誤是返回 0 (舉個(gè)例子,一個(gè)無效的 what 選項(xiàng))。what 字符串中的每個(gè)字符選擇填充一些 ar 結(jié)構(gòu)的字段,把上面在 lua_Debug 定義中用圓括號(hào)括起來的字母作為指示: `S′ 填充在 source, linedefined 和 what 字段中;`l′ 填充在 currentline 字段中,等等...。而且,`f′ 將正在運(yùn)?性謁燃渡系暮谷攵顏弧?
想要從一個(gè)不處于活動(dòng)狀態(tài)的函數(shù)那得到信息(就是不在棧上的函數(shù)),你需要將其壓入棧并且用 >′ 作為 what 字符串的開始。舉個(gè)例子,要知道函數(shù) f 定義在第幾行,你需要這樣寫
lua_Debug ar; lua_pushstring(L, "f"); lua_gettable(L, LUA_GLOBALSINDEX); /* get global `f' */ lua_getinfo(L, ">S", &ar); printf("%d/n", ar.linedefined);
lua_Debug 的字段有如下的含義:
source 如果函數(shù)在一個(gè)字符串中定義,那么 source 就是那個(gè)字符串。如果函數(shù)定義在一個(gè)文件中,source 開始于一個(gè) `@′ 后面跟隨文件名。
short_src 一個(gè)可打印版的 source,用于錯(cuò)誤信息。
linedefined 函數(shù)定義起始的行號(hào)。
what 如果這是一個(gè)Lua函數(shù),顯示 "Lua" 字符串, "C" 為C 函數(shù),"main" 如果這是一個(gè)語句段的main部分,"tail" 如果這是一個(gè)做了尾部調(diào)用的函數(shù)。在后面的情況里,Lua 沒有其他關(guān)于這個(gè)函部的信息。
currentline 代表當(dāng)前函數(shù)執(zhí)行到的行數(shù)。如果沒有行信息可用,currentline 被設(shè)置為 -1。
name 一個(gè)所給函數(shù)合理的函數(shù)名。因?yàn)楹瘮?shù)在Lua中屬于第一類值,它們沒有固定的名字:一些函數(shù)可能是多個(gè)全局變量的值,其他的可能只儲(chǔ)存在一個(gè)表字段里。lua_getinfo 函數(shù)檢測函數(shù)如何被調(diào)用或者是否為一個(gè)全局變量的值以尋找一個(gè)合適的名字。如果找不到合適的名字,name 被設(shè)置為 NULL。
namewhat name 字段的解釋。根據(jù)函數(shù)如何被調(diào)用,namewhat 的值可以是 "global", "local", "method", "field" 或者 "" (空字符串)。(當(dāng)沒有其他可選項(xiàng)的時(shí)候Lua使用空字符串代替)
nups 函數(shù)上值的數(shù)量。
4.2 - 操作局部變量和上值 Manipulating Local Variables and Upvalues
為了更多的操作局部變量和上值,調(diào)試接口使用索引:第一個(gè)參數(shù)或者局部變量索引為 1,以此類推,直到最后一個(gè)活動(dòng)的局部變量。整個(gè)函數(shù)中的活動(dòng)的上值沒有特定的順序。
下面的函數(shù)允許操作一個(gè)所給激活記錄的局部變量:
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);
參數(shù) ar 必須是一個(gè)被前一個(gè) lua_getstack 調(diào)用填充的有效的激活記錄或者作為一個(gè)鉤子函數(shù)的參數(shù)(見 4.3)。lua_getlocal 獲得一個(gè)局部變量的索引 n,將變量的值壓入棧,并且返回變量名。lua_setlocal 從棧頂分配一個(gè)值給變量并且返回變量名。當(dāng)索引超過活動(dòng)的局部變量的數(shù)量時(shí),兩個(gè)函數(shù)都返回 NULL。
以下的函部可以操作所給函數(shù)的上值(不像局部變量,函數(shù)的上值即使在函數(shù)不處于活動(dòng)狀態(tài)的時(shí)候都可以被訪問):
const char *lua_getupvalue (lua_State *L, int funcindex, int n); const char *lua_setupvalue (lua_State *L, int funcindex, int n);
這些函數(shù)可以作為Lua 函數(shù)使用也可以作為C 函數(shù)使用。(作為Lua 函數(shù),上值是函數(shù)外部使用的局部變量,因此它被包含在函數(shù)閉包中。)funcindex 指向棧中的一個(gè)函數(shù)。lua_getupvalue 得到一個(gè)上值的索引 n,將上值的值壓入棧,并返回其變量名。lua_setupvalue 從棧頂分配一個(gè)值給上值并返回變量名。當(dāng)索引大于上值數(shù)量時(shí),兩個(gè)函數(shù)都返回 NULL。對(duì)于C 函數(shù)來說,這些函數(shù)使用空字符串作為所有上值的變量名。
作為一個(gè)例子,下面的函數(shù)列舉了所給等級(jí)的棧中的函數(shù)的所有局部變量名和上值變量名:
int listvars (lua_State *L, int level) { lua_Debug ar; int i; const char *name; if (lua_getstack(L, level, &ar) == 0) return 0; /* failure: no such level in the stack */ i = 1; while ((name = lua_getlocal(L, &ar, i++)) != NULL) { printf("local %d %s/n", i-1, name); lua_pop(L, 1); /* remove variable value */ } lua_getinfo(L, "f", &ar); /* retrieves function */ i = 1; while ((name = lua_getupvalue(L, -1, i++)) != NULL) { printf("upvalue %d %s/n", i-1, name); lua_pop(L, 1); /* remove upvalue value */ } return 1; }4.3 - 鉤子 Hooks
Lua offers a mechanism of hooks, which are user-defined C functionsthat are called during the program execution. A hook may be called infour different events: a call event, when Lua calls a function; a return event, when Lua returns from a function; a line event, when Lua starts executing a new line of code; and a count event, which happens every "count" instructions. Lua identifies these events with the following constants: LUA_HOOKCALL, LUA_HOOKRET (or LUA_HOOKTAILRET, see below), LUA_HOOKLINE, and LUA_HOOKCOUNT.
A hook has type lua_Hook, defined as follows:
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
You can set the hook with the following function:
int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
func is the hook. mask specifies on which events the hook will be called: It is formed by a disjunction of the constants LUA_MASKCALL, LUA_MASKRET, LUA_MASKLINE, and LUA_MASKCOUNT. The count argument is only meaningful when the mask includes LUA_MASKCOUNT. For each event, the hook is called as explained below:
The call hook is called when the interpreter calls a function. The hook is called just after Lua enters the new function.
The return hook is called when the interpreter returns from a function. The hook is called just before Lua leaves the function.
The line hook is called when the interpreter is aboutto start the execution of a new line of code, or when it jumps back inthe code (even to the same line). (This event only happens while Lua isexecuting a Lua function.)
The count hook is called after the interpreter executes every count instructions. (This event only happens while Lua is executing a Lua function.)
A hook is disabled by setting mask to zero.
You can get the current hook, the current mask, and the current count with the following functions:
lua_Hook lua_gethook (lua_State *L); int lua_gethookmask (lua_State *L); int lua_gethookcount (lua_State *L);
Whenever a hook is called, its ar argument has its field event set to the specific event that triggered the hook. Moreover, for line events, the field currentline is also set. To get the value of any other field in ar, the hook must call lua_getinfo. For return events, event may be LUA_HOOKRET, the normal value, or LUA_HOOKTAILRET. In the latter case, Lua is simulating a return from a function that did a tail call; in this case, it is useless to call lua_getinfo.
While Lua is running a hook, it disables other calls to hooks.Therefore, if a hook calls back Lua to execute a function or a chunk,that execution occurs without any calls to hooks.
The Lua team is grateful to Tecgraf for its continued support to Lua. We thank everyone at Tecgraf,specially the head of the group, Marcelo Gattass. At the risk ofomitting several names, we also thank the following individuals forsupporting, contributing to, and spreading the word about Lua: AlanWatson. André Clinio, André Costa, Antonio Scuri, Asko Kauppi, BretMogilefsky, Cameron Laird, Carlos Cassino, Carlos Henrique Levy,Claudio Terra, David Jeske, Ed Ferguson, Edgar Toernig, Erik Hougaard,Jim Mathies, John Belmonte, John Passaniti, John Roll, Jon Erickson,Jon Kleiser, Mark Ian Barlow, Nick Trout, Noemi Rodriguez, NormanRamsey, Philippe Lhoste, Renata Ratton, Renato Borges, RenatoCerqueira, Reuben Thomas, Stephan Herrmann, Steve Dekorte, ThatcherUlrich, Tomás Gorham, Vincent Penquerc'h. Thank you!
與以前版本的不兼容性 Incompatibilities with Previous Versions
Lua 5.0 是一個(gè)主版本,所有與 Lua 4.0 有一些地方不兼容。
與 v4.0 的不兼容性 Incompatibilities with version 4.0
語言上的變動(dòng)
整個(gè)標(biāo)簽方法模式被元表所替代。The whole tag-method scheme was replaced by metatables.
Function calls written between parentheses result in exactly one value.
A function call as the last in a list constructor (like {a,b,f()}) has all its return values inserted in the list.
The precedence of or is smaller than the precedence of and.
in, false, and true are reserved words.
The old construction for k,v in t, where t is a table, is deprecated (although it is still supported). Use for k,v in pairs(t) instead.
When a literal string of the form [[...]] starts with a newline, this newline is ignored.
Upvalues in the form %var are obsolete; use external local variables instead.
庫的變更
Most library functions now are defined inside tables. There is a compatibility script (compat.lua) that redefines most of them as global names.
In the math library, angles are expressed in radians. With the compatibility script (compat.lua), functions still work in degrees.
The call function is deprecated. Use f(unpack(tab)) instead of call(f, tab) for unprotected calls, or the new pcall function for protected calls.
dofile does not handle errors, but simply propagates them.
dostring is deprecated. Use loadstring instead.
The read option *w is obsolete.
The format option %n$ is obsolete.
API 上的改動(dòng)
lua_open 不再需要堆棧大小作為參數(shù)(堆棧是動(dòng)態(tài)的)。
lua_pushuserdata 已經(jīng)被廢除了。使用 lua_newuserdata 或 lua_pushlightuserdata 來代替它。
Lua 完整語法參考
chunk ::= {stat [`;′]} block ::= chunk stat ::= varlist1 `=′ explist1 | functioncall | do block end | while exp do block end | repeat block until exp | if exp then block {elseif exp then block} [else block] end | return [explist1] | break | for Name `=′ exp `,′ exp [`,′ exp] do block end | for Name {`,′ Name} in explist1 do block end | function funcname funcbody | local function Name funcbody | local namelist [init] funcname ::= Name {`.′ Name} [`:′ Name] varlist1 ::= var {`,′ var} var ::= Name | prefixexp `[′ exp `]′ | prefixexp `.′ Name namelist ::= Name {`,′ Name} init ::= `=′ explist1 explist1 ::= {exp `,′} exp exp ::= nil | false | true | Number | Literal | function | prefixexp | tableconstructor | exp binop exp | unop exp prefixexp ::= var | functioncall | `(′ exp `)′ functioncall ::= prefixexp args | prefixexp `:′ Name args args ::= `(′ [explist1] `)′ | tableconstructor | Literal function ::= function funcbody funcbody ::= `(′ [parlist1] `)′ block end parlist1 ::= Name {`,′ Name} [`,′ `...′] | `...′ tableconstructor ::= `{′ [fieldlist] `}′ fieldlist ::= field {fieldsep field} [fieldsep] field ::= `[′ exp `]′ `=′ exp | name `=′ exp | exp fieldsep ::= `,′ | `;′ binop ::= `+′ | `-′ | `*′ | `/′ | `^′ | `..′ | `<′ | `<=′ | `>′ | `>=′ | `==′ | `~=′ | and | or unop ::= `-′ | not
21:51 |
添加評(píng)論 |
發(fā)送消息 |
固定鏈接 |
查看引用通告 (0) | |
LuaLua - 獨(dú)立程序6 - Lua 獨(dú)立程序 Lua Stand-alone
盡管Lua被設(shè)計(jì)為一種內(nèi)嵌于C 語言宿主程序中的擴(kuò)展語言,它還是經(jīng)常被用作一個(gè)獨(dú)立程序語言。一個(gè)Lua的解釋程序?qū)ua作為一個(gè)獨(dú)立的語言,我們稱之為簡化的 lua,它提供了標(biāo)準(zhǔn)的發(fā)行版本。獨(dú)立的解釋器包含了所有標(biāo)準(zhǔn)庫加上反射性的調(diào)試接口。它的使用方式如下:
lua [options] [script [args]]
options 可以是以下內(nèi)容:
- 將 標(biāo)準(zhǔn)輸入(stdin) 當(dāng)作文件執(zhí)行;
-e stat 執(zhí)行字符串 stat;
-l file “需要”file 文件;
-i 在運(yùn)行腳本后進(jìn)入交互模式;
-v 打印版本信息;
-- 停止處理選項(xiàng)。
在停止處理選項(xiàng)后,lua 運(yùn)行所給的腳本,傳遞所給參數(shù) args。當(dāng)無參數(shù)調(diào)用時(shí),lua 就像 stdin 是程序的終結(jié)時(shí) lua -v -i 所表現(xiàn)的一樣,而且還與 lua- 一樣。
Before running any argument, the interpreter checks for an environment variable LUA_INIT. If its format is @filename, then lua executes the file. Otherwise, lua executes the string itself.
All options are handled in order, except -i. For instance, an invocation like
$ lua -e'a=1' -e 'print(a)' script.lua
will first set a to 1, then print a, and finally run the file script.lua. (Here, $ is the shell prompt. Your prompt may be different.)
Before starting to run the script, lua collects all arguments in the command line in a global table called arg. The script name is stored in index 0, the first argument after the script name goes to index 1, and so on. The field ngets the number of arguments after the script name. Any argumentsbefore the script name (that is, the interpreter name plus the options)go to negative indices. For instance, in the call
$ lua -la.lua b.lua t1 t2
the interpreter first runs the file a.lua, then creates a table
arg = { [-2] = "lua", [-1] = "-la.lua", [0] = "b.lua", [1] = "t1", [2] = "t2"; n = 2 }
and finally runs the file b.lua.
在交互模式中,如果你寫入了一個(gè)不完整的語句,解釋器將等待你的完成。
If the global variable _PROMPT is defined as astring, then its value is used as the prompt. Therefore, the prompt canbe changed directly on the command line:
$ lua -e"_PROMPT='myprompt> '" -i
(the outer pair of quotes is for the shell, the inner is for Lua), or in any Lua programs by assigning to _PROMPT. Note the use of -i to enter interactive mode; otherwise, the program would end just after the assignment to _PROMPT.
在Unix系統(tǒng)中,Lua腳本可以用 chmod +x 將其變成可執(zhí)行程序,并且通過 #! 形式,例如
#!/usr/local/bin/lua
(當(dāng)然,Lua解釋器的位置可能有所不同,如果 lua 在你的 PATH 中,那么
#!/usr/bin/env lua
就是一個(gè)更通用的解決方案。)
21:48 |
添加評(píng)論 |
發(fā)送消息 |
固定鏈接 |
查看引用通告 (0) | |
LuaLua - 標(biāo)準(zhǔn)庫5 - 標(biāo)準(zhǔn)庫
The standard libraries provide useful functions that are implementeddirectly through the C API. Some of these functions provide essentialservices to the language (e.g., type and getmetatable);others provide access to "outside" services (e.g., I/O); and otherscould be implemented in Lua itself, but are quite useful or havecritical performance to deserve an implementation in C (e.g., sort).
All libraries are implemented through the official C API and areprovided as separate C modules. Currently, Lua has the followingstandard libraries:
基本庫 basic library;
字符串操作 string manipulation;
表操作 table manipulation;
數(shù)學(xué)函數(shù) (sin, log 等等)mathematical functions (sin, log, etc.);
輸入輸出 input and output;
操作系統(tǒng)機(jī)制 operating system facilities;
調(diào)試機(jī)制 debug facilities.
Except for the basic library, each library provides all its functions as fields of a global table or as methods of its objects.
To have access to these libraries, the C host program must first call the functions luaopen_base (for the basic library), luaopen_string (for the string library), luaopen_table (for the table library), luaopen_math (for the mathematical library), luaopen_io (for the I/O and the Operating System libraries), and luaopen_debug (for the debug library). These functions are declared in lualib.h.
5.1 - 基本函數(shù) Basic Functions
The basic library provides some core functions to Lua. If you do notinclude this library in your application, you should check carefullywhether you need to provide some alternative implementation for some ofits facilities.
assert (v [, message])
Issues an error when the value of its argument v is nil or false; otherwise, returns this value. message is an error message; when absent, it defaults to "assertion failed!"
collectgarbage ([limit])
Sets the garbage-collection threshold to the given limit (in Kbytes)and checks it against the byte counter. If the new threshold is smallerthan the byte counter, then Lua immediately runs the garbage collector(see 2.9). If limit is absent, it defaults to zero (thus forcing a garbage-collection cycle).
dofile (filename)
Opens the named file and executes its contents as a Lua chunk. When called without arguments, dofile executes the contents of the standard input (stdin). Returns any value returned by the chunk. In case of errors, dofile propagates the error to its caller (that is, it does not run in protected mode).
Terminates the last protected function called and returns message as the error message. Function error never returns.
The level argument specifies where the error message points the error. With level 1 (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on.
_G
A global variable (not a function) that holds the global environment (that is, _G._G = _G). Lua itself does not use this variable; changing its value does not affect any environment. (Use setfenv to change environments.)
getfenv (f)
Returns the current environment in use by the function. f can be a Lua function or a number, which specifies the function at that stack level: Level 1 is the function calling getfenv. If the given function is not a Lua function, or if f is 0, getfenv returns the global environment. The default for f is 1.
If the environment has a "__fenv" field, returns the associated value, instead of the environment.
If the object does not have a metatable, returns nil. Otherwise, if the object's metatable has a "__metatable" field, returns the associated value. Otherwise, returns the metatable of the given object.
gcinfo ()
Returns two results: the number of Kbytes of dynamic memory that Luais using and the current garbage collector threshold (also in Kbytes).
ipairs (t)
Returns an iterator function, the table t, and 0, so that the construction
for i,v in ipairs(t) do ... end
will iterate over the pairs (1,t[1]), (2,t[2]), ..., up to the first integer key with a nil value in the table.
loadfile (filename)
Loads a file as a Lua chunk (without running it). If there are noerrors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment.
loadlib (libname, funcname)
Links the program with the dynamic C library libname. Inside this library, looks for a function funcname and returns this function as a C function.
libname must be the complete file name of the C library, including any eventual path and extension.
This function is not supported by ANSI C. As such, it is onlyavailable on some platforms (Windows, Linux, Solaris, BSD, plus otherUnix systems that support the dlfcn standard).
loadstring (string [, chunkname])
Loads a string as a Lua chunk (without running it). If there are noerrors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment.
The optional parameter chunkname is the name to be used in error messages and debug information.
To load and run a given string, use the idiom
assert(loadstring(s))()next (table [, index])
Allows a program to traverse all fields of a table. Its firstargument is a table and its second argument is an index in this table. next returns the next index of the table and the value associated with the index. When called with nil as its second argument, next returns the first index of the table and its associated value. When called with the last index, or with nil in an empty table, next returns nil. If the second argument is absent, then it is interpreted as nil.
Lua has no declaration of fields; There is no difference between a field not present in a table or a field with value nil. Therefore, next only considers fields with non-nil values. The order in which the indices are enumerated is not specified, even for numeric indices. (To traverse a table in numeric order, use a numerical for or the ipairs function.)
The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table.
pairs (t)
Returns the next function and the table t (plus a nil), so that the construction
for k,v in pairs(t) do ... end
will iterate over all key-value pairs of table t.
Calls function f with the given arguments in protected mode. That means that any error inside f is not propagated; instead, pcall catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, pcall also returns all results from the call, after this first result. In case of any error, pcall returns false plus the error message.
print (e1, e2, ...)
Receives any number of arguments, and prints their values in stdout, using the tostringfunction to convert them to strings. This function is not intended forformatted output, but only as a quick way to show a value, typicallyfor debugging. For formatted output, use format (see 5.3).
rawequal (v1, v2)
Checks whether v1 is equal to v2, without invoking any metamethod. Returns a boolean.
rawget (table, index)
Gets the real value of table[index], without invoking any metamethod. table must be a table; index is any value different from nil.
rawset (table, index, value)
Sets the real value of table[index] to value, without invoking any metamethod. table must be a table, index is any value different from nil, and value is any Lua value.
require (packagename)
Loads the given package. The function starts by looking into the table _LOADED to determine whether packagename is already loaded. If it is, then requirereturns the value that the package returned when it was first loaded.Otherwise, it searches a path looking for a file to load.
If the global variable LUA_PATH is a string, this string is the path. Otherwise, require tries the environment variable LUA_PATH. As a last resort, it uses the predefined path "?;?.lua".
The path is a sequence of templates separated by semicolons. For each template, require will change each interrogation mark in the template to packagename, and then will try to load the resulting file name. So, for instance, if the path is
"./?.lua;./?.lc;/usr/local/?/?.lua;/lasttry"
a require "mod" will try to load the files ./mod.lua, ./mod.lc, /usr/local/mod/mod.lua, and /lasttry, in that order.
The function stops the search as soon as it can load a file, and then it runs the file. After that, it associates, in table _LOADED, the package name with the value that the package returned, and returns that value. If the package returns nil (or no value), require converts this value to true. If the package returns false, require also returns false. However, as the mark in table _LOADED is false, any new attempt to reload the file will happen as if the package was not loaded (that is, the package will be loaded again).
If there is any error loading or running the file, or if it cannot find any file in the path, then require signals an error.
While running a file, require defines the global variable _REQUIREDNAME with the package name. The package being loaded always runs within the global environment.
Sets the current environment to be used by the given function. f can be a Lua function or a number, which specifies the function at that stack level: Level 1 is the function calling setfenv.
As a special case, when f is 0 setfenv changes the global environment of the running thread.
If the original environment has a "__fenv" field, setfenv raises an error.
setmetatable (table, metatable)
Sets the metatable for the given table. (You cannot change the metatable of a userdata from Lua.) If metatable is nil, removes the metatable of the given table. If the original metatable has a "__metatable" field, raises an error.
tonumber (e [, base])
Tries to convert its argument to a number. If the argument is already a number or a string convertible to a number, then tonumber returns that number; otherwise, it returns nil.
An optional argument specifies the base to interpret thenumeral. The base may be any integer between 2 and 36, inclusive. Inbases above 10, the letter `A′ (in either upper or lower case) represents 10, `B′ represents 11, and so forth, with `Z′ representing 35. In base 10 (the default), the number may have a decimal part, as well as an optional exponent part (see 2.2.1). In other bases, only unsigned integers are accepted.
tostring (e)
Receives an argument of any type and converts it to a string in areasonable format. For complete control of how numbers are converted,use format (see 5.3).
If the metatable of e has a "__tostring" field, tostring calls the corresponding value with e as argument, and uses the result of the call as its result.
Returns the type of its only argument, coded as a string. The possible results of this function are "nil" (a string, not the value nil), "number", "string", "boolean, "table", "function", "thread", and "userdata".
unpack (list)
Returns all elements from the given list. This function is equivalent to
return list[1], list[2], ..., list[n]
except that the above code can be written only for a fixed n. The number n is the size of the list, as defined for the table.getn function.
_VERSION
A global variable (not a function) that holds a string containingthe current interpreter version. The current content of this string is "Lua 5.0".
xpcall (f, err)
This function is similar to pcall, except that you can set a new error handler.
xpcall calls function f in protected mode, using err as the error handler. Any error inside f is not propagated; instead, xpcall catches the error, calls the errfunction with the original error object, and returns a status code. Itsfirst result is the status code (a boolean), which is true if the callsucceeds without errors. In such case, xpcall also returns all results from the call, after this first result. In case of any error, xpcall returns false plus the result from err.
5.2 - Coroutine Manipulation
The operations related to coroutines comprise a sub-library of the basic library and come inside the table coroutine. See 2.10 for a general description of coroutines.
coroutine.create (f)
Creates a new coroutine, with body f. f must be a Lua function. Returns this new coroutine, an object with type "thread".
coroutine.resume (co, val1, ...)
Starts or continues the execution of coroutine co. The first time you resume a coroutine, it starts running its body. The arguments val1, ... go as the arguments to the body function. If the coroutine has yielded, resume restarts it; the arguments val1, ... go as the results from the yield.
If the coroutine runs without any errors, resume returns true plus any values passed to yield (if the coroutine yields) or any values returned by the body function (if the coroutine terminates). If there is any error, resume returns false plus the error message.
coroutine.status (co)
Returns the status of coroutine co, as a string: "running", if the coroutine is running (that is, it called status); "suspended", if the coroutine is suspended in a call to yield, or if it has not started running yet; and "dead" if the coroutine has finished its body function, or if it has stopped with an error.
coroutine.wrap (f)
Creates a new coroutine, with body f. fmust be a Lua function. Returns a function that resumes the coroutineeach time it is called. Any arguments passed to the function behave asthe extra arguments to resume. Returns the same values returned by resume, except the first boolean. In case of error, propagates the error.
coroutine.yield (val1, ...)
Suspends the execution of the calling coroutine. The coroutinecannot be running neither a C function, nor a metamethod, nor aniterator. Any arguments to yield go as extra results to resume.
5.3 - String Manipulation
This library provides generic functions for string manipulation,such as finding and extracting substrings, and pattern matching. Whenindexing a string in Lua, the first character is at position 1 (notat 0, as in C). Indices are allowed to be negative and are interpretedas indexing backwards, from the end of the string. Thus, the lastcharacter is at position -1, and so on.
The string library provides all its functions inside the table string.
string.byte (s [, i])
Returns the internal numerical code of the i-th character of s, or nil if the index is out of range. If i is absent, then it is assumed to be 1. i may be negative.
Note that numerical codes are not necessarily portable across platforms.
string.char (i1, i2, ...)
Receives 0 or more integers. Returns a string with length equal tothe number of arguments, in which each character has the internalnumerical code equal to its correspondent argument.
Note that numerical codes are not necessarily portable across platforms.
string.dump (function)
Returns a binary representation of the given function, so that a later loadstring on that string returns a copy of the function. function must be a Lua function without upvalues.
string.find (s, pattern [, init [, plain]])
Looks for the first match of pattern in the string s. If it finds one, then find returns the indices of s where this occurrence starts and ends; otherwise, it returns nil. If the pattern specifies captures (see string.gsub below), the captured strings are returned as extra results. A third, optional numerical argument init specifies where to start the search; it may be negative and its default value is 1. A value of true as a fourth, optional argument plain turns off the pattern matching facilities, so the function does a plain "find substring" operation, with no characters in pattern being considered "magic". Note that if plain is given, then init must be given too.
string.len (s)
Receives a string and returns its length. The empty string "" has length 0. Embedded zeros are counted, so "a/000b/000c" has length 5.
string.lower (s)
Receives a string and returns a copy of that string with alluppercase letters changed to lowercase. All other characters are leftunchanged. The definition of what is an uppercase letter depends on thecurrent locale.
string.rep (s, n)
Returns a string that is the concatenation of n copies of the string s.
string.sub (s, i [, j])
Returns the substring of s that starts at i and continues until j; i and j may be negative. If j is absent, then it is assumed to be equal to -1 (which is the same as the string length). In particular, the call string.sub(s,1,j) returns a prefix of s with length j, and string.sub(s, -i) returns a suffix of s with length i.
string.upper (s)
Receives a string and returns a copy of that string with alllowercase letters changed to uppercase. All other characters are leftunchanged. The definition of what is a lowercase letter depends on thecurrent locale.
Returns a formatted version of its variable number of argumentsfollowing the description given in its first argument (which must be astring). The format string follows the same rules as the printf family of standard C functions. The only differences are that the options/modifiers *, l, L, n, p, and h are not supported, and there is an extra option, q. The qoption formats a string in a form suitable to be safely read back bythe Lua interpreter: The string is written between double quotes, andall double quotes, newlines, and backslashes in the string arecorrectly escaped when written. For instance, the call
string.format('%q', 'a string with "quotes" and /n new line')
will produce the string:
"a string with /"quotes/" and / new line"
The options c, d, E, e, f, g, G, i, o, u, X, and x all expect a number as argument, whereas q and s expect a string. The * modifier can be simulated by building the appropriate format string. For example, "%*g" can be simulated with "%"..width.."g".
String values to be formatted with %s cannot contain embedded zeros.
string.gfind (s, pat)
Returns an iterator function that, each time it is called, returns the next captures from pattern pat over string s.
If pat specifies no captures, then the whole match is produced in each call.
As an example, the following loop
s = "hello world from Lua" for w in string.gfind(s, "%a+") do print(w) end
will iterate over all the words from string s, printing one per line. The next example collects all pairs key=value from the given string into a table:
t = {} s = "from=world, to=Lua" for k, v in string.gfind(s, "(%w+)=(%w+)") do t[k] = v endstring.gsub (s, pat, repl [, n])
Returns a copy of s in which all occurrences of the pattern pat have been replaced by a replacement string specified by repl. gsub also returns, as a second value, the total number of substitutions made.
If repl is a string, then its value is used for replacement. Any sequence in repl of the form %n, with n between 1 and 9, stands for the value of the n-th captured substring (see below).
If repl is a function, then this function is calledevery time a match occurs, with all captured substrings passed asarguments, in order; if the pattern specifies no captures, then thewhole match is passed as a sole argument. If the value returned by thisfunction is a string, then it is used as the replacement string;otherwise, the replacement string is the empty string.
The optional last parameter n limits the maximum number of substitutions to occur. For instance, when n is 1 only the first occurrence of pat is replaced.
Here are some examples:
x = string.gsub("hello world", "(%w+)", "%1 %1") --> x="hello hello world world" x = string.gsub("hello world", "(%w+)", "%1 %1", 1) --> x="hello hello world" x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1") --> x="world hello Lua from" x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv) --> x="home = /home/roberto, user = roberto" x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s) return loadstring(s)() end) --> x="4+5 = 9" local t = {name="lua", version="5.0"} x = string.gsub("$name_$version.tar.gz", "%$(%w+)", function (v) return t[v] end) --> x="lua_5.0.tar.gz"Patterns
A character class is used to represent a set of characters. The following combinations are allowed in describing a character class:
x (where x is not one of the magic characters ^$()%.[]*+-?) --- represents the character x itself.
. --- (a dot) represents all characters.
%a --- represents all letters.
%c --- represents all control characters.
%d --- represents all digits.
%l --- represents all lowercase letters.
%p --- represents all punctuation characters.
%s --- represents all space characters.
%u --- represents all uppercase letters.
%w --- represents all alphanumeric characters.
%x --- represents all hexadecimal digits.
%z --- represents the character with representation 0.
%x (where x is any non-alphanumeric character) --- represents the character x. This is the standard way to escape the magic characters. Any punctuation character (even the non magic) can be preceded by a `%′ when used to represent itself in a pattern.
[set] --- represents the class which is the union of all characters in set. A range of characters may be specified by separating the end characters of the range with a `-′. All classes %x described above may also be used as components in set. All other characters in set represent themselves. For example, [%w_] (or [_%w]) represents all alphanumeric characters plus the underscore, [0-7] represents the octal digits, and [0-7%l%-] represents the octal digits plus the lowercase letters plus the `-′ character.
The interaction between ranges and classes is not defined. Therefore, patterns like [%a-z] or [a-%%] have no meaning.
[^set] --- represents the complement of set, where set is interpreted as above.
For all classes represented by single letters (%a, %c, etc.), the corresponding uppercase letter represents the complement of the class. For instance, %S represents all non-space characters.
The definitions of letter, space, and other character groups depend on the current locale. In particular, the class [a-z] may not be equivalent to %l. The second form should be preferred for portability.
A pattern item may be
a single character class, which matches any single character in the class;
a single character class followed by `*′, whichmatches 0 or more repetitions of characters in the class. Theserepetition items will always match the longest possible sequence;
a single character class followed by `+′, whichmatches 1 or more repetitions of characters in the class. Theserepetition items will always match the longest possible sequence;
a single character class followed by `-′, which also matches 0 or more repetitions of characters in the class. Unlike `*′, these repetition items will always match the shortest possible sequence;
a single character class followed by `?′, which matches 0 or 1 occurrence of a character in the class;
%n, for n between 1 and 9; such item matches a substring equal to the n-th captured string (see below);
%bxy, where x and y are two distinct characters; such item matches strings that start with x, end with y, and where the x and y are balanced. This means that, if one reads the string from left to right, counting +1 for an x and -1 for a y, the ending y is the first y where the count reaches 0. For instance, the item %b() matches s with balanced parentheses.
A pattern is a sequence of pattern items. A `^′ at the beginning of a pattern anchors the match at the beginning of the subject string. A `$′ at the end of a pattern anchors the match at the end of the subject string. At other positions, `^′ and `$′ have no special meaning and represent themselves.
A pattern may contain sub-patterns enclosed in parentheses; they describe captures. When a match succeeds, the substrings of the subject string that match captures are stored (captured) for future use. Captures are numbered according to their left parentheses. For instance, in the pattern "(a*(.)%w(%s*))", the part of the string matching "a*(.)%w(%s*)" is stored as the first capture (and therefore has number 1); the character matching . is captured with number 2, and the part matching %s* has number 3.
As a special case, the empty capture () captures the current string position (a number). For instance, if we apply the pattern "()aa()" on the string "flaaap", there will be two captures: 3 and 5.
A pattern cannot contain embedded zeros. Use %z instead.
5.4 - Table Manipulation
This library provides generic functions for table manipulation. It provides all its functions inside the table table.
Most functions in the table library assume that the tablerepresents an array or a list. For those functions, an importantconcept is the size of the array. There are three ways to specify that size:
the field "n" --- When the table has a field "n" with a numerical value, that value is assumed as its size.
setn --- You can call the table.setn function to explicitly set the size of a table.
implicit size --- Otherwise, the size of the object is one less the first integer index with a nil value.
For more details, see the descriptions of the table.getn and table.setn functions.
table.concat (table [, sep [, i [, j]]])
Returns table[i]..sep..table[i+1] ... sep..table[j]. The default value for sep is the empty string, the default for i is 1, and the default for j is the size of the table. If i is greater than j, returns the empty string.
table.foreach (table, f)
Executes the given f over all elements of table. For each element, f is called with the index and respective value as arguments. If f returns a non-nil value, then the loop is broken, and this value is returned as the final value of foreach.
See the next function for extra information about table traversals.
table.foreachi (table, f)
Executes the given f over the numerical indices of table. For each index, f is called with the index and respective value as arguments. Indices are visited in sequential order, from 1 to n, where n is the size of the table (see 5.4). If f returns a non-nil value, then the loop is broken and this value is returned as the result of foreachi.
Returns the size of a table, when seen as a list. If the table has an n field with a numeric value, this value is the size of the table. Otherwise, if there was a previous call to table.setn over this table, the respective value is returned. Otherwise, the size is one less the first integer index with a nil value.
table.sort (table [, comp])
Sorts table elements in a given order, in-place, from table[1] to table[n], where n is the size of the table (see 5.4). If compis given, then it must be a function that receives two table elements,and returns true when the first is less than the second (so that not comp(a[i+1],a[i]) will be true after the sort). If comp is not given, then the standard Lua operator < is used instead.
The sort algorithm is not stable, that is, elements considered equal by the given order may have their relative positions changed by the sort.
table.insert (table, [pos,] value)
Inserts element value at position pos in table, shifting up other elements to open space, if necessary. The default value for pos is n+1, where n is the size of the table (see 5.4), so that a call table.insert(t,x) inserts x at the end of table t. This function also updates the size of the table by calling table.setn(table, n+1).
table.remove (table [, pos])
Removes from table the element at position pos,shifting down other elements to close the space, if necessary. Returnsthe value of the removed element. The default value for pos is n, where n is the size of the table (see 5.4), so that a call table.remove(t) removes the last element of table t. This function also updates the size of the table by calling table.setn(table, n-1).
table.setn (table, n)
Updates the size of a table. If the table has a field "n" with a numerical value, that value is changed to the given n. Otherwise, it updates an internal state so that subsequent calls to table.getn(table) return n.
5.5 - Mathematical Functions
This library is an interface to most of the functions of thestandard C math library. (Some have slightly different names.) Itprovides all its functions inside the table math. In addition, it registers the global __pow for the binary exponentiation operator ^, so that x^y returns xy. The library provides the following functions:
math.abs math.acos math.asin math.atan math.atan2 math.ceil math.cos math.deg math.exp math.floor math.log math.log10 math.max math.min math.mod math.pow math.rad math.sin math.sqrt math.tan math.frexp math.ldexp math.random math.randomseed
plus a variable math.pi. Most of them are onlyinterfaces to the corresponding functions in the C library. Alltrigonometric functions work in radians (previous versions of Lua useddegrees). The functions math.deg and math.rad convert between radians and degrees.
The function math.max returns the maximum value of its numeric arguments. Similarly, math.min computes the minimum. Both can be used with 1, 2, or more arguments.
The functions math.random and math.randomseed are interfaces to the simple random generator functions rand and srand that are provided by ANSI C. (No guarantees can be given for their statistical properties.) When called without arguments, math.random returns a pseudo-random real number in the range [0,1). When called with a number n, math.random returns a pseudo-random integer in the range [1,n]. When called with two arguments, l and u, math.random returns a pseudo-random integer in the range [l,u]. The math.randomseed function sets a "seed" for the pseudo-random generator: Equal seeds produce equal sequences of numbers.
5.6 - Input and Output Facilities
The I/O library provides two different styles for file manipulation.The first one uses implicit file descriptors, that is, there areoperations to set a default input file and a default output file, andall input/output operations are over those default files. The secondstyle uses explicit file descriptors.
When using implicit file descriptors, all operations are supplied by table io. When using explicit file descriptors, the operation io.open returns a file descriptor and then all operations are supplied as methods by the file descriptor.
The table io also provides three predefined file descriptors with their usual meanings from C: io.stdin, io.stdout, and io.stderr.
A file handle is a userdata containing the file stream (FILE*), with a distinctive metatable created by the I/O library.
Unless otherwise stated, all I/O functions return nil on failure (plus an error message as a second result) and some value different from nil on success.
io.close ([file])
Equivalent to file:close. Without a file, closes the default output file.
io.flush ()
Equivalent to file:flush over the default output file.
io.input ([file])
When called with a file name, it opens the named file (in textmode), and sets its handle as the default input file. When called witha file handle, it simply sets that file handle as the default inputfile. When called without parameters, it returns the current defaultinput file.
In case of errors this function raises the error, instead of returning an error code.
io.lines ([filename])
Opens the given file name in read mode and returns an iteratorfunction that, each time it is called, returns a new line from thefile. Therefore, the construction
for line in io.lines(filename) do ... end
will iterate over all lines of the file. When the iterator function detects the end of file, it returns nil (to finish the loop) and automatically closes the file.
The call io.lines() (without a file name) is equivalent to io.input():lines(), that is, it iterates over the lines of the default input file.
io.open (filename [, mode])
This function opens a file, in the mode specified in the string mode. It returns a new file handle, or, in case of errors, nil plus an error message.
The mode string can be any of the following:
"r" read mode (the default);
"w" write mode;
"a" append mode;
"r+" update mode, all previous data is preserved;
"w+" update mode, all previous data is erased;
"a+" append update mode, previous data is preserved, writing is only allowed at the end of file.
The mode string may also have a b at theend, which is needed in some systems to open the file in binary mode.This string is exactly what is used in the standard C function fopen.
io.output ([file])
Similar to io.input, but operates over the default output file.
io.read (format1, ...)
Equivalent to io.input():read.
io.tmpfile ()
Returns a handle for a temporary file. This file is open in update mode and it is automatically removed when the program ends.
io.type (obj)
Checks whether obj is a valid file handle. Returns the string "file" if obj is an open file handle, "closed file" if obj is a closed file handle, and nil if obj is not a file handle.
io.write (value1, ...)
Equivalent to io.output():write.
file:close ()
Closes file.
file:flush ()
Saves any written data to file.
file:lines ()
Returns an iterator function that, each time it is called, returns a new line from the file. Therefore, the construction
for line in file:lines() do ... end
will iterate over all lines of the file. (Unlike io.lines, this function does not close the file when the loop ends.)
file:read (format1, ...)
Reads the file file, according to the given formats,which specify what to read. For each format, the function returns astring (or a number) with the characters read, or nil if itcannot read data with the specified format. When called withoutformats, it uses a default format that reads the entire next line (seebelow).
The available formats are
"*n" reads a number; this is the only format that returns a number instead of a string.
"*a" reads the whole file, starting at the current position. On end of file, it returns the empty string.
"*l" reads the next line (skipping the end of line), returning nil on end of file. This is the default format.
number reads a string with up to that number of characters, returning nil on end of file. If number is zero, it reads nothing and returns an empty string, or nil on end of file.
file:seek ([whence] [, offset])
Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence, as follows:
"set" base is position 0 (beginning of the file);
"cur" base is current position;
"end" base is end of file;
In case of success, function seek returns the final file position, measured in bytes from the beginning of the file. If this function fails, it returns nil, plus a string describing the error.
The default value for whence is "cur", and for offset is 0. Therefore, the call file:seek() returns the current file position, without changing it; the call file:seek("set") sets the position to the beginning of the file (and returns 0); and the call file:seek("end") sets the position to the end of the file, and returns its size.
file:write (value1, ...)
Writes the value of each of its arguments to the filehandle file. The arguments must be strings or numbers. To write other values, use tostring or string.format before write.
5.7 - Operating System Facilities
This library is implemented through table os.
os.clock ()
Returns an approximation of the amount of CPU time used by the program, in seconds.
os.date ([format [, time]])
Returns a string or a table containing date and time, formatted according to the given string format.
If the time argument is present, this is the time to be formatted (see the os.time function for a description of this value). Otherwise, date formats the current time.
If format starts with `!′, then the date is formatted in Coordinated Universal Time. After that optional character, if format is *t, then date returns a table with the following fields: year (four digits), month (1--12), day (1--31), hour (0--23), min (0--59), sec (0--61), wday (weekday, Sunday is 1), yday (day of the year), and isdst (daylight saving flag, a boolean).
If format is not *t, then date returns the date as a string, formatted according to the same rules as the C function strftime.
When called without arguments, date returns a reasonable date and time representation that depends on the host system and on the current locale (that is, os.date() is equivalent to os.date("%c")).
os.difftime (t2, t1)
Returns the number of seconds from time t1 to time t2. In Posix, Windows, and some other systems, this value is exactly t2-t1.
os.execute (command)
This function is equivalent to the C function system. It passes command to be executed by an operating system shell. It returns a status code, which is system-dependent.
os.exit ([code])
Calls the C function exit, with an optional code, to terminate the host program. The default value for code is the success code.
os.getenv (varname)
Returns the value of the process environment variable varname, or nil if the variable is not defined.
os.remove (filename)
Deletes the file with the given name. If this function fails, it returns nil, plus a string describing the error.
os.rename (oldname, newname)
Renames file named oldname to newname. If this function fails, it returns nil, plus a string describing the error.
os.setlocale (locale [, category])
Sets the current locale of the program. locale is a string specifying a locale; category is an optional string describing which category to change: "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category is "all". The function returns the name of the new locale, or nil if the request cannot be honored.
os.time ([table])
Returns the current time when called without arguments, or a timerepresenting the date and time specified by the given table. This tablemust have fields year, month, and day, and may have fields hour, min, sec, and isdst (for a description of these fields, see the os.date function).
The returned value is a number, whose meaning depends on yoursystem. In Posix, Windows, and some other systems, this number countsthe number of seconds since some given start time (the "epoch"). Inother systems, the meaning is not specified, and the number returned bytime can be used only as an argument to date and difftime.
os.tmpname ()
Returns a string with a file name that can be used for a temporaryfile. The file must be explicitly opened before its use and removedwhen no longer needed.
This function is equivalent to the tmpnamC function, and many people (and even some compilers!) advise againstits use, because between the time you call this function and the timeyou open the file, it is possible for another process to create a filewith the same name.
5.8 - The Reflexive Debug Interface
The debug library provides the functionality of thedebug interface to Lua programs. You should exert care when using thislibrary. The functions provided here should be used exclusively fordebugging and similar tasks, such as profiling. Please resist thetemptation to use them as a usual programming tool: They can be veryslow. Moreover, setlocal and getlocal violate the privacy of local variables and therefore can compromise some otherwise secure code.
All functions in this library are provided inside a debug table.
debug.debug ()
Enters an interactive mode with the user, running each string thatthe user enters. Using simple commands and other debug facilities, theuser can inspect global and local variables, change their values,evaluate s, and so on. A line containing only the word cont finishes this function, so that the caller continues its execution.
Note that commands for debug.debug are not lexically nested with any function, so they have no direct access to local variables.
debug.gethook ()
Returns the current hook settings, as three values: the current hookfunction, the current hook mask, and the current hook count (as set bythe debug.sethook function).
debug.getinfo (function [, what])
This function returns a table with information about a function. Youcan give the function directly, or you can give a number as the valueof function, which means the function running at level function of the call stack: Level 0 is the current function (getinfo itself); level 1 is the function that called getinfo; and so on. If function is a number larger than the number of active functions, then getinfo returns nil.
The returned table contains all the fields returned by lua_getinfo, with the string what describing which fields to fill in. The default for what is to get all information available. If present, the option `f′ adds a field named func with the function itself.
For instance, the debug.getinfo(1,"n").name returns the name of the current function, if a reasonable name can be found, and debug.getinfo(print) returns a table with all available information about the print function.
debug.getlocal (level, local)
This function returns the name and the value of the local variable with index local of the function at level levelof the stack. (The first parameter or local variable has index 1, andso on, until the last active local variable.) The function returns nil if there is no local variable with the given index, and raises an error when called with a level out of range. (You can call debug.getinfo to check whether the level is valid.)
debug.getupvalue (func, up)
This function returns the name and the value of the upvalue with index up of the function func. The function returns nil if there is no upvalue with the given index.
debug.setlocal (level, local, value)
This function assigns the value value to the local variable with index local of the function at level level of the stack. The function returns nil if there is no local variable with the given index, and raises an error when called with a level out of range. (You can call getinfo to check whether the level is valid.)
debug.setupvalue (func, up, value)
This function assigns the value value to the upvalue with index up of the function func. The function returns nil if there is no upvalue with the given index.
debug.sethook (hook, mask [, count])
Sets the given function as a hook. The string mask and the number count describe when the hook will be called. The string mask may have the following characters, with the given meaning:
"c" The hook is called every time Lua calls a function;
"r" The hook is called every time Lua returns from a function;
"l" The hook is called every time Lua enters a new line of code.
With a count different from zero, the hook is called after every count instructions.
When called without arguments, the debug.sethook function turns off the hook.
When the hook is called, its first parameter is always a string describing the event that triggered its call: "call", "return" (or "tail return"), "line", and "count". Moreover, for line events, it also gets as its second parameter the new line number. Inside a hook, you can call getinfo with level 2 to get more information about the running function (level 0 is the getinfo function, and level 1 is the hook function), unless the event is "tail return". In this case, Lua is only simulating the return, and a call to getinfo will return invalid data.
debug.traceback ([message])
Returns a string with a traceback of the call stack. An optional message string is appended at the beginning of the traceback. This function is typically used with xpcall to produce better error messages.
21:43 |
添加評(píng)論 |
發(fā)送消息 |
固定鏈接 |
查看引用通告 (0) | |
LuaLua - 調(diào)試接口4 - 調(diào)試接口 The Debug Interface
Lua 沒有內(nèi)置的調(diào)試設(shè)施。它使用一種特殊的接口,這種接口依賴函數(shù)和 鉤子(hooks)。該接口允許構(gòu)造不同種類的調(diào)試器,分析器以及其他工具用以從解釋器得到所需的信息。
4.1 - 堆棧及函數(shù)信息 Stack and Function Information
得到解釋程序運(yùn)行時(shí)堆棧信息的主要函數(shù)是:
int lua_getstack (lua_State *L, int level, lua_Debug *ar);
這個(gè)函數(shù)用一個(gè)指定等級(jí)的函數(shù)的 activation record 的標(biāo)示符填充一個(gè) lua_Debug 結(jié)構(gòu),等級(jí) 0 是當(dāng)前運(yùn)行函數(shù),然而等級(jí) n+1 是在等級(jí) n 上調(diào)用的函數(shù)。當(dāng)沒有錯(cuò)誤發(fā)生時(shí),lua_getstack 返回 1;當(dāng)在比棧更深的等級(jí)上調(diào)用的時(shí)候,它返回 0;
lua_Debug 結(jié)構(gòu)被用來攜帶一個(gè)處于活動(dòng)狀態(tài)的函數(shù)的各種信息:
typedef struct lua_Debug { int event; const char *name; /* (n) */ const char *namewhat; /* (n) `global', `local', `field', `method' */ const char *what; /* (S) `Lua' function, `C' function, Lua `main' */ const char *source; /* (S) */ int currentline; /* (l) */ int nups; /* (u) number of upvalues */ int linedefined; /* (S) */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ ... } lua_Debug;
lua_getstack 只填充結(jié)構(gòu)的私有部分以備之后使用。要填充 lua_Debug 其他有用信息,調(diào)用
int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
這個(gè)函數(shù)發(fā)生錯(cuò)誤是返回 0 (舉個(gè)例子,一個(gè)無效的 what 選項(xiàng))。what 字符串中的每個(gè)字符選擇填充一些 ar 結(jié)構(gòu)的字段,把上面在 lua_Debug 定義中用圓括號(hào)括起來的字母作為指示: `S′ 填充在 source, linedefined 和 what 字段中;`l′ 填充在 currentline 字段中,等等...。而且,`f′ 將正在運(yùn)?性謁燃渡系暮谷攵顏弧?
想要從一個(gè)不處于活動(dòng)狀態(tài)的函數(shù)那得到信息(就是不在棧上的函數(shù)),你需要將其壓入棧并且用 >′ 作為 what 字符串的開始。舉個(gè)例子,要知道函數(shù) f 定義在第幾行,你需要這樣寫
lua_Debug ar; lua_pushstring(L, "f"); lua_gettable(L, LUA_GLOBALSINDEX); /* get global `f' */ lua_getinfo(L, ">S", &ar); printf("%d/n", ar.linedefined);
lua_Debug 的字段有如下的含義:
source 如果函數(shù)在一個(gè)字符串中定義,那么 source 就是那個(gè)字符串。如果函數(shù)定義在一個(gè)文件中,source 開始于一個(gè) `@′ 后面跟隨文件名。
short_src 一個(gè)可打印版的 source,用于錯(cuò)誤信息。
linedefined 函數(shù)定義起始的行號(hào)。
what 如果這是一個(gè)Lua函數(shù),顯示 "Lua" 字符串, "C" 為C 函數(shù),"main" 如果這是一個(gè)語句段的main部分,"tail" 如果這是一個(gè)做了尾部調(diào)用的函數(shù)。在后面的情況里,Lua 沒有其他關(guān)于這個(gè)函部的信息。
currentline 代表當(dāng)前函數(shù)執(zhí)行到的行數(shù)。如果沒有行信息可用,currentline 被設(shè)置為 -1。
name 一個(gè)所給函數(shù)合理的函數(shù)名。因?yàn)楹瘮?shù)在Lua中屬于第一類值,它們沒有固定的名字:一些函數(shù)可能是多個(gè)全局變量的值,其他的可能只儲(chǔ)存在一個(gè)表字段里。lua_getinfo 函數(shù)檢測函數(shù)如何被調(diào)用或者是否為一個(gè)全局變量的值以尋找一個(gè)合適的名字。如果找不到合適的名字,name 被設(shè)置為 NULL。
namewhat name 字段的解釋。根據(jù)函數(shù)如何被調(diào)用,namewhat 的值可以是 "global", "local", "method", "field" 或者 "" (空字符串)。(當(dāng)沒有其他可選項(xiàng)的時(shí)候Lua使用空字符串代替)
nups 函數(shù)上值的數(shù)量。
4.2 - 操作局部變量和上值 Manipulating Local Variables and Upvalues
為了更多的操作局部變量和上值,調(diào)試接口使用索引:第一個(gè)參數(shù)或者局部變量索引為 1,以此類推,直到最后一個(gè)活動(dòng)的局部變量。整個(gè)函數(shù)中的活動(dòng)的上值沒有特定的順序。
下面的函數(shù)允許操作一個(gè)所給激活記錄的局部變量:
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);
參數(shù) ar 必須是一個(gè)被前一個(gè) lua_getstack 調(diào)用填充的有效的激活記錄或者作為一個(gè)鉤子函數(shù)的參數(shù)(見 4.3)。lua_getlocal 獲得一個(gè)局部變量的索引 n,將變量的值壓入棧,并且返回變量名。lua_setlocal 從棧頂分配一個(gè)值給變量并且返回變量名。當(dāng)索引超過活動(dòng)的局部變量的數(shù)量時(shí),兩個(gè)函數(shù)都返回 NULL。
以下的函部可以操作所給函數(shù)的上值(不像局部變量,函數(shù)的上值即使在函數(shù)不處于活動(dòng)狀態(tài)的時(shí)候都可以被訪問):
const char *lua_getupvalue (lua_State *L, int funcindex, int n); const char *lua_setupvalue (lua_State *L, int funcindex, int n);
這些函數(shù)可以作為Lua 函數(shù)使用也可以作為C 函數(shù)使用。(作為Lua 函數(shù),上值是函數(shù)外部使用的局部變量,因此它被包含在函數(shù)閉包中。)funcindex 指向棧中的一個(gè)函數(shù)。lua_getupvalue 得到一個(gè)上值的索引 n,將上值的值壓入棧,并返回其變量名。lua_setupvalue 從棧頂分配一個(gè)值給上值并返回變量名。當(dāng)索引大于上值數(shù)量時(shí),兩個(gè)函數(shù)都返回 NULL。對(duì)于C 函數(shù)來說,這些函數(shù)使用空字符串作為所有上值的變量名。
作為一個(gè)例子,下面的函數(shù)列舉了所給等級(jí)的棧中的函數(shù)的所有局部變量名和上值變量名:
int listvars (lua_State *L, int level) { lua_Debug ar; int i; const char *name; if (lua_getstack(L, level, &ar) == 0) return 0; /* failure: no such level in the stack */ i = 1; while ((name = lua_getlocal(L, &ar, i++)) != NULL) { printf("local %d %s/n", i-1, name); lua_pop(L, 1); /* remove variable value */ } lua_getinfo(L, "f", &ar); /* retrieves function */ i = 1; while ((name = lua_getupvalue(L, -1, i++)) != NULL) { printf("upvalue %d %s/n", i-1, name); lua_pop(L, 1); /* remove upvalue value */ } return 1; }4.3 - 鉤子 Hooks
Lua offers a mechanism of hooks, which are user-defined C functionsthat are called during the program execution. A hook may be called infour different events: a call event, when Lua calls a function; a return event, when Lua returns from a function; a line event, when Lua starts executing a new line of code; and a count event, which happens every "count" instructions. Lua identifies these events with the following constants: LUA_HOOKCALL, LUA_HOOKRET (or LUA_HOOKTAILRET, see below), LUA_HOOKLINE, and LUA_HOOKCOUNT.
A hook has type lua_Hook, defined as follows:
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
You can set the hook with the following function:
int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
func is the hook. mask specifies on which events the hook will be called: It is formed by a disjunction of the constants LUA_MASKCALL, LUA_MASKRET, LUA_MASKLINE, and LUA_MASKCOUNT. The count argument is only meaningful when the mask includes LUA_MASKCOUNT. For each event, the hook is called as explained below:
The call hook is called when the interpreter calls a function. The hook is called just after Lua enters the new function.
The return hook is called when the interpreter returns from a function. The hook is called just before Lua leaves the function.
The line hook is called when the interpreter is aboutto start the execution of a new line of code, or when it jumps back inthe code (even to the same line). (This event only happens while Lua isexecuting a Lua function.)
The count hook is called after the interpreter executes every count instructions. (This event only happens while Lua is executing a Lua function.)
A hook is disabled by setting mask to zero.
You can get the current hook, the current mask, and the current count with the following functions:
lua_Hook lua_gethook (lua_State *L); int lua_gethookmask (lua_State *L); int lua_gethookcount (lua_State *L);
Whenever a hook is called, its ar argument has its field event set to the specific event that triggered the hook. Moreover, for line events, the field currentline is also set. To get the value of any other field in ar, the hook must call lua_getinfo. For return events, event may be LUA_HOOKRET, the normal value, or LUA_HOOKTAILRET. In the latter case, Lua is simulating a return from a function that did a tail call; in this case, it is useless to call lua_getinfo.
While Lua is running a hook, it disables other calls to hooks.Therefore, if a hook calls back Lua to execute a function or a chunk,that execution occurs without any calls to hooks.
21:41 |
添加評(píng)論 |
發(fā)送消息 |
固定鏈接 |
查看引用通告 (0) | |
LuaLua - API3 - 應(yīng)用程序接口
這一節(jié)描述Lua中的C API,這是對(duì)于宿主程序可用的C函數(shù)集合,用以和Lua通訊。所有的API函數(shù)及其相關(guān)類型和常量都聲明在頭文件lua.h中。
即便每次我都使用“函數(shù)”這個(gè)詞,任何設(shè)施在API里面都可能被一個(gè)宏所替代。所有這些宏(macro)都只使用一次它的參數(shù)(除了第一個(gè)參數(shù)、這個(gè)每次總是一個(gè)Lua狀態(tài)),所以不會(huì)產(chǎn)生隱藏的副作用。
3.1 - 狀態(tài)
Lua庫是可重入的(reentrant)的:它沒有全局變量。整個(gè)Lua解釋器的狀態(tài)(全局變量、棧、等等)儲(chǔ)存在一個(gè)動(dòng)態(tài)分配的 lua_State 結(jié)構(gòu)類型中。一個(gè)指向這個(gè)狀態(tài)的指針必須作為庫中每一個(gè)函數(shù)的第一個(gè)參數(shù),除了 lua_open 這個(gè)函數(shù)。該函數(shù)從最開始創(chuàng)建一個(gè)Lua狀態(tài)。
在調(diào)用任何API函數(shù)之前,你必須通過調(diào)用 lua_open 創(chuàng)建一個(gè)狀態(tài):
lua_State *lua_open (void);
調(diào)用 lua_close 去釋放這個(gè)由 lua_open 創(chuàng)建的狀態(tài):
void lua_close (lua_State *L);
這個(gè)函數(shù)銷毀所有被給予Lua狀態(tài)的對(duì)象(調(diào)用相應(yīng)的垃圾收集元方法)并且釋放那個(gè)狀態(tài)使用的所有動(dòng)態(tài)內(nèi)存。在個(gè)別的平臺(tái)上,你或許不需要調(diào)用這個(gè)函數(shù),因?yàn)楫?dāng)宿主程序結(jié)束的時(shí)候會(huì)自然的釋放所有的資源。另一方面,長時(shí)間運(yùn)行的程序,像一些守護(hù)進(jìn)程或者Web服務(wù)器,可能需要立即釋放那些不需要的狀態(tài)資源,以避免占用太多內(nèi)存。
3.2 - 堆棧和索引
Lua使用一個(gè)來自于C語言的 虛擬棧(virtual stack) 傳遞值。棧里面的每一個(gè)元素都代表一個(gè)Lua值 (nil, number, string, etc.)。
只要Lua調(diào)用C語言函數(shù),這個(gè)所調(diào)用的函數(shù)將得到一個(gè)新的棧,這個(gè)棧將獨(dú)立于先前的棧以及那些仍然活躍的C函數(shù)的棧。這個(gè)棧最初包含了C函數(shù)的所有參數(shù),并且這也會(huì)存放C函數(shù)的返回值(見 3.16)。
為了方便起見,大多數(shù)查詢操作的API不需要遵守一個(gè)嚴(yán)格的棧定義(注:即不需要遵循FILO)。他們可以使用 索引(index) 引用任何棧中元素:一個(gè)正數(shù)索引代表了棧中的絕對(duì)位置(從1開始);一個(gè)負(fù)數(shù)索引代表了從棧頂?shù)钠屏?。更特別的是,如果棧有 n 個(gè)元素,那么索引 1 代表第一個(gè)元素(這就是說,這個(gè)元素首先入棧)并且索引 n 代表了最后一個(gè)元素;索引 -1 也代表了最后一個(gè)元素(也就是棧頂)并且索引 -n 代表了第一個(gè)元素。我們說一個(gè)索引存在于 1 和棧頂之間是有效的,換句話說,如果 1 <= abs(index) <= top。
在任何時(shí)間里,你可以調(diào)用 lua_gettop 得到棧頂元素的索引:
int lua_gettop (lua_State *L);
因?yàn)樗饕龔?1 開始,lua_gettop 的結(jié)果等于棧中的元素?cái)?shù)量(如果是0就意味著棧為空)。
當(dāng)你與Lua API交互的時(shí)候,你有責(zé)任控制堆棧以避免溢出。。這個(gè)函數(shù)
int lua_checkstack (lua_State *L, int extra);
使棧的大小增長為 top + extra 個(gè)元素;如果無法將棧增加到那個(gè)大小將返回false。這個(gè)函數(shù)從不對(duì)棧進(jìn)行收縮;如果棧已經(jīng)比新的大小更大,它將不產(chǎn)生任何作用那個(gè)。
只要Lua調(diào)用C 函數(shù),它必須至少保證 LUA_MINSTACK 這個(gè)棧中的位置是可用的。LUA_MINSTACK 定義在 lua.h 中,它的值是 20,所以你不需要總擔(dān)心??臻g除非你的代碼通過循環(huán)將元素壓入棧。
大多數(shù)插敘函數(shù)接受指向有效??臻g的索引,那就是說,索引達(dá)到??臻g的最大值是你需要使用 lua_checkstack。這樣的索引稱為可接受索引(acceptable indices)。更正規(guī)的說法,我們給出一個(gè)嚴(yán)格的定義如下:
(index < 0 && abs(index) <= top) || (index > 0 && index <= stackspace)
注意,0永遠(yuǎn)不是一個(gè)可接受索引。
除非另外說明,任何函數(shù)接受有效索引可以被稱為是 偽索引(pseudo-indices),這些索引代表一些Lua值可以被C 代碼訪問但是卻不存在于棧中。假索引通常用于訪問全局環(huán)境變量,注冊(cè)表,和一個(gè)C 函數(shù)的上值(見 3.17)。
3.3 - 堆棧操作
一下的API提供了基本的棧操作:
void lua_settop (lua_State *L, int index); void lua_pushvalue (lua_State *L, int index); void lua_remove (lua_State *L, int index); void lua_insert (lua_State *L, int index); void lua_replace (lua_State *L, int index);
lua_settop 接受任何可接受的索引,或者0,并且將該索引設(shè)置為棧頂。如果新的棧頂比舊的更大,那么新元素被填上 nil 值。如果索引為 0,那么所有棧元素會(huì)被清除。在 lua.h 里面定義了一個(gè)有用的宏
#define lua_pop(L,n) lua_settop(L, -(n)-1)
用以從棧中彈出 n 個(gè)元素。
lua_pushvalue 將一個(gè)索引指向的元素的拷貝壓入棧。 lua_remove 刪除指定位置的元素,將該元素上方的所有元素下移以填滿空缺。lua_insert 將棧頂元素移動(dòng)到指定位置,將該位置以上的元素上移。lua_replace 將棧頂元素移動(dòng)到指定位置而不移動(dòng)其他任何其他元素(因此替代了給定位置的元素的值)。所有這些函數(shù)只接受有效的索引。(你不能使用偽索引調(diào)用 lua_remove 或 lua_insert,因?yàn)樗麄儾淮項(xiàng)V械奈恢?。?div style="height:15px;">
舉個(gè)例子,如果棧開始于 10 20 30 40 50*(自底向上;`*′ 標(biāo)記了棧頂),那么:
lua_pushvalue(L, 3) --> 10 20 30 40 50 30* lua_pushvalue(L, -1) --> 10 20 30 40 50 30 30* lua_remove(L, -3) --> 10 20 30 40 30 30* lua_remove(L, 6) --> 10 20 30 40 30* lua_insert(L, 1) --> 30 10 20 30 40* lua_insert(L, -1) --> 30 10 20 30 40* (no effect) lua_replace(L, 2) --> 30 40 20 30* lua_settop(L, -3) --> 30 40* lua_settop(L, 6) --> 30 40 nil nil nil nil*
3.4 - 堆棧查詢
下面的函數(shù)可以用來檢測棧內(nèi)元素的類型:
int lua_type (lua_State *L, int index); int lua_isnil (lua_State *L, int index); int lua_isboolean (lua_State *L, int index); int lua_isnumber (lua_State *L, int index); int lua_isstring (lua_State *L, int index); int lua_istable (lua_State *L, int index); int lua_isfunction (lua_State *L, int index); int lua_iscfunction (lua_State *L, int index); int lua_isuserdata (lua_State *L, int index); int lua_islightuserdata (lua_State *L, int index);
這些函數(shù)只能使用可接受的索引。
lua_type 返回棧中元素值的類型,如果所有索引無效則返回 LUA_TNONE(就是說如果棧為空)。這些lua_type 代表的返回值作為常量定義在 lua.h 中:LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA。下面的函數(shù)將這些常量轉(zhuǎn)換成字符串:
const char *lua_typename (lua_State *L, int type);
lua_is* 函數(shù)返回 1 當(dāng)對(duì)象與所給類型兼容的時(shí)候,其他情況返回 0。 lua_isboolean 是一個(gè)例外:它只針對(duì)布爾值時(shí)才會(huì)成功(否則將是無用的,因?yàn)槿魏沃刀际且粋€(gè)布爾值)。這些函數(shù)對(duì)于無效引用返回 0。 lua_isnumber 接受數(shù)字和用數(shù)字表示的字符串;lua_isstring 接受字符串和數(shù)字(見 2.2.1);lua_isfunction 接受Lua函數(shù)和C函數(shù); lua_isuserdata 接受完整的和輕量的用戶數(shù)據(jù)。要區(qū)分C 函數(shù)和Lua 函數(shù),你可以使用 lua_iscfunction。要區(qū)分用戶數(shù)據(jù),你可以使用 lua_islightuserdata。要區(qū)分?jǐn)?shù)字還是用數(shù)字表示的字符串,你可以使用 lua_type。
這些API還包含了用于比較棧中的兩個(gè)值的操作:
int lua_equal (lua_State *L, int index1, int index2); int lua_rawequal (lua_State *L, int index1, int index2); int lua_lessthan (lua_State *L, int index1, int index2);
lua_equal 和 lua_lessthan 在比較他們的副本的時(shí)候是等效的(見 2.5.2)。 lua_rawequal 用于比較基本類型但不包括元方法。如果有任何形式的無效索引,這些函數(shù)都返回 0(false)。
3.5 - 堆棧取值
為了將一個(gè)棧中的值轉(zhuǎn)變?yōu)橹付ǖ腃語言類型,你需要使用以下的轉(zhuǎn)換函數(shù):
int lua_toboolean (lua_State *L, int index); lua_Number lua_tonumber (lua_State *L, int index); const char *lua_tostring (lua_State *L, int index); size_t lua_strlen (lua_State *L, int index); lua_CFunction lua_tocfunction (lua_State *L, int index); void *lua_touserdata (lua_State *L, int index); lua_State *lua_tothread (lua_State *L, int index); void *lua_topointer (lua_State *L, int index);
這些函數(shù)由任何可接受索引作為參數(shù)進(jìn)行調(diào)用。當(dāng)遇到一個(gè)無效索引,函數(shù)表現(xiàn)為就好像接受了一個(gè)錯(cuò)誤類型的值。
lua_toboolean 將索引指向的Lua值轉(zhuǎn)換為C語言類型的布爾值(0 或 1)。就像所有Lua中的測試一樣,任何不等于 false 或者 nil 的Lua值通過 lua_toboolean 都將返回 1;否則將返回 0。當(dāng)然,如果是一個(gè)無效索引,也將返回 0。(如果你只想接受真實(shí)的布爾值,使用 lua_isboolean 去測試值的類型。)
lua_tonumber 將索引指向的Lua值轉(zhuǎn)換成一個(gè)數(shù)字(默認(rèn)情況下,lua_Number 是 double類型)。Lua值必須是一個(gè)數(shù)字或者可轉(zhuǎn)化為數(shù)字的字符串(見 2.2.1);否則,lua_tonumber 返回 0。
lua_tostring 將索引指向的Lua值轉(zhuǎn)換成字符串(const char*)。Lua值必須是一個(gè)字符串或者數(shù)字;否則,函數(shù)返回 NULL。如果值是一個(gè)數(shù)字,lua_tostring 會(huì)將棧中的真實(shí)值變成一個(gè)字符串類型。(當(dāng) lua_tostring 應(yīng)用于鍵時(shí)這個(gè)改變將引起 lua_next 的混亂。)lua_tostring 在Lua 狀態(tài)內(nèi)部返回一個(gè)字符串的指針。這個(gè)字符串總是以 0('/0')結(jié)尾,就像C 語言里的一樣,但是也可能包含其他 0 在其中。如果你不知道一個(gè)字符串中是否存在 0 ,你可以使用 lua_strlen 得到它的實(shí)際長度。因?yàn)長ua具有垃圾收集機(jī)制,所以不能保證 lua_tostring 返回的指針仍然有效,當(dāng)相應(yīng)的值從棧中刪除之后。如果你在當(dāng)前函數(shù)返回之后還需要這個(gè)字符串,你需要復(fù)制它并且將它存入注冊(cè)表(見 3.18)。
lua_tocfunction 將棧中的值轉(zhuǎn)換為C 函數(shù)。這個(gè)值必須是一個(gè)C 函數(shù);否則,lua_tocfunction 返回 NULL。類型 lua_CFunction 在 3.16 中有詳細(xì)解釋。
lua_tothread 將棧中的值轉(zhuǎn)換為Lua線程(被描繪成 lua_State *)。這個(gè)值必須是一個(gè)線程;否則;lua_tothread 返回 NULL。
lua_topointer 將棧中?鬧底晃ㄓ玫腃 語言指針(void *)。這個(gè)值可能是一個(gè)用戶數(shù)據(jù)、表、線程、或者函數(shù);否則,lua_topointer 返回 NULL。Lua保證同種類型的不同對(duì)象將返回不同指針。沒有直接的方法將指針轉(zhuǎn)換回原來的值。這個(gè)函數(shù)通常用于調(diào)試。
lua_touserdata 在 3.8 中有詳細(xì)解釋。
3.6 - 將值壓入堆棧
以下的API函數(shù)將C 語言值壓入棧:
void lua_pushboolean (lua_State *L, int b); void lua_pushnumber (lua_State *L, lua_Number n); void lua_pushlstring (lua_State *L, const char *s, size_t len); void lua_pushstring (lua_State *L, const char *s); void lua_pushnil (lua_State *L); void lua_pushcfunction (lua_State *L, lua_CFunction f); void lua_pushlightuserdata (lua_State *L, void *p);
這些函數(shù)接受一個(gè)C 語言值,將其轉(zhuǎn)換成相應(yīng)的Lua 值,并且將結(jié)果壓入棧。需要特別注意的是,lua_pushlstring 和 lua_pushstring 將對(duì)所給的字符串做一個(gè)內(nèi)部拷貝。lua_pushstring 只能壓入合適的C 語言字符串(也就是說,字符串要以 '/0' 結(jié)尾,并且不能包含內(nèi)嵌的 0);否則,你需要使用更通用的 lua_pushlstring 函數(shù),它可以接受一個(gè)指定的大小。
你可以壓入“格式化的”字符串:
const char *lua_pushfstring (lua_State *L, const char *fmt, ...); const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);
這些函數(shù)將格式化的字符串壓入棧并且返回這個(gè)字符串的指針。它們和 sprintf、vsprintf 類似,但是有一些重要的不同之處:
你不需要為結(jié)果分配空間:結(jié)果是Lua字符串并且Lua會(huì)關(guān)心內(nèi)存分配問題(和內(nèi)存釋放問題,通過垃圾收集機(jī)制)。
轉(zhuǎn)換受到限制。這里沒有標(biāo)志、寬度或精度。轉(zhuǎn)換操作的修飾符可以是簡單的`%%′(在字符串中插入一個(gè)`%′),`%s′(插入一個(gè)沒有大小限制的以 0 結(jié)尾的字符串),`%f′(插入一個(gè) lua_Number),`%d′(插入一個(gè) int),`%c′(插入一個(gè) int 作為一個(gè)字符)。
這個(gè)函數(shù)
void lua_concat (lua_State *L, int n);
連接棧頂?shù)?n 個(gè)值,將它們彈出,并且將結(jié)果留在棧頂。如果 n 為 1,結(jié)果是單個(gè)字符串(也就是說,函數(shù)什么也不做);如果 n 是 0,結(jié)果是空字符串。連接的完成依據(jù)Lua的語義(見 2.5.4)。
3.7 - 控制垃圾收集
Lua使用兩個(gè)數(shù)字控制垃圾收集循環(huán)。一個(gè)數(shù)字表示Lua使用的動(dòng)態(tài)內(nèi)存的字節(jié)數(shù),另一個(gè)是閥值。(見 2.9)。一個(gè)數(shù)字表示Lua使用的動(dòng)態(tài)內(nèi)存的字節(jié)數(shù),另一個(gè)是閥值。當(dāng)內(nèi)存字節(jié)數(shù)到達(dá)閥值時(shí),Lua就運(yùn)行垃圾收集器,來釋放死對(duì)象的空間。一旦字節(jié)計(jì)數(shù)器被調(diào)整,那么閥值就會(huì)被設(shè)為字節(jié)計(jì)數(shù)器新值的兩倍。
你可以通過以下的函數(shù)得到這兩個(gè)量的當(dāng)前值:
int lua_getgccount (lua_State *L); int lua_getgcthreshold (lua_State *L);
它們的返回值的單位都是千字節(jié)(K bytes)。你可以通過下面的函數(shù)改變閥值
void lua_setgcthreshold (lua_State *L, int newthreshold);
然后,新的閥值得單位也是千字節(jié)。當(dāng)你調(diào)用這個(gè)函數(shù),Lua設(shè)置閥新值并且和字節(jié)計(jì)數(shù)器作比較。如果新的閥值小于字節(jié)計(jì)數(shù)器,Lua將立刻運(yùn)行垃圾收集器。特別是 lua_setgcthreshold(L,0) 強(qiáng)迫進(jìn)行垃圾收集。在這之后,一個(gè)新值根據(jù)先前的規(guī)則被設(shè)置。
3.8 - 用戶數(shù)據(jù)類型 (Userdata)
用戶數(shù)據(jù)代表了Lua中使用的C語言值。Lua支持兩種用戶數(shù)據(jù):完整用戶數(shù)據(jù)(full userdata) 和 輕量用戶數(shù)據(jù)(light userdata)。
一個(gè)完整用戶數(shù)據(jù)代表了一塊內(nèi)存。它是一個(gè)對(duì)象(像一個(gè)表):你必須創(chuàng)建它,它有自己的元表,當(dāng)它被回收的時(shí)候你可以檢測到。一個(gè)完整用戶數(shù)據(jù)只能與自己相等(基于原始的相等規(guī)則)。
一個(gè)輕量用戶數(shù)據(jù)代表一個(gè)指針。它是?桓鮒擔(dān)ㄏ褚桓鍪鄭耗悴⒚揮寫唇ㄋ?,它也脫]性懟ⅲ荒鼙換厥眨ㄒ蛭游幢淮唇ǎ?。菎埧記]菹嗟鵲奶跫侵剛脛趕虻牡刂廢嗤?
在Lua 代碼里,沒辦法測試用戶數(shù)據(jù)類型是完整的還是輕量的;兩者都是 用戶數(shù)據(jù)類型。在C 代碼里,如果是完整用戶數(shù)據(jù),lua_type 返回 LUA_TUSERDATA,反之,返回 LUA_TLIGHTUSERDATA。
你可以通過下面的函數(shù)創(chuàng)建完整用戶數(shù)據(jù):
void *lua_newuserdata (lua_State *L, size_t size);
這個(gè)函數(shù)根據(jù)指定大小分配一個(gè)內(nèi)存塊,將用戶數(shù)據(jù)的地址壓入棧并且返回這個(gè)地址。
要將輕量用戶數(shù)據(jù)壓入棧,你需要使用 lua_pushlightuserdata(見 3.6)。
lua_touserdata (見 3.5)用來取回用戶數(shù)據(jù)的值。當(dāng)你用在完整用戶數(shù)據(jù)的時(shí)候,它返回這個(gè)塊的地址,當(dāng)你用在輕量用戶數(shù)據(jù)的時(shí)候,它返回它的指針,當(dāng)你用在非用數(shù)據(jù)的時(shí)候,返回 NULL。
當(dāng)Lua回收一個(gè)完整用戶數(shù)據(jù),它調(diào)用該用戶數(shù)據(jù)的 gc 元方法,然后釋放該用戶數(shù)據(jù)相應(yīng)的內(nèi)存。
3.9 - 元表 (Metatables)
下面的函數(shù)允許你操作對(duì)象的元表:
int lua_getmetatable (lua_State *L, int index); int lua_setmetatable (lua_State *L, int index);
lua_getmetatable 將所給對(duì)象的元表壓入棧。如果索引無效,或這個(gè)對(duì)象不含有元表,該函數(shù)返回 0 并且不對(duì)棧進(jìn)行任何操作。
lua_setmetatable 從棧中彈出一張表并且為所給對(duì)象設(shè)置一個(gè)新的元表。當(dāng)無法給所給對(duì)象設(shè)置元表的時(shí)候該函數(shù)返回 0(也就是說,這個(gè)對(duì)象既不是一個(gè)用戶數(shù)據(jù)也不是一張表);盡管那樣,它仍從棧中彈出這張表。
3.10 - 加載Lua語句段
你可以通過 lua_load 加載一個(gè)Lua塊:
typedef const char * (*lua_Chunkreader) (lua_State *L, void *data, size_t *size); int lua_load (lua_State *L, lua_Chunkreader reader, void *data, const char *chunkname);
lua_load 的返回值是:
0 --- 沒有錯(cuò)誤
LUA_ERRSYNTAX --- 預(yù)編譯時(shí)句法錯(cuò)誤
LUA_ERRMEM --- 內(nèi)存分配錯(cuò)誤
如果沒有錯(cuò)誤,lua_load 將編譯過的語句段作為Lua 函數(shù)壓入棧頂。否則,它將壓入一個(gè)錯(cuò)誤信息。
lua_load 自動(dòng)檢測語句段的類型是文本還是二進(jìn)制數(shù)據(jù),并且根據(jù)類型將其載入(見程序 luac)。
lua_load 使用一個(gè)用戶提供的 reader 函數(shù)讀取語句段的內(nèi)容。當(dāng)需要調(diào)用其它段時(shí),lua_load 調(diào)用 reader,傳遞其 data 參數(shù)。必須返回指向語句段所在的新內(nèi)存塊的指針,并將段大小設(shè)置為 0。為了標(biāo)志塊尾,reader 必須返回 NULL。reader 函數(shù)可以返回任何大于零的值。
在當(dāng)前的實(shí)現(xiàn)中,reader 函數(shù)不能調(diào)用任何Lua 函數(shù);為了保證這一點(diǎn),它總是會(huì)得到為 NULL 的Lua狀態(tài)。
語句段名(chunkname) 用于錯(cuò)誤信息和調(diào)試信息(見 4)。
參考輔助庫 (lauxlib.c) 了解如何使用 lua_load 以及如何使用現(xiàn)成的函數(shù)從文件和字符串中加載語句段。
3.11 - 表操作
通過調(diào)用以下函數(shù)可以創(chuàng)建表:
void lua_newtable (lua_State *L);
這個(gè)函數(shù)創(chuàng)建一張新的空表,并將其壓入棧。
要從棧中的表里讀取值,使用:
void lua_gettable (lua_State *L, int index);
index 代表表的位置。lua_gettable 從棧中彈出一個(gè)鍵,并且返回該鍵對(duì)應(yīng)的值,表仍然留在堆棧中。在Lua中,這個(gè)函數(shù)可能觸發(fā)一個(gè)針對(duì) index 事件的元方法(見 2.8)。想要在不調(diào)用任何元方法的情況下得到表主鍵所對(duì)應(yīng)的真實(shí)值,使用這個(gè)原始(raw)版本:
void lua_rawget (lua_State *L, int index);
要將一個(gè)值儲(chǔ)存到棧中的一張表中,你需要將鍵壓入棧,再將值壓入棧,調(diào)用:
void lua_settable (lua_State *L, int index);
index 代表表的位置。lua_settable 從棧中彈出主鍵和值。表仍然留在棧中。在Lua中,這個(gè)操作可能觸發(fā)針對(duì) settable 或者 newindex 事件的元方法。想要不受這些元方法的影響并且為任意表設(shè)置值,使用這個(gè)原始(raw)版本:
void lua_rawset (lua_State *L, int index);
你可以通過這個(gè)函數(shù)遍歷一張表:
int lua_next (lua_State *L, int index);
index 指向需要被遍歷的表。這個(gè)函數(shù)從堆棧中彈出一個(gè)鍵,從表中取一對(duì)鍵-值壓入棧(所給鍵的下一對(duì))。如果沒有更多的元素,lua_next 返回 0(對(duì)棧不進(jìn)行操作)。使用一個(gè) nil 鍵標(biāo)示遍歷的開始。
一個(gè)典型的遍歷操作看起來像這樣:
/* table is in the stack at index `t' */ lua_pushnil(L); /* first key */ while (lua_next(L, t) != 0) { /* `key' is at index -2 and `value' at index -1 */ printf("%s - %s/n", lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1))); lua_pop(L, 1); /* removes `value'; keeps `key' for next iteration */ }
當(dāng)遍歷一張表的時(shí)候,不要在鍵上直接調(diào)用 lua_tostring,除非你知道這個(gè)鍵確實(shí)是一個(gè)字符串。再次調(diào)用 lua_tostring 改變了所給索引指向的值;這使 lua_next 的調(diào)用發(fā)生混亂。
3.12 - 環(huán)境變量操作 (Manipulating Environments)
所有的全局變量保存在普通的Lua 表中,叫做環(huán)境變量。初始的環(huán)境變量被稱作全局環(huán)境變量。這張表總是在 LUA_GLOBALSINDEX 這個(gè)偽索引處。
要訪問或改變?nèi)肿兞康闹?,你可以?duì)環(huán)境變量表使用常規(guī)的表操作。舉個(gè)例子,存取一個(gè)全局變量的值:
lua_pushstring(L, varname); lua_gettable(L, LUA_GLOBALSINDEX);
你可以改變一個(gè)Lua 線程的全局環(huán)境變量通過 lua_replace 函數(shù)。
以下的函數(shù)提供獲取、設(shè)置Lua函數(shù)的環(huán)境變量的功能:
void lua_getfenv (lua_State *L, int index); int lua_setfenv (lua_State *L, int index);
lua_getfenv 將堆棧中 index 索引指向的函數(shù)的環(huán)境變量表壓入棧。如果函數(shù)是一個(gè)C 函數(shù),lua_getfenv 將全局環(huán)境變量壓入棧。lua_setfenv 從棧中彈出一張表并且將其設(shè)置為棧中 index 索引處的函數(shù)的新環(huán)境變量。如果給定索引處的對(duì)象不是一個(gè)Lua 函數(shù),lua_setfenv 返回 0。
3.13 - 將表作為數(shù)組使用 Using Tables as Arrays
有一些 API 能夠幫助我們將Lua 表作為數(shù)組使用,也就是說,表只由數(shù)字作為索引:
void lua_rawgeti (lua_State *L, int index, int n); void lua_rawseti (lua_State *L, int index, int n);
lua_rawgeti 將表中的第 n 個(gè)元素放入堆棧中的指定位置 index。lua_rawseti 將堆棧中指定位置 index 處的表中的第 n 個(gè)元素的值設(shè)定為棧頂?shù)闹担⒃瓉淼闹祻臈V袆h除。
3.14 - 調(diào)用函數(shù)
定義在Lua 中的函數(shù)和C語言函數(shù)經(jīng)過注冊(cè)就可以被宿主程序調(diào)用。這些調(diào)用必須遵循以下協(xié)議:首先,被調(diào)用的函數(shù)被壓入棧;然后,函數(shù)的參數(shù)必須順序(direct order)輸入,也就是說,第一個(gè)參數(shù)需要被第一個(gè)輸入。最后,函數(shù)通過下面的方法調(diào)用:
void lua_call (lua_State *L, int nargs, int nresults);
nargs 是你壓入棧的參數(shù)的數(shù)量。所有參數(shù)和函數(shù)值從堆棧中彈出,并且函數(shù)結(jié)果被壓入棧。返回值的數(shù)量被調(diào)整為 nresults,除非 nresults 是 LUA_MULTRET。在那種情況下,所有函數(shù)結(jié)果都被壓入棧。Lua 會(huì)檢測返回值是否適合棧空間。函數(shù)返回值按順序被壓入棧(第一個(gè)返回值首先入棧),所以調(diào)用結(jié)束后最后一個(gè)返回值在棧頂。
下面的例子展示宿主程序如何可以和這個(gè)Lua 代碼等效:
a = f("how", t.x, 14)
這里是C 語言里的做法:
lua_pushstring(L, "t"); lua_gettable(L, LUA_GLOBALSINDEX); /* global `t' (for later use) */ lua_pushstring(L, "a"); /* var name */ lua_pushstring(L, "f"); /* function name */ lua_gettable(L, LUA_GLOBALSINDEX); /* function to be called */ lua_pushstring(L, "how"); /* 1st argument */ lua_pushstring(L, "x"); /* push the string "x" */ lua_gettable(L, -5); /* push result of t.x (2nd arg) */ lua_pushnumber(L, 14); /* 3rd argument */ lua_call(L, 3, 1); /* call function with 3 arguments and 1 result */ lua_settable(L, LUA_GLOBALSINDEX); /* set global variable `a' */ lua_pop(L, 1); /* remove `t' from the stack */
注意上面的代碼是“平衡的”:在它結(jié)束時(shí),堆棧返回原來的配置。這個(gè)被認(rèn)為是良好的編程實(shí)踐。
(為了展示細(xì)節(jié),我們只用Lua 提供的原始 API 完成這個(gè)例子。通常程序員定義并使用幾個(gè)宏和輔助庫函數(shù)在Lua 中提供高級(jí)存取功能。請(qǐng)參考例子中標(biāo)準(zhǔn)庫函數(shù)的源代碼。)
3.15 - 受保護(hù)調(diào)用 Protected Calls
當(dāng)你通過 lua_call 調(diào)用一個(gè)函數(shù),所調(diào)用函數(shù)內(nèi)部產(chǎn)生的錯(cuò)誤將向上傳遞(通過一個(gè) longjmp)。如果你需要處理錯(cuò)誤,你應(yīng)該使用 lua_pcall:
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);
nargs 和 nresults 在 lua_call 中有相同的意義。如果調(diào)用過程中沒有錯(cuò)誤,lua_pcall 的行為非常像 lua_call 。然而,如果有錯(cuò)誤,lua_call 會(huì)捕獲它,將一個(gè)單一值(錯(cuò)誤信息)壓入棧,并且返回錯(cuò)誤代碼。像 lua_call ,lua_pcall 總是從棧中刪除函數(shù)和它的參數(shù)。
如果 errfunc 是 0,所返回的錯(cuò)誤信息就是原始的錯(cuò)誤信息。另外,errfunc 給出一個(gè)指向錯(cuò)誤處理函數(shù)(error handler function)的棧索引。(在當(dāng)前的實(shí)現(xiàn)中,索引不能為偽索引。)假設(shè)運(yùn)行時(shí)錯(cuò)誤,函數(shù)將和錯(cuò)誤信息一起被調(diào)用,并且他的返回值將是 lua_pcall 返回的信息。
錯(cuò)誤處理函數(shù)被用來為錯(cuò)誤信息增加更多的調(diào)試信息,例如棧的記錄。這樣的信息在 lua_pcall 調(diào)用返回后將不能被收集,因此棧已經(jīng)被解開了。
如果 lua_pcall 函數(shù)調(diào)用成功返回 0,否則返回以下的一個(gè)錯(cuò)誤代碼(定義在 lua.h):
LUA_ERRRUN --- 運(yùn)行時(shí)錯(cuò)誤
LUA_ERRMEM --- 內(nèi)存分配錯(cuò)誤。這樣的錯(cuò)誤下,Lua 不調(diào)用錯(cuò)誤處理函數(shù)
LUA_ERRERR --- 運(yùn)行錯(cuò)誤處理函數(shù)時(shí)發(fā)生的錯(cuò)誤
3.16 - 定義C 函數(shù)
Lua可以通過C 語言寫的函數(shù)進(jìn)行擴(kuò)展,這些函數(shù)必須是 lua_CFunction 類型的,作為以下定義:
typedef int (*lua_CFunction) (lua_State *L);
一個(gè)C 函數(shù)接收一個(gè)Lua 狀態(tài)并且返回一個(gè)整數(shù),數(shù)值需要返回給Lua。
為了正確的和Lua 通訊,C 函數(shù)必須遵循以下協(xié)議,它定義了參數(shù)和返回值傳遞的方法:一個(gè)C函數(shù)在它的堆棧中從Lua獲取順序(第一個(gè)參數(shù)首先入棧)參數(shù)。所以,當(dāng)函數(shù)開始時(shí),第一個(gè)參數(shù)在索引位置 1。為了將返回值傳遞給Lua,一個(gè)C函數(shù)將它們順序壓入棧,并且返回它們的數(shù)量。任何在堆棧中位于返回值以下的值都將被Lua 適當(dāng)?shù)慕獬?。就像Lua 函數(shù)一樣,一個(gè)C 函數(shù)被Lua調(diào)用也可以返回很多結(jié)果。
作為一個(gè)例子,下面的函數(shù)接收一個(gè)任意數(shù)量的數(shù)字參數(shù)并且返回它們的平均值和總合:
static int foo (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ lua_Number sum = 0; int i; for (i = 1; i <= n; i++) { if (!lua_isnumber(L, i)) { lua_pushstring(L, "incorrect argument to function `average'"); lua_error(L); } sum += lua_tonumber(L, i); } lua_pushnumber(L, sum/n); /* first result */ lua_pushnumber(L, sum); /* second result */ return 2; /* number of results */ }
下面是一些便利的宏用來在Lua中注冊(cè)一個(gè)C 函數(shù):
#define lua_register(L,n,f) / (lua_pushstring(L, n), / lua_pushcfunction(L, f), / lua_settable(L, LUA_GLOBALSINDEX)) /* lua_State *L; */ /* const char *n; */ /* lua_CFunction f; */
它接收Lua 中的函數(shù)名和一個(gè)指向函數(shù)的指針。這樣,上面的C 函數(shù)foo可以在Lua中被注冊(cè)為 average 并被調(diào)用。
lua_register(L, "average", foo);
3.17 - 定義C 函數(shù)關(guān)閉 Defining C Closures
當(dāng)一個(gè)C 函數(shù)被創(chuàng)建后,它可以與一些值關(guān)聯(lián),這樣創(chuàng)建了一個(gè) C 閉包(C closure);這些值可以被隨時(shí)被函數(shù)訪問。為了使值和C 函數(shù)關(guān)聯(lián),首先這些值要被壓入棧(有多個(gè)值時(shí),第一個(gè)值先入),然后這個(gè)函數(shù)
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
被用來將C 函數(shù)壓入棧,通過參數(shù) n 告知應(yīng)該有多少個(gè)值和該函數(shù)關(guān)聯(lián)(lua_pushcclosure 將這些值從堆棧中彈出);事實(shí)上,這個(gè)宏 lua_pushcfunction 被定義作為 lua_pushcfunction 將 n 設(shè)置為 0。
然后,無論何時(shí)C 函數(shù)被調(diào)用,那些值被定為于指定的偽索引處。那些偽索引有一個(gè)宏 lua_upvalueindex 產(chǎn)生。第一個(gè)和函數(shù)關(guān)聯(lián)的值在 lua_upvalueindex(1) 處,其他的以此類推。當(dāng) n 比當(dāng)前函數(shù)的上值大時(shí),lua_upvalueindex(n) 會(huì)產(chǎn)生一個(gè)可接受的索引(但是無效)。
C語言函數(shù)和關(guān)閉的例子,請(qǐng)參考Lua官方發(fā)行版中的標(biāo)準(zhǔn)庫(src/lib/*.c)。
3.18 - 注冊(cè)表 Registry
Lua提供了一個(gè)注冊(cè)表,一張可以被所有C 代碼用來儲(chǔ)存任何需要儲(chǔ)存的Lua值的預(yù)定義表,特別是如果C 代碼需要維護(hù)C 函數(shù)以外存活的Lua值。這張表總是位于 LUA_REGISTRYINDEX 這個(gè)為索引處。任何C 語言庫可以將數(shù)據(jù)儲(chǔ)存在這張表中,只要它選擇的鍵和其他庫不同。典型的做法是你應(yīng)該使用字符串作為主鍵包含你的庫名或者在你的代碼中使用一個(gè)包含C 對(duì)象地址的輕量用戶數(shù)據(jù)。
在注冊(cè)表中的整數(shù)鍵被引用機(jī)制所使用,由輔助庫實(shí)現(xiàn),因此不應(yīng)該被用作其它用途。
3.19 - C 中的錯(cuò)誤處理 Error Handling in C
總的來說,Lua使用C longjmp 機(jī)制來處理錯(cuò)誤。當(dāng)Lua面對(duì)任何錯(cuò)誤(例如內(nèi)存分配錯(cuò)誤,類型錯(cuò)誤,句法錯(cuò)誤)它 升起(raises) 一個(gè)錯(cuò)誤,也就是說,它做了一個(gè)長跳躍。一個(gè)受保護(hù)的環(huán)境使用 setjmp 設(shè)置一個(gè)恢復(fù)點(diǎn);任何錯(cuò)誤跳至最近最活躍的恢復(fù)點(diǎn)。
如果錯(cuò)誤發(fā)生在任何受保護(hù)的環(huán)境,Lua調(diào)用 panic 函數(shù) 并且隨后調(diào)用exit(EXIT_FAILURE)。你可以將panic 函數(shù)變?yōu)橐韵聝?nèi)容。
lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);
你的新panic 函數(shù)可以避免程序因?yàn)闆]有返回(例如通過一個(gè)長跳躍)而退出。否則,相應(yīng)的Lua 狀態(tài)將不一致;唯一安全的操作就是關(guān)閉它。
幾乎所有的API 函數(shù)都可能引起錯(cuò)誤,例如導(dǎo)致一個(gè)內(nèi)存分配錯(cuò)誤。:lua_open, lua_close, lua_load 和 lua_pcall 這些的函數(shù)運(yùn)行在保護(hù)模式下(也就是說,它們創(chuàng)建了一個(gè)受保護(hù)的環(huán)境并在其中運(yùn)行),所以它們從不會(huì)引起錯(cuò)誤。
有另外一個(gè)函數(shù)將所給的C 函數(shù)運(yùn)行在保護(hù)模式下:
int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);
lua_cpcall 在保護(hù)模式下調(diào)用 func。func 由一個(gè)包含 ud 的輕量用戶數(shù)據(jù)開始。在錯(cuò)誤問題上,lua_cpcall 像 lua_pcall 一樣返回相同的錯(cuò)誤代碼(見 3.15),加上在棧頂?shù)囊粋€(gè)錯(cuò)誤對(duì)象;否則,返回 0,并且不對(duì)堆棧進(jìn)行任何操作。任何由 func 返回的值都被丟棄。
C 代碼可以通過調(diào)用下面的函數(shù)產(chǎn)生一個(gè)Lua錯(cuò)誤:
void lua_error (lua_State *L);
錯(cuò)誤信息(實(shí)際上可以是任何類型的對(duì)象)必須在棧頂。這個(gè)函數(shù)進(jìn)行一個(gè)長跳躍,因此從來不會(huì)返回。
3.20 - 線程
Lua 提供了操作線程的部分支?幀H綣閿卸嘞叱灘僮韉腃 語言庫,那么Lua能夠與其協(xié)作并且在Lua中實(shí)現(xiàn)相同的機(jī)制。同樣,Lua在線程之上實(shí)現(xiàn)自己的協(xié)同程序系統(tǒng)。以下函數(shù)用來在Lua中創(chuàng)建一個(gè)線程:
lua_State *lua_newthread (lua_State *L);
這個(gè)函數(shù)將線程壓入棧并且返回代表新線程的 lua_State 指針。這個(gè)返回的新狀態(tài)與所有全局對(duì)象(例如表)共享初始狀態(tài),但是有一個(gè)獨(dú)立的運(yùn)行時(shí)堆棧。
每個(gè)線程都有自己獨(dú)立的全局環(huán)境表。當(dāng)你創(chuàng)建一個(gè)線程,這張表就和所給狀態(tài)一樣,但是你可以獨(dú)自更改它們。
沒有明確的函數(shù)可以關(guān)閉或者銷毀一個(gè)線程。線程和其他Lua對(duì)象一樣受垃圾收集程序的支配:
要像協(xié)同程序一樣操作線程,Lua提供了以下函數(shù):
int lua_resume (lua_State *L, int narg); int lua_yield (lua_State *L, int nresults);
你需要?jiǎng)?chuàng)建一個(gè)線程以便啟動(dòng)協(xié)同程序;然后你將函數(shù)體和事件參數(shù)壓入堆棧;然后調(diào)用 lua_resume,narg 的值代表參數(shù)的數(shù)量。當(dāng)同步程序暫?;蛘呓Y(jié)束執(zhí)行,函數(shù)將返回。當(dāng)它返回后,棧中包含的所有值傳遞給 lua_yield,或者有主體函數(shù)返回。如果同步程序運(yùn)行無誤,lua_resume 返回 0,否則返回一個(gè)錯(cuò)誤代碼(見 3.15)。對(duì)于錯(cuò)誤,堆棧只包含錯(cuò)誤信息。要重起同步程序,將作為結(jié)果傳遞給 yield 的值壓入堆棧,并且調(diào)用 lua_resume。
lua_yield 函數(shù)只能像C 函數(shù)的返回表達(dá)式一樣被調(diào)用,就像下面所展示的:
return lua_yield (L, nresults);
如果C 函數(shù)像這樣調(diào)用 lua_yield,正在運(yùn)行的同步程序暫停它的執(zhí)行,并且調(diào)用 lua_resume 開始讓這個(gè)協(xié)同程序返回。nresults 這個(gè)參數(shù)代表了在堆棧中作為結(jié)果傳遞給 lua_resume 的值的數(shù)量。
要在不同線程中交換值,你可以使用 lua_xmove:
void lua_xmove (lua_State *from, lua_State *to, int n);
它從堆棧 from 中彈出 n 個(gè)值,并將其壓入堆棧 to。
21:38 |
添加評(píng)論 |
發(fā)送消息 |
固定鏈接 |
查看引用通告 (0) | |
LuaLua - 語法2 - 語言
這一章將描述Lua的詞法、語法和語義結(jié)構(gòu)。換句話說,這一章會(huì)講什么標(biāo)記是合法的,他們是如何組合的,以及他們的組合是什么含義。
語言結(jié)構(gòu)會(huì)使用常用的擴(kuò)展BNF范式來解釋,如{a} 表示0或多個(gè)a, [a] 表示a是可選的(0個(gè)或1個(gè))。非終端字體(不能顯示的)用 斜體表示,關(guān)鍵字是粗體,其他終端符號(hào)用typewriter(等寬)字體,并用單引號(hào)引出。
2.1 - 詞法約定
Lua中的標(biāo)識(shí)符(Identifiers)可以是任意的數(shù)字、字符和下劃線“_”,但不能以數(shù)字開頭。這條規(guī)則符合大多數(shù)編程語言中的標(biāo)識(shí)符的定義。(字符的具體定義要根據(jù)系統(tǒng)的地區(qū)設(shè)置:任何區(qū)域設(shè)置可以認(rèn)同的字母表中的字母都可以用在標(biāo)識(shí)符中。)
下面的關(guān)鍵字(keywords)為保留關(guān)鍵字不可以作為標(biāo)識(shí)符出現(xiàn):
and break do else elseif end false for function if in local nil not or repeat return then true until while
Lua對(duì)大小寫敏感:and是一個(gè)保留字,但是 And 和 AND 是兩個(gè)不一樣的、但都合法的標(biāo)識(shí)符。習(xí)慣上來說,以下劃線開始且后面跟著大寫字母的標(biāo)識(shí)符 (例如 _VERSION) 是為Lua內(nèi)部變量所保留的。
下面的字符(串)是其他的一些標(biāo)記:
+ - * / ^ = ~= <= >= < > == ( ) { } [ ] ; : , . .. ...
字符串(Literal strings) 以單引號(hào)或者雙引號(hào)定界,同時(shí)可以包含以下C語言風(fēng)格的轉(zhuǎn)義字符:
/a --- 鈴聲(bell)
/b --- 回退(backspace)
/f --- form feed
/n --- 新行(newline)
/r --- 回車(carriage return)
/t --- 水平制表符(horizontal tab)
/v --- 垂直制表符(vertical tab)
// --- 反斜杠(backslash)
/" --- 雙引號(hào)(quotation mark)
/' --- 單引號(hào)(apostrophe)
/[ --- 左方括號(hào)(left square bracket)
/] --- 右方括號(hào)(right square bracket)
另外,一個(gè) `/newline′ (一個(gè)反斜杠加上一個(gè)真正的換行符)會(huì)導(dǎo)致字符串內(nèi)的分行。字符串中的字符也可以使用轉(zhuǎn)義字符`/ddd′通過數(shù)字值來指定。ddd 是最多為3個(gè)十進(jìn)制數(shù)字的序列。Lua中的字符串也可以包含8進(jìn)制數(shù)字,包括嵌入零,它可以表示為 `/0′。
字符串也可以用雙方括號(hào)來定界[[ · · · ]]。這種括號(hào)方式的語法,字符串可以跨越多行,也可以包含嵌套的,同時(shí)不會(huì)轉(zhuǎn)義任何序列。方便起見,當(dāng)開始的 `[[′ 后面緊跟著一個(gè)換行符的話,這個(gè)換行符不會(huì)包括在字符串內(nèi)。舉個(gè)例子:在一個(gè)使用ASCII編碼(其中`a′ 的編碼是 97,換行符是 10,字符`1′ 是 49)的系統(tǒng)中,以下四種格式得到的都是同一個(gè)字符串:
(1) "alo/n123/"" (2) '/97lo/10/04923"' (3) [[alo 123"]] (4) [[ alo 123"]]
數(shù)值常量(Numerical constants) 可以有一個(gè)可選的底數(shù)部分和一個(gè)可選的指數(shù)部分。以下是有效的數(shù)值常量:
3 3.0 3.1416 314.16e-2 0.31416E1
注釋(Comments) 可以在任何地方出現(xiàn),必須在最前面加上雙減號(hào) (--)。如果緊接著 -- 的文本不是 [[,那么會(huì)認(rèn)為是一個(gè) 短注釋(short comment), 這一行往后到行尾都是注釋。否則,會(huì)認(rèn)為是一個(gè) 常注釋(long comment),注釋直到相應(yīng)的 ]]結(jié)束。長注釋可以跨越多行,同時(shí)可以包含嵌套的 [[ · · · ]] 括號(hào)對(duì)。
為了方便起見,文件的第一行如果是以#開始,這個(gè)機(jī)制允許Lua在Unix系統(tǒng)中用做一個(gè)腳本解釋器(見 6)。
2.2 - 值和類型
Lua是一種 動(dòng)態(tài)類型語言(dynamically typed language)。這意味著變量是沒有類型的;只有值才有。語言中沒有類型定義。所有的值都包含他自身的類型。
Lua中有八種基本類型:nil, boolean, number, string, function, userdata, thread 和 table。 Nil 空類型只對(duì)應(yīng) nil值,他的屬性和其他任何值都有區(qū)別;通常它代表沒有有效的值。 Boolean 布爾類型有兩種不同的值 false and true。在Lua中, nil and false 代表成假條件;其他任何值都代表成真條件。 Number 數(shù)字類型表示實(shí)數(shù)(雙精度浮點(diǎn)數(shù))。(構(gòu)建Lua解釋器時(shí)也可以很容易地用其他內(nèi)部的表示方式表示數(shù)字,如單精度浮點(diǎn)數(shù)或者長整型)。 String 字符串類型表示一個(gè)字符的序列。Lua 字符串可以包含8位字符,包括嵌入的 ('/0') (見 2.1)。
函數(shù)是Lua中的 第一類值(first-class values)。也就是說函數(shù)可以保存在變量中,當(dāng)作參數(shù)傳遞給其他函數(shù),或者被當(dāng)作結(jié)果返回。Lua可以調(diào)用(和處理)Lua寫的函數(shù)和C寫的函數(shù) (見 2.5.7)。
用戶數(shù)據(jù)類型(userdata) 提供了讓任意C數(shù)據(jù)儲(chǔ)存在Lua變量中的功能。這種類型直接對(duì)應(yīng)著一塊內(nèi)存,Lua中也沒有任何預(yù)先定義的操作,除了賦值和一致性比較。然而,通過使用 元表(metatables),程序員可以定義處理userdata的操作。(見 2.8)。 Userdata 值不能在Lua中建立或者修改,只能通過 C API。這保證了宿主程序的數(shù)據(jù)完整性。
線程(thread) 類型代表了相互獨(dú)立的執(zhí)行線程,用來實(shí)現(xiàn)同步程序。
表(table) 類型實(shí)現(xiàn)了聯(lián)合數(shù)組,也就是說,數(shù)組不僅可以使用數(shù)字,還能使用其他的值(除了 nil)。 而且,tables 可以是 互異的(heterogeneous),他們可以保存任何類型的值(除了 nil)。 Tables 是Lua中唯一的數(shù)據(jù)結(jié)構(gòu)機(jī)制;他們可以用來表示一般數(shù)組,特征表,集合,記錄,圖,樹等等。如果要表示記錄,Lua使用字段名作為索引。語言支持 a.name 這種比較優(yōu)美的表示方式,還有 a["name"]。在Lua中有幾種建立表的簡便方法 (見 2.5.6)。
就像索引一樣,表字段的值也可以是任何類型(除了 nil)。特別需要注意地是,由于函數(shù)是第一型的值,表字段也可以包含函數(shù)。這樣表也可以支持 方法(methods) (見 2.5.8)。
表,函數(shù),和用戶數(shù)據(jù)類型的值都是 對(duì)象(objects):變量不會(huì)包含他們的實(shí)際值,只是一個(gè)他們的引用(references)。 賦值,參數(shù)傳遞和函數(shù)返回只是操作這些值的引用,這些操作不會(huì)暗含任何拷貝。
庫函數(shù) type 返回一個(gè)字符串描述給出值所表示的類型 (見 5.1)。
2.2.1 - 類型轉(zhuǎn)換
Lua提供運(yùn)行時(shí)的數(shù)字和字符串值得自動(dòng)轉(zhuǎn)換。任何對(duì)字符串的算術(shù)操作都會(huì)現(xiàn)嘗試把字符串轉(zhuǎn)換成數(shù)字,使用一般規(guī)則轉(zhuǎn)換。反過來,當(dāng)一個(gè)數(shù)值用在需要字符串的地方時(shí),數(shù)字會(huì)自動(dòng)轉(zhuǎn)換成字符串,遵循一種合理的格式。如果要指定數(shù)值如何轉(zhuǎn)換成字符串,請(qǐng)使用字符串庫中的 format 函數(shù)(見 5.3)。
2.3 - 變量
變量是儲(chǔ)存值的地方。Lua中有三種不同的變量:全局變量,局部變量和表字段。
一個(gè)名稱可以表示全局變量或局部變量(或者一個(gè)函數(shù)的正式參數(shù),一種局部變量的特殊形式):
var ::= Name
Lua假設(shè)變量是全局變量,除非明確地用local進(jìn)行聲明 (見 2.4.7)。局部變量有 詞義范圍(lexically scoped):局部變量可以被在它們范圍內(nèi)的函數(shù)自由訪問 (見 2.6)。
在變量第一次賦值之前,它的值是 nil。
方括號(hào)用于對(duì)表進(jìn)行檢索:
var ::= prefixexp `[′ exp `]′
第一個(gè)表達(dá)式 (prefixexp)結(jié)果必須是表;第二個(gè)表達(dá)式 (exp) 識(shí)別表中一個(gè)特定條目。給出表的表達(dá)式有一個(gè)限制語法;詳細(xì)見 2.5。
var.NAME 語法是 var["NAME"] 的較好形式:
var ::= prefixexp `.′ Name
訪問全局變量和表字段的實(shí)質(zhì)可以通過元表進(jìn)行改變。對(duì)索引變量 t[i] 的訪問等同于調(diào)用 gettable_event(t,i)。(關(guān)于 gettable_event 的完整描述見 2.8。這個(gè)函數(shù)并沒有在Lua中定義,也無法調(diào)用。我們?cè)谶@里僅僅用來解釋原理)。
所有的全局變量存在一個(gè)普?ǖ腖ua表中,稱之為 環(huán)境變量表(environment tables) 或簡稱 環(huán)境(environments)。由C寫的并導(dǎo)入到Lua中的函數(shù) (C 函數(shù)) 全部共享一個(gè)通用 全局環(huán)境(global environment)。Lua寫的每個(gè)函數(shù) (a Lua 函數(shù)) 都有一個(gè)它自己的環(huán)境的引用,這樣這個(gè)函數(shù)中的所有的全局變量都會(huì)指向這個(gè)環(huán)境變量表。當(dāng)新創(chuàng)建一個(gè)函數(shù)時(shí),它會(huì)繼承創(chuàng)建它的函數(shù)的環(huán)境。要改變或者獲得Lua函數(shù)的環(huán)境表,可以調(diào)用 setfenv or getfenv (見 5.1)。
訪問全局變量 x 等同于 _env.x,又等同于
gettable_event(_env, "x")
_env 是運(yùn)行的函數(shù)的環(huán)境。(_env 變量并沒有在Lua中定義。我們這里僅僅用來解釋原理)
2.4 - 語句
Lua支持一種很通俗的語句集,和Pascal或者C中的很相似。他包括賦值,控制結(jié)構(gòu),過程調(diào)用,表構(gòu)造和變量聲明。
2.4.1 - 語句段
Lua執(zhí)行的最小單元稱之為一個(gè) 段(chunk)。一段語句就是簡單的語句的序列,以順序執(zhí)行。每一個(gè)語句后面都可以加上一個(gè)分號(hào)(可選):
chunk ::= {stat [`;′]}
Lua將語句段作為一個(gè)匿名函數(shù) (見 2.5.8) 的本體進(jìn)行處理。這樣,語句段可以定義局部變量或者返回值。
一段語句可以儲(chǔ)存在文件內(nèi)或者宿主程序的一個(gè)字符串中。當(dāng)語句段被執(zhí)行時(shí),他首先被預(yù)編譯成虛擬機(jī)使用的字節(jié)碼,然后虛擬機(jī)用一個(gè)解釋器執(zhí)行被編譯的代碼。
語句段也可以被預(yù)編譯為二進(jìn)制代碼;詳情參看 luac 程序。源代碼和編譯形態(tài)可以互相轉(zhuǎn)換;Lua自動(dòng)監(jiān)測文件類型然后作相應(yīng)操作。
2.4.2 - 語句塊
一個(gè)語句塊是一系列語句;從語句構(gòu)成上來看,語句塊等同于語句段:
block ::= chunk
一個(gè)語句塊可以明確定界來替換單個(gè)語句:
stat ::= do block end
顯式語句塊可以很好地控制變量的聲明范圍。顯示語句塊有時(shí)也常會(huì)在另一個(gè)語句塊的中間添加 return 或 break 語句 (見 2.4.4)。
2.4.3 - 賦值
Lua允許多重賦值。因此,賦值的語法定義為:等號(hào)左邊是一個(gè)變量表,右邊是一個(gè)表達(dá)式表。兩邊的表中的元素都用逗號(hào)分隔開來:
stat ::= varlist1 `=′ explist1 varlist1 ::= var {`,′ var} explist1 ::= exp {`,′ exp}
我們將在 2.5 討論表達(dá)式。
在賦值之前,值的表長度會(huì)被 調(diào)整 為和變量的表一樣。如果值比需要的多,多出的值就會(huì)被扔掉。如果值的數(shù)量不夠,就會(huì)用足夠多的 nil 來填充表直到滿足數(shù)量要求。如果表達(dá)式表以一個(gè)函數(shù)調(diào)用結(jié)束,那么在賦值之前,函數(shù)返回的所有的值都會(huì)添加到值的表中(除非把函數(shù)調(diào)用放在括號(hào)里面;見 2.5)。
賦值語句首先計(jì)算出所有的表達(dá)式,然后才會(huì)執(zhí)行賦值,所以代碼:
i = 3 i, a[i] = i+1, 20
設(shè)置 a[3] 為 20,但不影響 a[4]。因?yàn)樵?a[i] 中的 i 在賦值為4之前是等于3。同樣的,下面這行:
x, y = y, x
可以交換 x 和 y 的值。
對(duì)全局變量和表字段的賦值可以看作是通過元表進(jìn)行的。對(duì)一個(gè)索引變量的賦值 t[i] = val 等同于 settable_event(t,i,val)。 (settable_event詳細(xì)介紹參看 2.8 ,Lua中并未定義該函數(shù),他也無法直接調(diào)用。我們這里只是用它來進(jìn)行解釋。)
對(duì)全局變量的賦值 x = val 等同于賦值語句 _env.x = val,像前面也等同于:
settable_event(_env, "x", val)
_env 是運(yùn)行函數(shù)的環(huán)境。(_env 變量并未在Lua中定義。我們這里只是用來進(jìn)行解釋。)
2.4.4 - 控制結(jié)構(gòu)
控制結(jié)構(gòu) 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)。
控制結(jié)構(gòu)的條件表達(dá)式 exp 可以返回任意值。false 和 nil 都表示假。所有其他的值都認(rèn)為是真(特別要說明的:數(shù)字0和空字符串也表示真)。
語句 return 用來從函數(shù)或者是語句段中返回一個(gè)值。函數(shù)和語句段都可以返回多個(gè)值,所以 return 語句的語法為:
stat ::= return [explist1]
break 語句可以用來終止while, repeat 或者 for 循環(huán)的執(zhí)行,直接跳到循環(huán)后面的語句。
stat ::= break
break 結(jié)束最里面的一個(gè)循環(huán)。
由于語法的原因, return 和 break 語句只能作為語句塊的 最后一個(gè) 語句。如果確實(shí)需要在語句塊的中間使用 return 或者 break,需要使用一個(gè)顯示語句塊: `do return end′ 和 `do break end′,這樣現(xiàn)在 return 和 break 就成為他們(內(nèi)部)語句塊中的最后一個(gè)語句了。實(shí)際上,這兩種用法一般只用在調(diào)試中。
2.4.5 - For 語句
for 語句有兩種形式:數(shù)值形式和一般形式。
數(shù)值形式的 for 循環(huán)根據(jù)一個(gè)控制變量用算術(shù)過程重復(fù)一語句塊。語法如下:
stat ::= for Name `=′ exp `,′ exp [`,′ exp] do block end
block 語句塊根據(jù) name 以第一個(gè) exp 的值開始,直到他以第三個(gè) exp 為步長達(dá)到了第二個(gè) exp。一個(gè)這樣的 for 語句:
for var = e1, e2, e3 do block end
等價(jià)于一下代碼:
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 block var = var + _step end end
注意:
三種控制表達(dá)式只會(huì)被計(jì)算一次,在循環(huán)開始之前。他們的結(jié)果必須是數(shù)值。
_limit 和 _step 是不可見的變量。這里只是為了進(jìn)行解釋。
如果你在程序塊內(nèi)給 var 賦值,結(jié)果行為將會(huì)不確定。
如果沒有給出第三個(gè)表達(dá)式(步長),那么默認(rèn)為1。
你可以使用 break 來退出 for 循環(huán)。
循環(huán)變量 var 是局部變量;你不可以在 for 循環(huán)結(jié)束之后繼續(xù)使用。如果你需要使用這個(gè)值,請(qǐng)?jiān)谕顺鲅h(huán)之前把它們傳給其他變量。
for 的語句的一般形式是操作于函數(shù)之上的,稱之為迭代器(iterators)。每一個(gè)迭代過程,它調(diào)用迭代函數(shù)來產(chǎn)生新的值,直到新的值是 nil 。一般形式 for 循環(huán)有如下語法:
stat ::= for Name {`,′ Name} in explist1 do block end
一個(gè)這樣的 for 語句
for var_1, ..., var_n in explist do block end
等同于以下代碼:
do local _f, _s, var_1 = explist local var_2, ... , var_n while true do var_1, ..., var_n = _f(_s, var_1) if var_1 == nil then break end block end end
注意:
explist 只會(huì)計(jì)算一次。他的結(jié)果是一個(gè) 迭代 函數(shù),一個(gè) 狀態(tài),和給第一個(gè) 迭代變量的一個(gè)初始值。
_f 和 _s 是不可見的變量。這里只是用來進(jìn)行解釋說明。
如果你在語句塊中給 var_1 賦值,那么行為就會(huì)變得不確定。
你可以使用 break 來退出 for 循環(huán)。
循環(huán)變量 var_i 是局部變量;你不可以在 for 循環(huán)結(jié)束之后繼續(xù)使用。如果你需要使用這個(gè)值,請(qǐng)?jiān)谕顺鲅h(huán)之前把它們傳給其他變量。
2.4.6 - 語句式函數(shù)調(diào)用
如果要忽略可能的影響,函數(shù)調(diào)用可以按照語句執(zhí)行:
stat ::= functioncall
I在這里,所有的返回值都會(huì)被忽略。函數(shù)調(diào)用將在 2.5.7 詳細(xì)解釋。
2.4.7 - 局部變量聲明
局部變量可以在語句塊中任何地方聲明。聲明時(shí)也可以添加一個(gè)初始賦值:
stat ::= local namelist [`=′ explist1] namelist ::= Name {`,′ Name}
如果出現(xiàn)初始賦值,他的語法和多重賦值語句一樣(見 2.4.3)。否則,所有的變量都會(huì)初始化為 nil。
一個(gè)語句段也是一個(gè)語句塊(見 2.4.1),所以語句段之內(nèi)的任何顯式語句塊之外也可以聲明局部變量。這種局部變量在語句段結(jié)束就會(huì)銷毀。
局部變量的可見規(guī)則會(huì)在 2.6解釋。
2.5 - 表達(dá)式
Lua中有以下幾種基本表達(dá)式:
exp ::= prefixexp exp ::= nil | false | true exp ::= Number exp ::= Literal exp ::= function exp ::= tableconstructor prefixexp ::= var | functioncall | `(′ exp `)′
數(shù)字和字符串已經(jīng)在 2.1 中解釋;變量在 2.3 中解釋;函數(shù)定義在 2.5.8;函數(shù)調(diào)用在 2.5.7;表構(gòu)造器在 2.5.6。
一個(gè)用括號(hào)括起的表達(dá)式只會(huì)返回一個(gè)值。這樣,(f(x,y,z)) 將只會(huì)返回單一的一個(gè)值,即使 f 可以返回多個(gè)值,((f(x,y,z)) 的值將是 f 返回的第一個(gè)值或者如果 f 沒有返回任何值就是 nil )。
表達(dá)式也可以使用各種算術(shù)運(yùn)算符,關(guān)系運(yùn)算符和邏輯運(yùn)算符,下面幾節(jié)就會(huì)講到。
2.5.1 - 算術(shù)運(yùn)算符
Lua支持常見的幾種運(yùn)算符:二元 + (加), - (減), * (乘), / (除), 以及 ^ (指數(shù)運(yùn)算); 一元 - (負(fù)號(hào))。如果操作數(shù)是數(shù)字,或者是可以轉(zhuǎn)換成數(shù)字的字符串(見 2.2.1),那么所有的操作都和算術(shù)意義上的運(yùn)算一致(除了指數(shù))。指數(shù)運(yùn)算其實(shí)是調(diào)用一個(gè)全局函數(shù) __pow,否則一個(gè)合適的元方法將會(huì)被調(diào)用(見 2.8)。標(biāo)準(zhǔn)數(shù)學(xué)庫定義了函數(shù) __pow,給出了指數(shù)運(yùn)算的定義(見 5.5)。
2.5.2 - 關(guān)系運(yùn)算符
Lua中的關(guān)系運(yùn)算符有
== ~= < > <= >=
這些運(yùn)算只會(huì)產(chǎn)生 false 或 true值。
等于 (==) 先比較操作數(shù)的類型。如果類型不一樣,結(jié)果便是 false。否則,再比較操作數(shù)的值。對(duì)象(表,用戶數(shù)據(jù),線程,和函數(shù))是按照引用進(jìn)行比較:只有兩個(gè)對(duì)象是同一個(gè)對(duì)象的時(shí)候,才認(rèn)為是相等。每次你創(chuàng)建一個(gè)新的對(duì)象(表,用戶數(shù)據(jù),或者是函數(shù))。這個(gè)新的對(duì)象將不同于前面存在的任何對(duì)象。
你可以用"eq"元方法改變Lua比較表的方式(見 2.8)。
2.2.1 的轉(zhuǎn)換規(guī)則 不適用 于相等比較。這樣," "0"==0 結(jié)果是 false ,同樣 t[0] 和 t["0"] 給出的是表中不同的字段。
而操作符 ~= 是等于 (==) 的相反的操作。
T操作符的執(zhí)行順序如下。如果兩個(gè)參數(shù)都是數(shù)字,那么它們就直接進(jìn)行比較。如果,兩個(gè)參數(shù)都是字符串,那么它們的值會(huì)根據(jù)當(dāng)前的區(qū)域設(shè)置進(jìn)行比較。否則,Lua嘗試調(diào)用"lt"或者 "le" 元方法(見 2.8)。
2.5.3 - 邏輯運(yùn)算符
Lua中的邏輯運(yùn)算符是:
and or not
和控制結(jié)構(gòu)一樣(見 2.4.4),所有的邏輯操作符認(rèn)為 false 和 nil 都?羌伲淥鬧刀際欽妗?
not 操作符總是返回 false 或 true。
合取運(yùn)算 and 如果第一個(gè)參數(shù)是 false 或者 nil 則返回第一個(gè)參數(shù);否則 and 返回第二個(gè)參數(shù)。析取運(yùn)算 or 如果第一個(gè)參數(shù)不是 nil 或 false 則返回第一個(gè)參數(shù),否則 or 返回第二個(gè)參數(shù)。 and 和 or 都使用截取計(jì)算,也就是,只有有必要的情況下才計(jì)算第二個(gè)參數(shù)。例如:
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 - 串聯(lián)接
在Lua中字符串連接操作符是兩個(gè)點(diǎn) (`..′)。如果兩邊的操作數(shù)都是字符或者數(shù)字,他們就都會(huì)按照 2.2.1的規(guī)則被轉(zhuǎn)換成字符串。否則,將調(diào)用 "concat" 元方法(見 2.8)。
2.5.5 - 優(yōu)先級(jí)
Lua中的操作符的優(yōu)先級(jí)如下表所示,從低到高優(yōu)先級(jí):
or and < > <= >= ~= == .. + - * / not - (unary) ^
表達(dá)式中,你可以使用括號(hào)來改變優(yōu)先順序。串聯(lián)接符 (`..′) 和指數(shù)符 (`^′) 都是右結(jié)合的。其他二元操作都是左結(jié)合的。
2.5.6 - 表構(gòu)造器
表構(gòu)造器是創(chuàng)建表的表達(dá)式。當(dāng)計(jì)算構(gòu)造器的時(shí)候,就會(huì)創(chuàng)建一個(gè)新的表。構(gòu)造器可以用來創(chuàng)建空的表,或者創(chuàng)建表并初始化一些字段。一般的語法如下:
tableconstructor ::= `{′ [fieldlist] `}′ fieldlist ::= field {fieldsep field} [fieldsep] field ::= `[′ exp `]′ `=′ exp | Name `=′ exp | exp fieldsep ::= `,′ | `;′
[exp1] = exp2 形式的每一個(gè)添加到新表中的字段條目以 exp1 為鍵并以 exp2 為值。name = exp 形式的字段,等同于 ["name"] = exp。最后,exp 形式的字段等同于 [i] = exp 其中 i 是連續(xù)的整數(shù),從1開始。其它格式的字段不會(huì)影響它的計(jì)數(shù)。例如:
a = {[f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45}
等同于:
do local temp = {} temp[f(1)] = g temp[1] = "x" -- 1st exp temp[2] = "y" -- 2nd exp temp.x = 1 -- temp["x"] = 1 temp[3] = f(x) -- 3rd exp temp[30] = 23 temp[4] = 45 -- 4th exp a = temp end
如果列表中最后一個(gè)字段的形式是 exp 同時(shí)表達(dá)式又是一個(gè)函數(shù)調(diào)用,那么調(diào)用返回的所有值會(huì)依次進(jìn)入列表(見 2.5.7)。如果要避免這種情況,在函數(shù)調(diào)用兩邊加上括號(hào)(見 2.5)。
字段列表可以有一個(gè)結(jié)尾的分隔符,這個(gè)對(duì)由機(jī)器生成的列表十分方便。
2.5.7 - 函數(shù)調(diào)用
Lua中的一個(gè)函數(shù)調(diào)用有如下語法:
functioncall ::= prefixexp args
在函數(shù)調(diào)用中,首先會(huì)計(jì)算 prefixexp 和 args 。如果 prefixexp 的值是 function 類型,那么那個(gè)函數(shù)就會(huì)被調(diào)用,同時(shí)使用給出的參數(shù)。否則,他的 "call" 元方法就會(huì)被調(diào)用,第一個(gè)參數(shù)是 prefixexp 的值,接下來是原來的調(diào)用參數(shù)(見 2.8)。
形式
functioncall ::= prefixexp `:′ Name args
可以用來調(diào)用“方法”("methods")。調(diào)用 v:name(...) 語法上比 v.name(v,...),要好一些,除非表達(dá)式 v 只計(jì)算一次。
參數(shù)可以有以下幾種語法:
args ::= `(′ [explist1] `)′ args ::= tableconstructor args ::= Literal
所有的參數(shù)表達(dá)式都會(huì)在實(shí)際調(diào)用之前進(jìn)行計(jì)算。f{...} 的調(diào)用形式在語法上較 f({...}) 要好,是因?yàn)?,參?shù)列表示一個(gè)單獨(dú)的新表。 f'...' (或者 f"..." 或者 f[[...]]) 較 f('...') 要好,是因?yàn)閰?shù)列表是一個(gè)單獨(dú)的字符串。
因?yàn)楹瘮?shù)可以返回任意個(gè)結(jié)果(見 2.4.4),結(jié)果的數(shù)量必須在使用它們前進(jìn)行調(diào)整。如果函數(shù)按照語句進(jìn)行調(diào)用(見 2.4.6),那么它的返回列表就會(huì)被調(diào)整為零個(gè)元素,這樣就舍棄了所有的返回值。如果調(diào)用函數(shù)時(shí),他是一個(gè)表達(dá)式列表的最后一個(gè)元素,那么不會(huì)做調(diào)整(除非調(diào)用時(shí)加了括號(hào))。
以下是一些例子:
f() -- 調(diào)整為0個(gè)結(jié)果 g(f(), x) -- f() 被調(diào)整成1個(gè)結(jié)果 g(x, f()) -- g 獲得 x 加上f()返回的所有值 a,b,c = f(), x -- f() 被調(diào)整成1個(gè)結(jié)果(此時(shí)c獲得nil值) a,b,c = x, f() -- f() 被調(diào)整為兩個(gè)結(jié)果 a,b,c = f() -- f() 被調(diào)整為3個(gè)結(jié)果 return f() -- 返回所有 f() 返回的值 return x,y,f() -- 建立一個(gè)表包含所有 f() 返回的值 {f()} -- creates a list with all values returned by f() {f(), nil} -- f() 被調(diào)整為一個(gè)結(jié)果
如果你用括號(hào)括起調(diào)用的函數(shù),那么它就會(huì)被調(diào)整為返回一個(gè)值。
return x,y,(f()) -- returns x, y, and the first value from f() {(f())} -- creates a table with exactly one element
作為Lua語法自由格式的一個(gè)例外,你不能在函數(shù)調(diào)用的 `(′ 前面加入一個(gè)換行。這個(gè)限制可以避免語言中的一些二義性。如果你寫:
a = f (g).x(a)
Lua會(huì)讀作 a = f(g).x(a)。這樣,如果你想執(zhí)行為兩條語句,你必須在中間加分號(hào)。如果你實(shí)際上想調(diào)用 f,你就必須刪除 (g) 前面的換行。
return functioncall 的調(diào)用格式稱之為 尾部調(diào)用(tail call)。Lua實(shí)現(xiàn)了proper tail calls;在一個(gè)尾部調(diào)用中,被調(diào)用的函數(shù)將會(huì)重新使用調(diào)用程序的棧。因此,程序執(zhí)行對(duì)嵌套尾部調(diào)用的次數(shù)沒有任何限制。然而,尾部調(diào)用會(huì)清楚調(diào)用函數(shù)的調(diào)試信息。注意尾部調(diào)用只有在特殊的語法中才能出現(xiàn),也就是 return 只有一個(gè)函數(shù)調(diào)用作為參數(shù),這種語法保證了調(diào)用函數(shù)確切返回被調(diào)用函數(shù)的返回值。所以,下面的例子都不是尾部調(diào)用:
return (f(x)) -- results adjusted to 1 return 2 * f(x) return x, f(x) -- additional results f(x); return -- results discarded return x or f(x) -- results adjusted to 1
2.5.8 - 函數(shù)定義
函數(shù)定義的語法是:
function ::= function funcbody funcbody ::= `(′ [parlist1] `)′ block end
下面較好的語法簡化了函數(shù)定義:
stat ::= function funcname funcbody stat ::= local function Name funcbody funcname ::= Name {`.′ Name} [`:′ Name]
語句
function f () ... end
會(huì)被翻譯為
f = function () ... end
語句
function t.a.b.c.f () ... end
會(huì)被翻譯為
t.a.b.c.f = function () ... end
語句
local function f () ... end
會(huì)被翻譯為
local f; f = function () ... end
一個(gè)函數(shù)定義是一個(gè)可執(zhí)行的表達(dá)式,他的類型為 函數(shù)(function) 。當(dāng)Lua預(yù)編譯語句段的時(shí)候,他的函數(shù)體也會(huì)被預(yù)編譯。這樣,當(dāng)Lua執(zhí)行函數(shù)定義的時(shí)候,函數(shù)被 實(shí)例化 (封裝 closed)。這個(gè)函數(shù)實(shí)例(或閉包 closure)是表達(dá)式的最終結(jié)果。同一個(gè)函數(shù)的不同的實(shí)例可以引用不同的外部局部變量也可以有不同的環(huán)境表。
形式參數(shù)(代表參數(shù)的變量,簡稱形參)就像用實(shí)際參數(shù)值(簡稱實(shí)參)初始化的局部變量一樣。
parlist1 ::= namelist [`,′ `...′] parlist1 ::= `...′
當(dāng)調(diào)用一個(gè)函數(shù)時(shí),實(shí)參表會(huì)調(diào)整為和形參一樣的長度,除非函數(shù)是 variadic 或者 變長參數(shù)函數(shù)(vararg function)。變長參數(shù)函數(shù)在其參數(shù)列表最后有三個(gè)點(diǎn) (`...′)。 變長參數(shù)函數(shù)不會(huì)對(duì)參數(shù)列表進(jìn)行調(diào)整;而是,它把所有的額外實(shí)參放到一個(gè)隱含的形參 arg中。 的值是一個(gè)表,包含一個(gè)字段 `n′ 表示額外參數(shù)的個(gè)數(shù),位置 1, 2, ..., n是額外的參數(shù)。
請(qǐng)思考以下函數(shù)定義的例子:
function f(a, b) end function g(a, b, ...) end function r() return 1,2,3 end
然后,我們有以下實(shí)參到形參的對(duì)應(yīng)關(guān)系:
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, arg={n=0} g(3, 4) a=3, b=4, arg={n=0} g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2} g(5, r()) a=5, b=1, arg={2, 3; n=2}
結(jié)果使用 return 語句返回(見 2.4.4)。如果控制到達(dá)了函數(shù)尾部而沒有遇到 return 語句,那么函數(shù)沒有返回值。
冒號(hào)(:) 語法是用來定義 methods 的,也就是,函數(shù)有一個(gè)隱含的額外參數(shù) self. 。這樣,語句:
function t.a.b.c:f (...) ... end
相對(duì)以下是較好的形式:
t.a.b.c.f = function (self, ...) ... end
2.6 - 可見性規(guī)則
Lua是一個(gè)有詞法范圍的語言。變量的范圍從聲明語句后的第一個(gè)語句開始到包含聲明的最內(nèi)部的語句塊為止。例如:
x = 10 -- global variable do -- new block local x = x -- new `x', with value 10 print(x) --> 10 x = x+1 do -- another block local x = x+1 -- another `x' print(x) --> 12 end print(x) --> 11 end print(x) --> 10 (the global one)
注意:在類似 local x = x,正在聲明的新的 x 尚未進(jìn)入范圍,所以第二個(gè) x 指代的是外面的變量。
由于詞法范圍的規(guī)則,在局部變量的范圍內(nèi)定義的函數(shù)可以任意訪問這些變量。例如:
local counter = 0 function inc (x) counter = counter + x return counter end
內(nèi)部函數(shù)使用的局部變量在函數(shù)內(nèi)部稱之為 上值(upvalue),或者 外局部變量(external local variable)。
注意每個(gè) local 語句執(zhí)行時(shí)會(huì)定義一個(gè)新的局部變量??匆韵吕樱?div style="height:15px;">
a = {} local x = 20 for i=1,10 do local y = 0 a[i] = function () y=y+1; return x+y end end
循環(huán)產(chǎn)生了十個(gè)閉包(也就是,十個(gè)匿名函數(shù)的實(shí)例)。每個(gè)閉包使用不同的 y 變量,但他們共享同一個(gè) x 變量。
因?yàn)長ua是一個(gè)擴(kuò)展語言,所有的Lua動(dòng)作都是從宿主程序中調(diào)用Lua庫中函數(shù)的C代碼開始的(見 3.15)。無論錯(cuò)誤發(fā)生在Lua編譯過程時(shí)或執(zhí)行時(shí),控制返回C,然后可以做相應(yīng)的處理(比如打印一個(gè)錯(cuò)誤)。
Lua代碼可以通過調(diào)用error函數(shù)來產(chǎn)生一個(gè)錯(cuò)誤(見 5.1)。如果你要在Lua中捕獲錯(cuò)誤,你可以使用 pcall 函數(shù)(見 5.1)。
Lua中的每一個(gè)表和用戶數(shù)據(jù)都可以擁有一個(gè) 元表(metatable)。這個(gè) 元表 是一個(gè)普通的Lua表,定義了在特定操作下原始表和用戶數(shù)據(jù)的行為。你可以通過設(shè)置一個(gè)對(duì)象的元表中的特定字段來更改它某些方面的行為。例如,當(dāng)一個(gè)對(duì)象是一個(gè)加法的操作數(shù)時(shí),Lua檢查它的元表中的 "__add" 字段是不是一個(gè)函數(shù)。如果是,Lua調(diào)用它來執(zhí)行加法。
我們稱元表中的鍵(字段名,key)為 事件(events) ,值為 元方法(metamethods)。在上一個(gè)例子中, "add" 是事件,執(zhí)行加法的函數(shù)是元方法。
元表可以控制對(duì)象在算術(shù)操作、比較、串連接、索引取值中如何運(yùn)行。元表也可以定義一個(gè)函數(shù)當(dāng)收集內(nèi)存垃圾時(shí)調(diào)用。每一個(gè)操作這里L(fēng)ua都用一個(gè)特定的鍵關(guān)聯(lián),稱之為事件。當(dāng)Lua對(duì)一個(gè)表或是一個(gè)用戶數(shù)據(jù)執(zhí)行上面中的一個(gè)操作時(shí),它先檢查元表控制的操作已經(jīng)羅列在下面。每個(gè)操作有一個(gè)相應(yīng)的名稱,代表了他的含義。他們?cè)谠碇械逆I是由名稱前加上兩條下劃線;如,操作 "add" 的鍵是 "__add"。這些操作的語義
這里給出的Lua代碼僅僅是說明性的;真正的行為是硬編碼在解釋器中的,比下面的的模擬的效率要高很多。描述中用到的函數(shù) (rawget, tonumber, 等等) 在 5.1 中會(huì)對(duì)他們進(jìn)行描述。特別地,要獲得一個(gè)給定對(duì)象的元方法,我們使用這個(gè)表達(dá)式:
也就是,訪問元方法時(shí)不會(huì)調(diào)用其它元方法,同時(shí)調(diào)用沒有元表的對(duì)象不會(huì)出錯(cuò)(它返回一個(gè) nil值)。
下面的 getbinhandler 函數(shù)定義了Lua如何給一個(gè)二元操作選擇一個(gè)處理器。首先,Lua嘗試第一個(gè)操作數(shù)。如果它的類型沒有定義這個(gè)操作的處理器,那么然后Lua嘗試第二個(gè)操作數(shù)。
function getbinhandler (op1, op2, event) return metatable(op1)[event] or metatable(op2)[event] end
function add_event (op1, op2) local o1, o2 = tonumber(op1), tonumber(op2) if o1 and o2 then -- both operands are numeric? return o1 + o2 -- `+' here is the primitive `add' else -- at least one of the operands is not numeric local h = getbinhandler(op1, op2, "__add") if h then -- call the handler with both operands return h(op1, op2) else -- no handler available: default behavior error("...") end end end
function pow_event (op1, op2) local o1, o2 = tonumber(op1), tonumber(op2) if o1 and o2 then -- both operands are numeric? return __pow(o1, o2) -- call global `__pow' else -- at least one of the operands is not numeric local h = getbinhandler(op1, op2, "__pow") if h then -- call the handler with both operands return h(op1, op2) else -- no handler available: default behavior error("...") end end end
function unm_event (op) local o = tonumber(op) if o then -- operand is numeric? return -o -- `-' here is the primitive `unm' else -- the operand is not numeric. -- Try to get a handler from the operand local h = metatable(op).__unm if h then -- call the handler with the operand and nil return h(op, nil) else -- no handler available: default behavior 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 -- primitive string concatenation else local h = getbinhandler(op1, op2, "__concat") if h then return h(op1, op2) else error("...") end end end
"eq": == 操作。函數(shù) getcomphandler 定義了Lua是如何為比較操作選擇一個(gè)元方法的。只有當(dāng)參與比較的兩個(gè)對(duì)象屬于同一類型而且需要的元方法一樣時(shí),才會(huì)選擇這個(gè)元方法。
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
function eq_event (op1, op2) if type(op1) ~= type(op2) then -- different types? return false -- different objects end if op1 == op2 then -- primitive equal? return true -- objects are equal end -- try metamethod local h = getcomphandler(op1, op2, "__eq") if h then return h(op1, op2) else return false end end
a ~= b is equivalent to not (a == b).
function lt_event (op1, op2) if type(op1) == "number" and type(op2) == "number" then return op1 < op2 -- numeric comparison elseif type(op1) == "string" and type(op2) == "string" then return op1 < op2 -- lexicographic comparison else local h = getcomphandler(op1, op2, "__lt") if h then return h(op1, op2) else error("..."); end end end
a > b is equivalent to b < a.
function le_event (op1, op2) if type(op1) == "number" and type(op2) == "number" then return op1 <= op2 -- numeric comparison elseif type(op1) == "string" and type(op2) == "string" then return op1 <= op2 -- lexicographic comparison 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 is equivalent to b <= a. Note that, in the absence of a "le" metamethod, Lua tries the "lt", assuming that a <= b is equivalent to not (b < a).
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) -- call the handler else return h[key] -- or repeat operation on it end
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) -- call the handler else h[key] = value -- or repeat operation on it end
function function_event (func, ...) if type(func) == "function" then return func(unpack(arg)) -- primitive call else local h = metatable(func).__call if h then return h(func, unpack(arg)) else error("...") end end end
Lua 會(huì)自動(dòng)進(jìn)行內(nèi)存管理。這意味著你不需要擔(dān)心新對(duì)象的內(nèi)存分配問題,也不需要釋放不用的對(duì)象。Lua 通過不斷地運(yùn)行 垃圾收集器 收集 dead objects(也就是那些Lua中無法訪問的對(duì)象)來自動(dòng)管理內(nèi)存。Lua中所有的對(duì)象都是自動(dòng)管理的目標(biāo):表,用戶數(shù)據(jù),函數(shù),線程,和字符串。Lua使用兩個(gè)數(shù)字控制垃圾收集循環(huán)。一個(gè)數(shù)字表示Lua使用的動(dòng)態(tài)內(nèi)存的字節(jié)數(shù),另一個(gè)是閥值。當(dāng)內(nèi)存字節(jié)數(shù)到達(dá)閥值時(shí),Lua就運(yùn)行垃圾收集器,來釋放死對(duì)象的空間。一旦字節(jié)計(jì)數(shù)器被調(diào)整,那么閥值就會(huì)被設(shè)為字節(jié)計(jì)數(shù)器新值的兩倍。
通過C API,你可以查詢和更改閥值(見 3.7)。將閥值設(shè)為零時(shí)會(huì)強(qiáng)制立刻進(jìn)行垃圾收集,同時(shí)把他設(shè)為足夠大就可以停止垃圾收集。僅使用Lua代碼中的 gcinfo 和 collectgarbage 函數(shù) (見 5.1)可以獲得一定程度上對(duì)垃圾收集循環(huán)的控制。
使用 C API,你可以對(duì)用戶數(shù)據(jù)設(shè)置一個(gè)垃圾收集元方法(見 2.8)。這些元方法也稱為 終結(jié)器(finalizers)。終結(jié)器允許你用外部的資源管理來調(diào)整Lua的垃圾收集(如關(guān)閉文件,網(wǎng)絡(luò)或數(shù)據(jù)庫連接,或者釋放你自己的內(nèi)存。
用元表中包含 __gc 字段的自由用戶數(shù)據(jù)不會(huì)立即被垃圾收集器回收。而是,Lua把它們放在一個(gè)列表中。收集完畢之后,Lua會(huì)對(duì)這個(gè)列表中的用戶數(shù)據(jù)執(zhí)行和以下函數(shù)相等的操作:
function gc_event (udata) local h = metatable(udata).__gc if h then h(udata) end end
在每個(gè)垃圾收集過程最后,調(diào)用用戶數(shù)據(jù)的終結(jié)器的順序,將按照他們?cè)谑占^程中添加到列表中的相反順序進(jìn)行。也就是,第一個(gè)被調(diào)用的終結(jié)器是和在程序中創(chuàng)建的最后一個(gè)用戶數(shù)據(jù)相關(guān)的那個(gè)終結(jié)器。
一個(gè) 弱表(weak table) 是一個(gè)包含的元素是 弱引用(weak references)的表。垃圾收集器會(huì)忽略弱引用。換句話說,如果指向一個(gè)對(duì)象的引用只有弱引用,那么這個(gè)對(duì)象還是要被垃圾收集器回收。
弱表可以包含弱的鍵,弱的值,或者兩者皆有。一個(gè)包含弱鍵的表允許它的鍵被回收,但值不可以。一個(gè)同時(shí)包含弱鍵和弱值的表允許鍵和值的回收。無論哪種情況,只要鍵或者值中的一個(gè)被回收了,那么這一對(duì)鍵值將會(huì)從表中刪除。這個(gè)表的弱屬性是由它的元表的 __mode 字段控制的。如果 __mode 字段是一個(gè)包含字符 `k′的字符串,那么表中的鍵是弱鍵。如果 __mode 字段是一個(gè)包含字符 `v′ 的字符串,那么表中的值是弱值。
Lua支持同步程序,也稱為 半同步程序(semi-coroutines) 或 協(xié)同多線程(collaborative multithreading)。Lua中的一個(gè)同步程序代表了一個(gè)獨(dú)立的執(zhí)行線程。然而,不像在多線程系統(tǒng)中的線程那樣,一個(gè)同步程序只有在調(diào)用了一個(gè)yield(產(chǎn)生結(jié)果)函數(shù)才能掛起它的執(zhí)行。
你可以調(diào)用 coroutine.create 來創(chuàng)建一個(gè)同步程序。它唯一的一個(gè)參數(shù)是一個(gè)函數(shù),代表同步程序的主函數(shù)。create 函數(shù)僅僅建立一個(gè)新的同步程序然后返回一個(gè)它的句柄 (一個(gè)線程 thread 類型的對(duì)象);它不會(huì)啟動(dòng)該同步程序。
當(dāng)你第一次調(diào)用 coroutine.resume,將 coroutine.create 返回的線程對(duì)象作為第一個(gè)參數(shù)傳遞給它,然后同步程序就啟動(dòng)了,從它的主函數(shù)的第一行開始。傳給 coroutine.resume 的額外的參數(shù)會(huì)作為同步程序主函數(shù)的參數(shù)傳遞過去。在同步程序開始執(zhí)行之后,它一直運(yùn)行到它結(jié)束或產(chǎn)生結(jié)果。
一個(gè)同步程序通過兩種方式結(jié)束它的運(yùn)行:正常情況下,當(dāng)它的主函數(shù)返回(顯式地或隱式的,在最后一個(gè)指令之后)時(shí)結(jié)束;異常地,如果有未保護(hù)的錯(cuò)誤。第一各情況下,coroutine.resume 返回 true,加上同步程序主函數(shù)返回的其它值。在有錯(cuò)誤的情況下,coroutine.resume 返回 false ,并附上錯(cuò)誤信息。
一個(gè)同步程序通過調(diào)用 coroutine.yield 來產(chǎn)生結(jié)果。當(dāng)一個(gè)同步程序產(chǎn)生結(jié)果,相應(yīng)的 coroutine.resume 就立刻返回,即使操作發(fā)生在嵌套函數(shù)調(diào)用中(也就是,不在主函數(shù)中,而在被主函數(shù)直接或間接調(diào)用的函數(shù)中)。在這種情況下, coroutine.resume 也返回 true,以及傳給 coroutine.yield。的所有參數(shù)。下次你繼續(xù)同一個(gè)同步程序時(shí),它會(huì)從它原來yield的地方繼續(xù)執(zhí)行,而 coroutine.yield 將返回給主程序傳給 coroutine.resume 的額外參數(shù)。
coroutine.wrap 函數(shù)創(chuàng)建一個(gè)和 coroutine.create 一樣的同步程序,但它不返回同步程序本身,而是返回一個(gè)繼續(xù)同步程序的函數(shù)(當(dāng)調(diào)用的時(shí)候)。傳遞給這個(gè)函數(shù)的參數(shù)作為繼續(xù)resume的額外參數(shù)。函數(shù)將返回resume返回的所有值,出除了第一個(gè)(布爾值的錯(cuò)誤代碼)。不像 coroutine.resume,這個(gè)函數(shù)不捕獲錯(cuò)誤;出現(xiàn)任何錯(cuò)誤都傳回給調(diào)用者。
function foo1 (a) print("foo", a) return coroutine.yield(2*a)endco = coroutine.create(function (a,b) print("co-body", a, b) local r = foo1(a+1) print("co-body", r) local r, s = coroutine.yield(a+b, a-b) print("co-body", r, s) return b, "end"end) a, b = coroutine.resume(co, 1, 10)print("main", a, b)a, b, c = coroutine.resume(co, "r")print("main", a, b, c)a, b, c = coroutine.resume(co, "x", "y")print("main", a, b, c)a, b = coroutine.resume(co, "x", "y")print("main", a, b)
co-body 1 10foo 2main true 4co-body rmain true 11 -9co-body x ymain true 10 endmain false cannot resume dead coroutine