HelloKenLee

Lua4.0中文参考手册

Posted on By KenLee

Lua4.0 参考手册 中文翻译

1 简介


(原文链接: https://www.lua.org/manual/4.0/manual.html)

Lua 是一个扩展编程语言,支持通用编程功能与数据描述功能。作为一个强大,轻量级的配置语言,它被设计用来做为别的程序的配置语言。

Lua 被实现为由 C 语言写成的库。做为一个扩展语言,Lua 没有 “main” 函数的概念;它只能嵌入到宿主运行,叫做嵌入程序。宿主可以调用函数去执行一段 Lua 代码,可以读写 Lua 变量,可以注册被 Lua 代码调用的 C 函数。通过 C 函数的使用,Lua 可以扩展自己以应对不同的领域,从而创建可定制、共享语法框架的编程语言。

Lua 是自由分发的软件,正常提供没有什么保证,如它的版权声明所述。本文档里描述的这个实现可以从下面的地址得到:

http://www.lua.org/

http://www.tecgraf.puc-rio.br/lua/

ftp://ftp.tecgraf.puc-rio.br/pub/lua/

如同其它的参考手册一样,这个文档也很枯燥。一些 Lua 设计背后的决策讨论可以参见下面的论文,它们都可以在官网上找到。

  • R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua-an extensible extension language. Software: Practice & Experience 26 #6 (1996) 635–652.

  • L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. The design and implementation of a language for extending applications. Proceedings of XXI Brazilian Seminar on Software and Hardware (1994) 273–283.

  • L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua: an extensible embedded language. Dr. Dobb’s Journal 21 #12 (Dec 1996) 26–33.


2 环境和块


Lua 中所有的语句都在一个全局环境中执行。这个环境由嵌入程序调用 lua_open 初始化并一直持续到调用 lua_close 结束,或者到嵌入程序结束时结束。如果必要的话,宿主程序员可以建立多个独立的全局环境,并可以在它们之间自由的切换(参见 5.1 节)。

这个全局环境可以用 Lua 代码或者嵌入程序来管理,可以通过 Lua 的实现库 API 函数来读写全局变量。

Lua 中的全局变量不需要声明。所有的变量被认为是全局的除非明确声明为 local (参见 4.4.6 节)。在第一次赋值之前,全局变量的值为 nil(可以修改这个默认值,参见 4.8 节)。一个表(table)保存所有的全局名字和值(表的解释参见 3 节)。

Lua 的执行单元叫做块(chunk) 。简单地说,一个块就是一个按顺序执行的语句序列。每个语句可以可选的后跟一个分号:

   chunk ::= {stat [`;']}

语句在 4.4 节描述。(上面的符号标记法是通用的扩展的 BNF,{ a } 表示 0 个或多个 a,[ a ] 表示一个可选的 a,{ a } +(加号是右上角标)表示一个或多个 a。完整的语法描述在本文档的最后面。)

一个模块可以保存在一个文件中或者一个宿主程序的字符串中。当执行一个模块,首先它被预编译为虚拟机的字节码,然后它的语句由模拟的虚拟机顺序执行。模块对于全局环境的所有修改是持久的,这些修改在模块结束后依然可见。

块可被预编译为二进制格式并保存在文件中;参见程序 luac。包含块代码的文本文件和它们的二进制预编译字节码形式是等价的。Lua 可以自动检测文件类型并做相应操作。


3 类型和标签(Types and Tags)


Lua 是动态类型语言。这意味着变量没有类型;只有值有类型。所以,Lua 语言中没有类型定义。所有值含有自己的类型。除了类型,所有值还有一个标签(tag)。

Lua 中有六种基本数据类型:nil, number, string, function, userdata, 和 table。Nil 是值为 nil 的类型,它的主要性质就是和其它值不同。Number 表示实数(双精浮点型),string 是字符串。Lua 是 8 位的,所以字符串可以包含所有的 8 位字符,包括内部的 0 (’\0’)(参见 4.1 节)。type 函数返回描述给定值的类型的字符串(参见 6.1 节)。

Lua 中函数是第一类值(first-class values)。这意味着他们可以存储在变量中,做为参数传递给其他函数或者做为结果返回。Lua 可以调用(和操纵)写在 Lua 或 C 中的函数。这两种函数可以由它们的标签区分:所有的 Lua 函数有相同的标签,所有的 C 函数也有相同的标签,并且不同于 Lua 函数的标签。tag 函数可以返回给定值的标签(参见 6.1 节)。

userdata 类型允许 Lua 变量保存任意的 C 指针(void)。它相当于 C 语言中的 void 型指针,所以在 Lua 中除赋值和相等测试操作以外,没有其它预定义的操作。但是,通过使用标签方法(tag methods),程序员可以为 userdata 值定义操作(参见 4.8 节)。

table 类型实现为关联数组,即数组不仅可以用数字索引,也可以用任意值索引(除了 nil)。因此,该类型不仅可用于表示普通数组,也可以用于表示符号表,集合,记录等。表是 Lua 中主要的数据类型。为表示一个记录,Lua 使用字段名为下标。语言通过提供 a.name 这种表示作为 a[“name”] 的语法糖。表也可以包含方法。因为函数是第一类的,表字段可以包含函数。t:f(x) 形式是 t.f(t,x) 的语法糖,它调用表 t 中的方法 f ,并把它自己做为第一个参数传入(参见 4.5.9 节)。

注意 table 是对象而非值。变量不能包含 table,只能引用它。赋值,参数传递,返回总是会操作 table 的引用,而不会对 table 进行拷贝。而且,table 必须在使用前显式创建(参见 4.5.7 节)。

每一个类型 nil, number, 和 string 有不同的标签。所有这些类型的值有同样的预定义标签。正如上面解释的一样,类型 function 的值可以有两个不同的标签,根据它们是 Lua 函数或者 C 函数。最后,类型 userdata 和 table 可以有程序员指定的可变的标签(参见 4.8 节)。tag 函数返回给定值的标签。用户标签由函数 newtag 生成。settag 函数用来改变表的标签(参见 6.1 节)。userdata 值的标签只能在 C 中设置(参见 5.7 节)。当有事件发生时,标签主要用来选择标签方法(tag methods)。标签方法是扩展 Lua 语义的主要机制(参见 4.8 节)。


4 语言


这节描述 Lua 的词法,语法和语义。


4.1 词法约定


Lua 中的标识符可以是任何字母,数字,下划线组成的字符串,且首字母不可为数字。这符合大多数语言中标识符的定义,除了字母的字义依赖于当前的区域设置:所有的在当前的区域设置中被认为是字母的字符可以被用在标识符里。下面这些是保留的关键字,不可用做标识符:

   and break do else elseif
   end for function if in
   local nil not or repeat
   return then until while

Lua 是区分大小写的语言:and 是保留字,但是 And 和 αnd (如果区域设置允许的话)是不同的,有效的标识符。作为一个约定,由下划线开始后跟大写字母的标识符(比如 _INPUT)保留作为内部变量。

以下字符串表示其它标记:

   ~= <= >= < > == = + - * /      
   ( ) { } [ ] ; , . .. ...

字符串常量可以由成对的单引号或双引号界定,并且可以包含 C 语言风格的转义序列 `\a’ (响铃), `\b’ (退格), `\f’ (换页), `\n’ (换行), `\r’ (回车), `\t’ (水平制表符), `\v’ (垂直制表符), `\’’ **(反斜杠), **`"’ **(双绰号), **`'’ **(单引号), and **`\newline’ (就是说,一个反斜杠后跟一个 newline,这会在字符串中生成一个新行)。字符串中的字符可以由它的数值来指定,通过转义序列`\ddd’,这里的 ddd 是一个三位数字。Lua 中的字符串可以包含任意的 8 位值,包含内嵌的 0 ,它可以由 `\000’ 来指定。

字符串常量也可以由成对的 [[ … ]] 界定。 这一种形式的字面量可以跨行,可以包含嵌套的 [[ … ]],并且不解释转义序列。这种形式特别适用于编写包含程序块或其他引用字符串的字符串。例如,在一个使用 ASCII 码的系统中,下面的三个常量是等价的:

1) "alo\n123\""
2) '\97lo\10\04923"'
3) [[alo
        123"]]

注释可在字符串外面的任何地方用两个连字符(–)开始,直到行尾。而且,块的第一行会被跳过如果它由 # 开始。这种机制是为了允许把 Lua 作为一种 Unix 系统中的脚本解释器(参见 8 节)。

数值常量可以由可选的小数部分,可选的指数部分写成。下面是一些有效的数值常量示例:

3 3.0 3.1416 314.16e-2 0.31416E1

4.2 强制转换


Lua 在运行时提供了一些在值之间的自动转换。在字符串上的算术运算会试图把字符串转换为数值,遵从通常的规则。相反的,当一个数值参与字符串操作时,数值会被转换为字符串,以一种合理的格式。转换格式要求一个数值转换到字符串后,能再被转换回原始的数值。所以,对于一个数值,转换并不需要生成一个好看的文本格式。为完全控制数值到字符串的转换,使用 format 函数(参见 6.2 节)。


4.3 调整


Lua 中的函数可以返回多个值。因为没有类型声明,当一个函数被调用时系统不知道函数会返回多少值,或者它需要多少个参数。所以,有时候,值列表必须在运行时调整到给定长度。如果实际值多于所需,那么多余的值会被丢掉;如果需要的值多于实际的,根据需要在列表中进行 nil 扩展。调整也发生多重赋值(参见 4.4.2 节)和函数调用(参见 4.5.8 节)中。


4.4 语句


Lua 支持几乎所有常规的语句,和 Pascal 和 C 类似。常规的命令包括:赋值,控制结构和过程调用。非常规的命令包括表的构造函数(参见 4.5.7 节),和局部变量的声明(参见 4.4.6 节)。


4.4.1 块


一个块(block)就是一个语句列表。在语法中,一个 block 等同于一个 chunk:

block ::= chunk

一个 block 也可以显式的界定:

stat ::= do block end

显式的 block 在控制局部变量的作用域(参见 4.4.6 节)时很有用。显式的 block 有时也被用于在另一个块中添加一个 return 或者 break 语句(参见 4.4.3 节)。


4.4.2 赋值


Lua 支持多重赋值。所以,语法定义了赋值的左边是一个变量列表,右边是一个表达式的列表。两个列表元素都以逗号分割。

stat ::= varlist1 `=' explist1
varlist1 ::= var {`,' var}

这个语句首先求出所有右边的值,再排列左边的变量,最后对其赋值。所以,代码:

    i = 3
    i, a[i] = 4, 20

把 a[3] 设置为 20,但是并不影响 a[4] 因为 i 在 a[i] 里被赋值为 4 之前就被求值(evaluated)了。

多重赋值可以用来交换两个变量的值,如下所示:

x, y = y, x

赋值的两边的列表可能长度不同。在赋值前,值的列表被调整到和变量列表的长度相等(参见 4.3 节)。

一个名字可以指示一个全局变量或局部变量或形式参数:

var ::= name

方括号用来索引 table:

var ::= varorfunc `[' exp1 `]'
varorfunc ::= var | functioncall

varorfunc 的结果是一个 table,由表达式 exp1 的值索引的字段获得所赋的值。

var.NAME 仅仅是 var[“NAME”] 的语法糖。

var ::= varorfunc `.' name

全局变量和下标变量的赋值和评估(evaluations )的意义可以被 tag method 修改(参见 4.8 节)。事实上,一个 x=val 的赋值,这里 x 是一个全局变量,等价于调用 setglobal(“x”, val);并且 t[i] = val 等价于 settable_event(t,i,val)。这些函数的完整描述参见 4.8 节(setglobal 在基础的库中,settable_event 仅用于解释目的)。


4.4.3 控制结构


控制结构 if, while 和 repeat 有通常意义和常见的语法

stat ::= while exp1 do block end
stat ::= repeat block until exp1
stat ::= if exp1 then block {elseif exp1 then block} [else block] end

一个控制结构的条件表达式 exp1 可以返回任何值。所有不是 nil 的值都被认为是真,只有 nil 被认为是假。

return 语句用于从函数或者块中返回值。因为函数或块可以返回多个值,return 语句的语法是:

stat ::= return [explist1]

break 语句可以被用来终结一个循环的执行,跳到循环之后的下一条语句:

stat ::= break

break 结束包含它的最内层的循环(while, repeat 或 for)。

由于语法原因,return 和 break 语句只能写在一个 block 的最后一句。如果确实需要在一个块的中间 return 或 break,可以使用一个显式的 block,如同在语句 do return end 中,因为这时 return 是内层 block 的最后一个语句。


4.4.4 For 语句


for 语句有两种格式,一种用于数值,一种用于表。数值型的 for 循环有如下语法:

stat ::= for name `=' exp1 `,' exp1 [`,' exp1] do block end

一个 for 语句如同

    for var = e1 ,e2, e3 do block end

等价于代码:

    do
      local var, _limit, _step = tonumber(e1), tonumber(e2), tonumber(e3)
      if not (var and _limit and _step) then error() end
      while (_step>0 and var<=_limit) or (_step<=0 and var>=_limit) do
        block
        var = var+_step
      end
    end

注意如下:

  • _limit 和 _step 是不可见的变量。这里的名字仅用于解释目的。
  • 如果在 block 内部对 var 赋值,行为未定义。
  • 如果没有第三个表达式(步长),则步长为 1 。
  • 循环结束(limit)和步长只被求值一次,在循环开始之前。
  • 变量 var 是对于语句来说是局部的;在 for 结束后不可以使用它的值。
  • 你可以使用 break 退出一个 for 循环。如果你需要索引的值,在退出前把它赋给另一个变量。

表的 for 循环语句遍历给定表的所有键值对(index, value)。它有如下语法:

stat ::= for name `,' name in exp1 do block end

一个 for 语句如同

    for index, value in exp do block end

等价于下面的代码

    do
      local _t = exp
      local index, value = next(t, nil)
      while index do
        block
        index, value = next(t, index)
      end
    end

注意如下:

  • _t 是不可见变量,这里的名字仅用于解释目的。
  • 如果在 block 内部对 index 赋值,行为未定义。
  • 如果在遍历时改变表 _t ,行为未定义。
  • 变量 index 和 value 是对于语句来说是局部的;在 for 结束后不可以使用它们的值。
  • 你可以使用 break 退出一个 for 循环。如果你需要 index 或 value 的值,在退出前把它们赋给另外的变量。
  • 表的元素遍历的顺序是未定义的,即使是数字索引。如果你想以数字顺序遍历索引,使用数值型 for 。

4.4.5 函数调用做为语句


由于可能的副作用(side-effects),函数调用可以作为语句执行。

stat ::= functioncall

在这种情况下,所有的返回值都被丢弃。函数调用在 4.5.8 节解释。


4.4.6 局部声明


局部变量可以在块中的任何位置声明。声明可以包含赋初始值。

stat ::= local declist [init]
declist ::= name {`,' name}
init ::= `=' explist1

如果有赋初值操作,那么他和多重赋值有同样的语义。否则,所有的变量被初始化为 nil。

一个 chunk 也是一个 block,所以局部变量可以在任何显式 block 的外部声明。

局部变量的作用域从声明的地方开始,直到 block 结束。所以,代码 local print=print 新建一个名为 print 的局部变量,该局部变量的初始值为同名全局变量的值。


4.5 表达式



4.5.1 基本表达式


Lua 中基本表达式是:

    exp ::= `(' exp `)'
    exp ::= nil
    exp ::= number
    exp ::= literal
    exp ::= var
    exp ::= upvalue
    exp ::= function
    exp ::= functioncall
    exp ::= tableconstructor

数值(数值常量)和字符串常量在 4.1 节解释过了。变量在 4.4.2 节解释过了。upvalue 在 4.6 节解释。函数定义在 4.5.9 节解释。函数调用在 4.5.8 节解释。表的构造函数在 4.5.7 节解释。

一个全局变量 x 的调用等价于调用 getglobal(“x”),一个下标变量 t[i] 等价于 gettable_event(t,i) 。这些函数的解释参见 4.8 节(getglobal 在基础的库中,gettable_event 仅用于解释目的)。

非终结的 exp1 被用来指示一个表达式的返回值必须被调整为一个值:

    exp1 ::= exp

4.5.2 算术运算符


Lua 支持常见的算术运算符。这些运算符是二元操作符 +(加), -(减), *(乘), /(除) 和 ^(幂),一元操作符 -(负)。如果操作数是数值,或者是可以转化为数值的字符串(根据 4.2 节中给出的规则),所有的操作(除了幂操作)具有通常意义。否则,一个适用的标签函数(参见 4.8 节)将会被调用。幂操作将一直调用一个标签函数。标准的数学库以预想的意义重定义了幂操作(参见 6.3 节)。


4.5.3 关系运算符


Lua 中的关系运算符是:

    == ~= < > <= >=

他们返回 nil 做为假,非 nil 做为真。

相等(==)首先比较两个操作数的标签。如果不同,结果为 nil。否则,比较它们的值。数值或字符串以常见的方式比较。表, userdata 和函数按引用比较,也就是说,两个比较的表只有是同一个的时候才被认为是相等的。不等运算 ~= 和相等运算 (==) 具有完全相反的结果。

4.2 节的转换规则不适用于相等比较。所以,”0”==0 的求值结果为 false,t[0] 和 t[“0”] 表示表中不同的元素。

顺序比较操作符是这么工作的:如果两个参数都是数值,他们就以数值比较。如果两个参数都可以转化为字符串,它们将以字典序比较。否则的话, 标签方法 “lt” 将被调用(参见 4.8 节)。


4.5.4 逻辑运算符


Lua 中的逻辑运算符是:

    and or not

和控制结构一样,所有的逻辑运算符认为 nil 为假而其它的都为真。 合取运算符 and 返回 nil 如果它的第一个参数为 nil;否则,它返回它的第二个参数。析取运算符 or 返回它的第一个参数如果它不同于 nil;否则,它返回它的第第二个参数。and 和 or 是短路求值,也就是说,第二个操作数只在需要的时候才被求值。

有两个使用逻辑运算符的习惯用法。第一个是:

    x = x or v

它等价于

    if x == nil then x = v end

该用法当 x 未设置时给 x 设置一个默认值。 第二个习惯用法是:

    x = a and b or c

它应该被读为 x = (a and b) or c. 它等价于

    if a then x = b else x = c end

条件是 b 不为 nil。


4.5.5 连接


Lua 中的字符串连接操作符由两点“..” 表示。如果两个操作数是字符串或者数字,他们按 4.2 节的规则转化为字符串。否则,标签方法 “concat” 将被调用(参见 4.8 节)。


4.5.6 优先级


运算符的优先级如下表所示,从低到高排列:

    and or
    < > <= >= ~= ==
    ..
    + -
    * /
    not - (unary)
    ^

所有的二元操作符具体左结合性,^除外,幂操作具有右结合性。预编译可能重新排列相关运算符(比如 .. 和 +)的求值顺序,只要优化不改变正常的结果。反而,这些优化可能改变一些结果,如果你为这些运算符定义了一些不相关的标签方法。


4.5.7 表的构造函数


Table 的构造函数是创建表的表达式。当对构造函数求值的时候,会生成一个新的表。构造函数可以用来新建一个空表,或者新建一个表并初始化一些字段。 构造函数的语法如下:

tableconstructor ::= `{' fieldlist `}'
fieldlist ::= lfieldlist | ffieldlist | lfieldlist `;' ffieldlist | ffieldlist `;' lfieldlist
lfieldlist ::= [lfieldlist1]
ffieldlist ::= [ffieldlist1]

lfieldlist1 被用来初始化列表。

lfieldlist1 ::= exp {`,' exp} [`,']

列表中的表达式被赋值给一个连续的数值索引,索引从 1 开始。例如:

    a = {"v1", "v2", 34}

等同于:

    do
      local temp = {}
      temp[1] = "v1"
      temp[2] = "v2"
      temp[3] = 34
      a = temp
   end

ffieldlist1 初始化表中的其它字段:

ffieldlist1 ::= ffield {`,' ffield} [`,']
ffield ::= `[' exp `]' `=' exp | name `=' exp

例如:

    a = {[f(k)] = g(y), x = 1, y = 3, [0] = b+c}

等同于:

    do
      local temp = {}
      temp[f(k)] = g(y)
      temp.x = 1 -- or temp["x"] = 1
      temp.y = 3 -- or temp["y"] = 3
      temp[0] = b+c
      a = temp
    end

事实上, {x = 1, y = 4} 这样的表达式是表达式 {["x"] = 1, ["y"] = 4} 的语法糖。

两种形式都可以有一个可选的结尾逗号,并且在同样的构造中可以用分号分割。例如,下面的形式都是对的。

    x = {;}
    x = {"a", "b",}
    x = {type="list"; "a", "b"}
    x = {f(0), f(1), f(2),; n=3,}

4.5.8 函数调用


Lua 中的函数调用有如下语法:

functioncall ::= varorfunc args

首先,varorfunc 被求值。如果它的值类型为 function,这个函数就用给定的参数被调用。 否则,标签方法 “function” 被调用,第一个参数为 varorfunc 的值,之后是原来的调用参数(参见 4.8 节)。

形如:

functioncall ::= varorfunc `:' name args

可被用来调用 “methods”。v:name(…) 调用是 v.name(v, …) 的语法糖,除了 v 只被求值一次。

参数的语法如下:

args ::= `(' [explist1] `)'
args ::= tableconstructor
args ::= literal
explist1 ::= {exp1 `,'} exp

所有参数表达式在函数调用前被求值。f{…} 调用是 f({…}) 的语法糖,即,参数列表是一个新建的 table。f’…’ (或者 f”…” 或者 f[[…]])调用是 f(‘…’) 的语法糖,即,参数列表是一个字符串常量。

因为一个函数可以返回任意多个值(参见 4.4.3 节),返回值的个数在使用之前必须进行调整(参见 4.3 节)。如果一个函数调用作为语句使用(参见 4.4.5 节),它的返回结果会被调整到 0 个,因而返回值被全部丢弃。如果一个函数在需要一个值(语法中被表示为非终结的 exp1)的地方调用,它的返回结果会被调整到 1 个,因而除了第一个返回值其它的都被丢弃。如果一个函数在需要多个值的地方调用(语法上表示为非终结的 exp),不对返回结果个数进行调整。唯一可以保持多个值的地方是赋值的最后(或唯一)一个表达式,参数列表中,或者在 return 语句中。这里是一些例子:

    f()             -- 调整到 0 个返回值
    g(f(), x)       -- f() 调整到 1 个返回值
    g(x, f())       -- g 获得 x 和 f() 的所有返回值
    a,b,c = f(), x  -- f() 调整到 1 个返回值 ( c 获得 nil )
    a,b,c = x, f()  -- f() 调整到 2 个返回值
    a,b,c = f()     -- f() 调整到 3 个返回值
    return f()      -- 返回所有的 f() 的返回值
    return x,y,f()  -- 返回 a, b, 和所有的 f() 的返回值

4.5.9 函数定义


函数定义的语法是:

function ::= function `(' [parlist1] `)' block end
stat ::= function funcname `(' [parlist1] `)' block end
funcname ::= name | name `.' name | name `:' name

语句

    function f () ... end

    f = function () ... end

的语法糖。 语句

    function v.f () ... end

    v.f = function () ... end

的语法糖。

一个函数定义是一个可执行的表达式,它的值类型为 function。当 Lua 预编译一个 chunk,它所有的函数体也都会被预编译。然后,当 Lua 执行函数定义,它的 upvalue 就是确定的了(参见 4.6 节),函数也就实例化(或者闭合)了。这个函数实例(或者闭包)就是表达式的最终值。同样函数的不同实例可能有不同的 upvalue 。

参数和局部变量的表现一样,由参数值进行初始化。

parlist1 ::= `...'
parlist1 ::= name {`,' name} [`,' `...']

当一个函数被调用时,实参的个数被调整为和形参一样(参见 4.3 节),除非函数是可变参数函数(vararg function),也就是参数列表的最后是三个点(’…‘)。一个可变参数函数不需要调整它的实参列表;而是把所有额外的实参收集到名为 arg 的隐含参数中。arg 的值是一个表,它的一个字段 n 表示额外参数的个数,并且额外的参数位于 1, 2, …, n 。

作为一个例子,考虑下面的定义:

    function f(a, b) end
    function g(a, b, ...) end
    function r() return 1,2,3 end

我们就有下面的实参和形参的对应关系:

-- CALL          PARAMETERS
   f(3)          -- a=3, b=nil
   f(3, 4)       -- a=3, b=4
   f(3, 4, 5)    -- a=3, b=4 
   f(r(), 10)    -- a=1, b=10 
   f(r())        -- a=1, b=2
   g(3)          -- a=3, b=nil, 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} 

结果由 return 语句返回(见 4.4.3节)。如果执行到函数最后也没有 return 指令的话,函数不返回值。

语法

funcname ::= name `:' name

用来定义函数,函数有一个隐含的参数 self。

语句

    function v:f (...) ... end

只是

    v.f = function (self, ...) ... end

的语法糖。 注意函数获得一个额外的名为 self 的形式参数。


4.6 可见性和 Upvalue


一个函数体可以引用它自己的局部变量(包括它的参数)和全局变量,只要它们没有被函数中同名的局部变量所隐藏(shadowed )。一个不可以使用包含它的函数的局部变量,因为这样的变量可能在函数调用的时候已经不存在了。然而,一个函数可通过 upvalue 使用包含它的函数中的局部变量。upvalue 的语法如下:

upvalue ::= `%' name

一个 upvalue 多少有点像是一个变量表达式,但是它的值是冻结的(frozen)当使用它的函数实例化时。upvalue 中使用的名字可以是任何变量的名字,只要函数定义的时候该变量是可见的,也就是说,直接包含它的函数中的全局变量和局部变量。注意,当 upvalue 是一个表时,只有表的引用(也就是 upvalue 的值)是冻结的。表的内容是可以任意修改的。使用表的值作为 upvalue 可以让函数有可写的但是私有的状态。 下面是一些例子:

    a,b,c = 1,2,3 -- global variables
    local d
    function f (x)
      local b = {} -- x and b are local to f; b shadows the global b
      local g = function (a)
        local y -- a and y are local to g
        p = a -- OK, access local `a'
        p = c -- OK, access global `c'
        p = b -- ERROR: cannot access a variable in outer scope
        p = %b -- OK, access frozen value of `b' (local to `f')
        %b = 3 -- ERROR: cannot change an upvalue
        %b.x = 3 -- OK, change the table contents
        p = %c -- OK, access frozen value of global `c'
        p = %y -- ERROR: `y' is not visible where `g' is defined
        p = %d -- ERROR: `d' is not visible where `g' is defined
      end -- g
    end -- f

4.7 错误处理


由于 Lua 是一个扩展语言,所有的 Lua 动作从宿主程序中的 C 代码调用 Lua 库中的一个函数开始。每当一个错误在 Lua 编译或执行时发生,函数 _ERRORMESSAGE 将被调用(如果它不是 nil 的话),然后相应的库中的函数 (lua_dofile, lua_dostring, lua_dobuffer 和 lua_call) 被终止,并返回一个错误状态。

内存分配错误是上面规则的一个例外。当内存分配失败,Lua 也许不能执行 _ERRORMESSAGE 函数。因此,对于这种错误,Lua 不调用 _ERRORMESSAGE 函数,而是,库中相应的函数立即带一个特别的错误码(ERRMEM)返回。这个和其它的错误码定义在 lua.h 中,参见 5.8 节。

_ERRORMESSAGE 唯一的参数是一个描述错误的字符串。这个函数的默认定义叫做 _ALERT,它打印信息到 stderr (参见 6.1 节)。标准 I/O 库重定义了 _ERRORMESSAGE 并且使用调试机制(参见 7 节)去打印一些额外的信息,比如调用堆栈回溯。

Lua 代码可能通过显式调用函数 error (参见 6.1 节)生成一个错误。Lua 代码可以使用函数 call (参见 6.1 节)捕获一个错误。


4.8 标签方法


Lua 提供一个强大的机制去扩展它的语义,叫做标签方法 (tag method)。一个标签方法是一个程序员定义的在 Lua 程序执行的特定关键点调用的函数,它允许程序员在这些关键点上改变标准的 Lua 行为。每一个这样的点叫做一个事件。

特定事件的标签方法根据事件中的所涉及值的标签被调用(参见 3 节)。函数 settagmethod 改变给定对(tag,event)关联的标签方法。它的第一个参数是标签,第二个参数是事件的名字(一个字符串,参见下面),第三个参数是新的方法(一个函数),或者 nil 用来恢复对(标签事件对)的默认行为。settagmethod 函数返回标签事件对之前的标签方法。一个于之对应的函数 gettagmethod 接收一个标签和一个事件名并返回于之关联的当前方法。

标签方法在下面的事件中被调用,由给定名字区分。标签方法的语义可以由 Lua 函数描述解释器在每个事件的行为来更好的解释。这个函数不仅展示什么时候会调用标签方法,也展示它的参数,返回值和默认的行为。这里展示的代码仅用于说明目的;解释器中真正的行为是硬编码的,并且它比这个模拟更加的高效。这些解释(rawget, tonumber, call, etc.)中使用的所有的函数在 6.1 节中描述。

  • ``add’‘: 当 + 运算被应用于非数值型的操作数时会调用到它。 下面的函数 getbinmethod 定义了 Lua 如何为一个二元运算选择一个标签方法。首先,Lua 尝试第一个操作数,如果它的标签没有为操作定义标签方法;那么 Lua 将尝试第二个操作数,如果它依然失败,那么将从标签 0 获得一个标签方法。
      function getbinmethod (op1, op2, event)
        return gettagmethod(tag(op1), event) or
               gettagmethod(tag(op2), event) or
               gettagmethod(0, event)
      end
    

    使用这个函数, ``add’’ 事件的标签方法是:

      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 tm = getbinmethod(op1, op2, "add")
          if tm then
            -- call the method with both operands and an extra
            -- argument with the event name
            return tm(op1, op2, "add")
          else -- no tag method available: default behavior
            error("unexpected type at arithmetic operation")
          end
        end
      end
    
  • ``sub’‘: 当 - 运算被应用于非数值型的操作数时会调用到它。 它的行为类似于 ``add’’ 事件。

  • ``mul’‘: 当 * 运算被应用于非数值型的操作数时会调用到它。 它的行为类似于 ``add’’ 事件。

  • ``div’’: 当 / 运算被应用于非数值型的操作数时会调用到它。 它的行为类似于 ``add’’ 事件。

  • ``pow’’:^(幂)运算调用时,即使对于数值型操作数。
      function pow_event (op1, op2)
        local tm = getbinmethod(op1, op2, "pow")
        if tm then
          -- call the method with both operands and an extra
          -- argument with the event name
          return tm(op1, op2, "pow")
        else -- no tag method available: default behavior
          error("unexpected type at arithmetic operation")
        end
      end
    
  • ``unm’‘: 当一元运算 - 被应用于非数值型的操作数时会调用到它。
      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 tag method from the operand;
          -- if it does not have one, try a "global" one (tag 0)
          local tm = gettagmethod(tag(op), "unm") or
                     gettagmethod(0, "unm")
          if tm then
            -- call the method with the operand, nil, and an extra
            -- argument with the event name
            return tm(op, nil, "unm")
          else -- no tag method available: default behavior
            error("unexpected type at arithmetic operation")
          end
        end
      end
    
  • ``lt’’: 当比较运算被应用于非数值型或非字符串型的操作数时会调用到它。它相当于 < 操作符。
      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 tm = getbinmethod(op1, op2, "lt")
          if tm then
            return tm(op1, op2, "lt")
          else
            error("unexpected type at comparison");
          end
        end
      end
    

    其它的比较运算符使用这个标签方法根据常见的等值性:

      a>b <=> b<a
      a<=b <=> not (b<a)
      a>=b <=> not (a<b)
    
  • ``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 tm = getbinmethod(op1, op2, "concat")
          if tm then
            return tm(op1, op2, "concat")
          else
            error("unexpected type for concatenation")
          end
        end
      end
    
  • ``index’’: 当 Lua 试图返回一个索引不在表中的值时会调用到它。语义参见 ``gettable’’ 事件。

  • ``getglobal’’: 当 Lua 需要一个全局变量的值时会调用到它。这个方法可以只为 nil 设置,且只为由 newtag 新建的标签设置。注意标签是全局变量的当前值。
      function getglobal (varname)
        -- access the table of globals
        local value = rawget(globals(), varname)
        local tm = gettagmethod(tag(value), "getglobal")
        if not tm then
          return value
        else
          return tm(varname, value)
        end
      end
    

    函数 getglobal 在基本库中被定义(参见 6.1 节)。

  • ``setglobal’’: 当 Lua 给一个全局变量赋值时会调用到它。对于数值,字符串,表和有默认标签的 userdata 不可以设置这个方法。
      function setglobal (varname, newvalue)
        local oldvalue = rawget(globals(), varname)
        local tm = gettagmethod(tag(oldvalue), "setglobal")
        if not tm then
          rawset(globals(), varname, newvalue)
        else
          tm(varname, oldvalue, newvalue)
        end
      end
    

    函数 setglobal 在基本库中被定义(参见 6.1 节)。

  • ``gettable’’: 当 Lua 调用一个索引变量时会调用到它。对于有默认标签的表不可以设置这个方法。
      function gettable_event (table, index)
        local tm = gettagmethod(tag(table), "gettable")
        if tm then
          return tm(table, index)
        elseif type(table) ~= "table" then
          error("indexed expression not a table");
        else
          local v = rawget(table, index)
          tm = gettagmethod(tag(table), "index")
          if v == nil and tm then
            return tm(table, index)
          else
            return v
          end
        end
      end
    
  • ``settable’’: 当 Lua 设置一个索引变量时会调用到它。对于有默认标签的表不可以设置这个方法。
      function settable_event (table, index, value)
        local tm = gettagmethod(tag(table), "settable")
        if tm then
          tm(table, index, value)
        elseif type(table) ~= "table" then
          error("indexed expression not a table")
        else
          rawset(table, index, value)
        end
      end
    
  • ``function’’: 当 Lua 试图调用一个不是函数的值时会调用到它。
      function function_event (func, ...)
        if type(func) == "function" then
          return call(func, arg)
        else
          local tm = gettagmethod(tag(func), "function")
          if tm then
            for i=arg.n,1,-1 do
              arg[i+1] = arg[i]
            end
            arg.n = arg.n+1
            arg[1] = func
            return call(tm, arg)
          else
            error("call expression not a function")
          end
        end
      end
    
  • ``gc’’: 当 Lua 垃圾回收一个 userdata 时会调用到它。这个标签方法只可以在 C 中设置,并且不可以设置给有默认标签的 userdata。对于每个被垃圾回收的 userdata, Lua 作和下面函数等价的操作:
      function gc_event (obj)
        local tm = gettagmethod(tag(obj), "gc")
        if tm then
          tm(obj)
        end
      end
    

    在一个垃圾回收周期中,userdata 的标签方法被以标签创建的逆序调用,也就是说,被调用的第一个标签方法是关联于程序中创建的最后一个标签。而且,在周期结束时,Lua 做等价于 gc_event(nil) 调用的事情。


5 API


这节描述 Lua 的 API, 也就是宿主程序和 Lua 交互的一组 C 函数。所有的 API 函数和相关联的类型及常量都在文件 lua.h 中声明。

所有的 API 提供的功能都可以由宏来替代,即使当我们使用术语函数(``function’‘)。因为这样的宏只使用它的参数一次,所以不会产生隐蔽的副作用。


5.1 状态(State)


Lua 库是完全可重入的:它没有使用任何全局变量。Lua 解释器的完整状态(全局变量,栈,标签方法,等)保存在一个动态分配的 lua_State 类型中。这个状态必须作为第一个参数传递给库中的每一个函数(除了下面说的 lua_open)。

在调用任何 API 函数之前,你必须新建一个状态,通过调用

    lua_State *lua_open (int stacksize);

这个函数唯一的参数是解释器的栈尺寸。(每个函数调用的每个参数需要一个栈位置,局部变量,临时值,再加一个用做标记的位置。栈必须有额外的 20 个可用位置。对于很小的实现,没有递归函数,栈的尺寸为 100 可能就足够了。)如果 stacksize 是 0 ,那么默认的尺寸 1024 会被使用。

为释放一个由 lua_open 创建的状态,调用

    void lua_close (lua_State *L);

这个函数销毁给定的 Lua 环境中的所有的对象(调用相应的垃圾回收标签方法,如果有的话)并且释放所有的这个状态使用的动态内存。通常,你不必调用这个函数,因为当你的程序结束时所有的资源会被自然释放。另一方面,长时间运行的程序,如守护程序或一个 web 服务器程序,可能需要在状态不需要的时候释放它,以避免内存增长过大。

除了 lua_open,所有的 Lua API 中的函数都需要一个状态作为它们的第一个参数。


5.2 栈和索引


Lua 用一个栈和 C 之间传递数据。栈中的每一个元素代表一个 Lua 值(nil,数值,字符串,等)。 为了方便起见,大多数 API 中的 query 操作并不需严格遵守栈的使用规则。反而,它们可以通过使用一个索引来引用任何栈中的元素。一个正数索引代表一个绝对的栈位置(始于 1 ,不像 C 中从 0 开始)。一个负数索引代表一个从栈顶开始的偏移量。更具体地说,如果栈有 n 个元素,索引 1 代表第一个元素(即,第一个压栈的元素),索引 n 代表最后一个元素;索引 -1 同样代表最后一个元素(即,栈顶的元素),索引 -n 代表第一个元素。我们说一个索引是有效的如果它位于 1 到栈顶之间(即,如果 1 <= abs(index) <= top)。 在任何时间,你可以获得栈顶元素的索引通过调用

    int lua_gettop (lua_State *L);

因为索引开始于 1,lua_gettop 的结果等于栈中元素的个数(所以 0 代表一个空栈)。

当你和 Lua API 交互的时候,你有责任去控制栈的溢出。 函数

    int lua_stackspace (lua_State *L);

返回栈中依然可用的位置个数。每当 Lua 调用 C,它保证最少有 LUA_MINSTACK 个位置可用。LUA_MINSTACK 在 lua.h 中定义,最小为 16,所以你需要担心栈的空间仅当你的代码中有循环压栈的时候。

大部分 query 函数接受任何位于可用栈空间中的值为索引。这样的索引被称为可接受索引。更正式的讲,我们可以定义一个可接受的索引如下

    (index < 0 && abs(index) <= top) || (index > 0 && index <= top + stackspace)

注意 0 不是一个可接受的索引。


5.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);

lua_settop 接受任何可接受的索引,或者 0,并且设置栈顶为该索引。如果新的栈顶比老的大,那么新的元素被填充为 nil。如果索引为 0,那么移除所有栈的元素。一个有用 API 中的宏定义是

    #define lua_pop(L,n) lua_settop(L, -(n)-1)

它把栈顶的 n 个元素出栈。

lua_pushvalue 压栈给定索引处的元素的拷贝。lua_remove 移除给定位置的元素,下移所有的该位置以上的元素去填充空白。lua_insert 把栈顶的元素移动到给定的位置,并把给定位置之上的位置上移以开辟空间。这些函数只接受有效的索引。作为一个例子,如果栈开始时是 10 20 30 40 50 (从栈底到栈顶),那么:

    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_settop(L, -3)    --> 30 10 20
    lua_settop(L, 6)     --> 30 10 20 nil nil nil

5.4 查询栈


为了检查一个栈元素的类型,可以使用下面的函数:

    int lua_type (lua_State *L, int index);
    int lua_tag (lua_State *L, int index);
    int lua_isnil (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);

这些函数可以使用任何可接受的索引调用。

lua_type 根据给定对象的类型返回下面的常量之一:LUA_TNIL, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA。如果索引是无效的(即,如果栈的位置是空),lua_type 返回 LUA_TNONE。这个常量可以转换成字符串用:

    const char *lua_typename (lua_State *L, int t);

这里 t 是一个 lua_type 返回的类型。lua_typename 返回的字符串是:”nil”, “number”, “string”, “table”, “function”, “userdata”, 和 “no value”。 lua_tag 返回值的标签,或者 LUA_NOTAG 当索引无效时。

函数 lua_is* 返回 1 如果对象的类型的给定的类型兼容的话,否则 0 。对于无效的索引它们总是返回 0 。lua_isnumber 接受数值和数值的字符串, lua_isstring 接受字符串和数字(参见 4.2 节),lua_isfunction 接受 Lua 函数和 C 函数。为了区分 Lua 函数和 C 函数,你应该使用 lua_iscfunction。为了区分数值和数值的字符串,你可以使用 lua_type。 API 也有函数用来比较栈上的两个值:

    int lua_equal (lua_State *L, int index1, int index2);
    int lua_lessthan (lua_State *L, int index1, int index2);

这些函数和它们在 Lua 中相对应的函数是等价的。特别地,lua_lessthan 等价于 lt_event 描述位于 4.8 节。两个函数都返回 0 如果任意一个索引是无效的话。

为把栈上的一个值转换为一个指定的 C 类型,你可以使用下面的转换函数:

    double        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_tonumber 把给定索引处的值转换为一个浮点数字。这个值必须是一个数值或者是可以转换为数值的字符串(参见 4.2 节);否则,返回 0。

lua_tostring 把一个 Lua 值转换为一个字符串(const char*).这个值必须是一个字符串或者一个数值;否则,函数返回 NULL。这个函数返回一个指向 Lua 环境中的字符串的指针。这些字符串总是有一个 0 (’\0’)在最后一个字符的后面(像 C 中那样),但是可能包含其它的 0 在它内部。如果你不知道一个字符串是否包含 0,你可以使用 lua_strlen 去取得它的实际长度。因为 Lua 有垃圾回收,没有保证由 lua_tostring 返回的指针是有效的在各自的值在从栈上移除之后。

lua_tocfunction 把栈上的值转换为 C 函数。这个值必须是一个 C 函数;否则,lua_tocfunction 返回 NULL。类型 lua_CFunction 在 5.13 节解释。

lua_touserdata 把一个值转换为 void*。这个值的必须有类型 userdata;否则,返回 NULL。


5.5 压栈


API 有下面的函数用来把 C 的值压栈:

    void lua_pushnumber (lua_State *L, double n);
    void lua_pushlstring (lua_State *L, const char *s, size_t len);
    void lua_pushstring (lua_State *L, const char *s);
    void lua_pushusertag (lua_State *L, void *u, int tag);
    void lua_pushnil (lua_State *L);
    void lua_pushcfunction (lua_State *L, lua_CFunction f);

函数接受一个 C 值,把它转换为相应的 Lua 值,并且把结果压栈。特别地,lua_pushlstring 和 lua_pushstring 生成一个给定字符串的内部拷贝。lua_pushstring 只能用于压栈合适的 C 字符串(即,字符串由 0 结尾且内容没有 0 );否则你应该使用更通用的 lua_pushlstring,它接受一个明确指定的尺寸。


5.6 垃圾回收


Lua 用两个数值来控制它的垃圾回收。一个数值计算 Lua 使用的多少字节的动态内存,另一个是一个临界值。(这个 Lua 保持的内部的字节计数器并不完全准确;他只是一个下限,通常有 10% 的是正确的值[这半句不知道如何译])当字节值达到临界值,Lua 执行一次垃圾回收,它回收所有的无用的对象的内存(即,Lua 中访问不到的对象)。字节计数器被校正,并且临界值被重置为字节计数器的 2 倍。 你可以使用这两个值的当前值通过使用下面的函数:

    int lua_getgccount (lua_State *L);
    int lua_getgcthreshold (lua_State *L);

都分别返回它们的值,单元为千字节。你可以改变临界值用

    void lua_setgcthreshold (lua_State *L, int newthreshold);

再次的,给定 newthreshold 值的单元为千字节。当你调用这个函数时,Lua 设置新的临界值并再次检查它和字节计数器。如果临界值小于字节计数器,Lua 将立刻执行垃圾回收;垃圾回收之后,一个新的临界值会被设置根据前面的规则。

如果你想改变垃圾回收的这种可适应行为,你可以用 nil 使用垃圾回收的标签方法并设置你自己的临界值(标签方法会在 Lua 重置临界值之后调用)。


5.7 用户自定义数据和标签


因为 userdata 是对象,函数 lua_pushusertag 可能新建一个 userdata。如果 Lua 有一个给定值(void*)和标签的 userdata,那么 userdata 被压栈。否则,一个新的 userdata 被创建,有给定的值和标签。如果这个函数通过标签 LUA_ANYTAG 来调用,那么 Lua 将试图去寻找任何有给定值的 userdata,不管它的标签。如果没有 userdata 有那个值,那么一个新的被创建,标签为 0 。 userdata 可以有不同的标签,它的语义只有宿主程序知道。标签由下面的函数创建:

    int lua_newtag (lua_State *L);

函数 lua_settag 改变栈顶对象的标签(并不弹出它):

    void lua_settag (lua_State *L, int tag);

对象必须是一个 userdata 或者是一个表;给定的标签必须是由 lua_newtag 创建的值。


5.8 执行 Lua 代码


一个宿主程序可以执行写在文件中或在字符串中的 Lua 块,使用下面的函数:

    int lua_dofile (lua_State *L, const char *filename);
    int lua_dostring (lua_State *L, const char *string);
    int lua_dobuffer (lua_State *L, const char *buff,
                      size_t size, const char *name);

这些函数返回 0 表示成功,或者失败时返回一个错误码:

  • LUA_ERRRUN – 执行模块时出错。
  • LUA_ERRSYNTAX – 预编译时语法错误。
  • LUA_ERRMEM – 内存分配错误。对于此类错误,Lua 不会调用 _ERRORMESSAGE (参见 4.7 节)。
  • LUA_ERRERR – 执行 _ERRORMESSAGE 时出错。对于此类错误,Lua 不会再次调用 _ERRORMESSAGE ,以免死循环。
  • LUA_ERRFILE – 打开文件错误(只针对 lua_dofile)。在这种情况下,你可能想要检查 errno,调用 strerror,或者调用 perror 去告诉用户哪里出了问题。

这些常量定义在 lua.h 中。

当通过 NULL 参数调用时,lua_dofile 执行标准输入流。lua_dofile 和 lua_dobuffer 都可以执行预编译的块。它们自动检测出块是文本或者二进制,并相应地加载它(参见程序 luac)。lua_dostring 只执行文本格式的源代码。

lua_dobuffer 的第三个参数是块的名字,它用在错误信息和调试信息中。如果名字是 NULL,Lua 会给块一个默认的名字。

这些函数把块最终返回的结果压栈。一个块可以返回任意数量的值;Lua 保证这些值可以存放到栈空间中,但是调用之后就需要你自己维护栈空间了。如果你需要压栈其它的元素在调用这些函数之后,并且你想安全地使用栈空间,你必须要么通过 lua_stackspace 来检查栈空间,要求从栈同移除元素(如果你不需要它们的话)。例如,下面的代码加载一个文件中的块并且丢弃所有的这个块返回的结果,把栈恢复为调用之间的状态:

   {
    int oldtop = lua_gettop(L);
    lua_dofile(L, filename);
    lua_settop(L, oldtop);
   }

5.9 操纵 Lua 中的全局变量


可以使用下面的函数读取 Lua 全局变量的值:

    void lua_getglobal (lua_State *L, const char *varname);

它将会把给定变量的值压栈。在 Lua 中,这个函数可能触发一个标签方法 ``getglobal’‘(参见 4.8 节)。为了读取全局变量真正的值,而不唤起任何标签方法,在全局表上使用 lua_rawget(参见下面)。

保存一个值到全局变量,调用

    void lua_setglobal (lua_State *L, const char *varname);

它出栈需要保存到给定变量的值。在 Lua 中,这个函数可能会触发一个标签方法 ``setglobal’’ (参见 4.8 节)。为了设置全局变量的真正的值,而不唤起任何标签方法,在全局表上使用 lua_rawset (参见下面)。

所有的全局变量都保存在一个普通的表中。你可以获得这个表通过调用

    void lua_getglobals (lua_State *L);

它把当前的全局变量的表压栈。为设置其它的表作为全局变量的表,调用

    void lua_setglobals (lua_State *L);

将要使用的表出栈。


5.10 操纵 Lua 中的表


Lua 表也可以通过 API 来操作。 为读取表中的值,表必须在栈的某个地方。在这种情况下,调用

    void lua_gettable (lua_State *L, int index);

这里 index 指定表。lua_gettable 出栈一个 key,并且返回(在栈上)表在 key 处的内容。在 Lua 中,这个操作可能会触发一个标签方法 ``gettable’’ 。为了获得表的 key 的真正值,而不唤起任何标签方法,使用 raw 版本的:

    void lua_rawget (lua_State *L, int index);

为保存一个值到表中,表已经在栈的某个地方了,你压栈 key 和值(以这种顺序),然后调用

    void lua_settable (lua_State *L, int index);

这里的 index 指定表。lua_settable 出栈 key 和 value。在 Lua 中,这个操作可能会触发一个标签方法 ``settable’’ 。为了设置任意表的 index 的真正值,而不唤起任何标签方法,使用 raw 版本的:

    void lua_rawset (lua_State *L, int index);

最后,函数

    void lua_newtable (lua_State *L);

创建一个新的空表并压栈。


5.11 把表作为数组使用


有 API 函数可以使用 Lua 表作为数组,也就是说,表的索引都有数值:

    void lua_rawgeti (lua_State *L, int index, int n);
    void lua_rawseti (lua_State *L, int index, int n);
    int  lua_getn (lua_State *L, int index);

lua_rawgeti 获得在栈的 index 处表的第 n 个元素。

lua_rawseti 设置在栈的 index 处表的第 n 个元素为栈顶的值。

lua_getn 返回在栈 index 处表的元素个数。这个数值是表中字段 n 的值,如果它有一个数值型值,或者表中最大的数值型索引不是一个 nil 值。


5.12 调用 Lua 函数


定义在 Lua 中的函数(或者注册到 Lua 的 C 函数)可以从宿主程序调用。这采用如下协议:首先,被调用的函数压栈;然后,函数的参数以直接顺序压栈(参见 5.5 节),即,第一个参数先压栈。最后,函数被调用通过:

    int lua_call (lua_State *L, int nargs, int nresults);

这个函数返回和 lua_dostring (参见 5.8 节)同样的错误码。如果你想传播这个错误,而不是返回一个错误码,使用

    void lua_rawcall (lua_State *L, int nargs, int nresults);

在这两个函数中,nargs 是你压栈的参数的个数。所有的参数和函数值都从栈中弹出,函数的返回值压栈。函数的返回值被调整(参见 4.3 节)为 nresults,除非 nresults 是 LUA_MULTRET。在这种情况下,所有的函数返回值都压栈。函数的返回值以直接顺序压栈(第一个返回值最先压栈),所以调用之后最后一个返回值在栈顶。 下面的例子显示宿主程序如何做到和 Lua 代码等价的:

    a,b = f("how", t.x, 4)

在 C 中:

    lua_getglobal(L, "t");    /* global `t' (for later use) */
    lua_getglobal(L, "f");    /* function to be called */
    lua_pushstring(L, "how"); /* 1st argument */
    lua_pushstring(L, "x");   /* push the string `x' */
    lua_gettable(L, -4);      /* push result of t.x (2nd arg) */
    lua_pushnumber(L, 4);     /* 3rd argument */
    lua_call(L, 3, 2);        /* call function with 3 arguments and 2 results */
    lua_setglobal(L, "b");    /* set global variable `b' */
    lua_setglobal(L, "a");    /* set global variable `a' */
    lua_pop(L, 1);            /* remove `t' from the stack */

注意,上面的代码是平衡的:在它结束时,栈被回退为它的原来配置。这被认为是好的编译实践。

一些特别的 Lua 函数有它们自己的 C 接口。宿主程序可以生成一个 Lua 错误通过调用函数

    void lua_error (lua_State *L, const char *message);

这个函数永远不返回。如果 lua_error 被从 Lua 调用的 C 函数中调用,相应的 Lua 执行被终结,就如同 Lua 代码中发生了一个错误。否则,整个宿主程序被一个 exit (EXIT_FAILURE)调用终止。在终结执行之前,message 被传递给错误处理函数 _ERRORMESSAGE (参见 4.7 节)。如果 message 为 NULL, _ERRORMESSAGE 便不会被调用。

标签方法可以通过下面的函数改变

    void lua_settagmethod (lua_State *L, int tag, const char *event);

第二个参数是标签,第三个参数是事件名字(参见 4.8 节);新的方法从栈上弹出。为获得当前的 标签方法的值,使用函数

    void lua_gettagmethod (lua_State *L, int tag, const char *event);

可以拷贝所有的标签方法从一个标签到另一个:

    int lua_copytagmethods (lua_State *L, int tagto, int tagfrom);

这个函数返回 tagto。 你可以遍历一个表通过函数

    int lua_next (lua_State *L, int index);

这里 index 指定被遍历的表。这个函数从栈中弹出一个 key,并从表中压栈一个 key-value 对(也就是给定 key 的下一个键值对)。如果没有元素了,函数返回 0 (不压栈)。一个典型的调用如下:

    /* 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 `index' for next iteration */
    }

函数

    void lua_concat (lua_State *L, int n);

连接栈顶的 n 个值,弹出它们,并且压栈结果;n 最小为 2 。连接操作遵守 Lua 通用的语义(参见 4.5.5 节)。


5.13 定义 C 函数


注册 C 函数到 Lua ,用下面的宏:

    #define lua_register(L, n, f) (lua_pushcfunction(L, f), lua_setglobal(L, n))
    /* const char *n; */
    /* lua_CFunction f; */

它接受函数在 Lua 中的名字,和一个函数指针。这个指针的类型必须为 lua_CFunction,其定义为:

    typedef int (*lua_CFunction) (lua_State *L);

也就是一个 Lua 环境参数一个整型返回值的函数指针。

为了和 Lua 正确的交互,C 函数必须遵守下面的协议,这个协议规定了参数和返回值传递的方法:C 函数在栈里得到它从 Lua 获得的参数,以直接顺序(第一个参数最先压栈)。为了返回值到 Lua, C 函数可以把返回值顺序压栈(第一个返回值最先压栈),并且返回结果的个数。就像 Lua 函数一样,一个由 Lua 调用的 C 函数也可以返回多个值。

作为一个例子,下面的函数接受一个可变个数的数值型参数并且返回它们的平均数和总数:

    static int foo (lua_State *L) {
      int n = lua_gettop(L); /* number of arguments */
      double sum = 0;
      int i;
      for (i = 1; i <= n; i++) {
        if (!lua_isnumber(L, i))
          lua_error(L, "incorrect argument to function `average'");
        sum += lua_tonumber(L, i);
      }
      lua_pushnumber(L, sum/n); /* first result */
      lua_pushnumber(L, sum); /* second result */
      return 2; /* number of results */
    }

函数可以作为 ‘average’ 注册到 Lua ,通过调用

    lua_register(L, "average", foo);

当新建一个 C 函数时,可以为它关联一些 upvalue (参见 4.6 节),从而新建一个 C 闭包,当函数被调用时这些值被传递给它,作为普通的参数。为了为了一个 C 函数关联 upvalue,首先这些值应该压栈。然后函数

    void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);

被用来压栈 C 函数,参数 n 说明应该有多少个 upvalue 被关联到函数(这些 upvalue 从栈上弹出);事实上,宏 pushcfunction 被定义为参数 n 为 0 的 lua_pushcclosure 。然后,无论何时 C 函数被调用时,这些 upvalue 被插入函数作为最后的参数,在函数调用的实际参数之后。这使得程序容易获得 upvalue 而不必知道函数接受多少个参数(回忆一下,Lua 中的函数可以接受任意数量的参数):第 i 个 upvalue 在栈里的索引为 i-(n+1),这里 n 为 upvalue 的个数。 更多的 C 函数和闭包的例子,参见官方发布版中的文件 lbaselib.c, liolib.c, lmathlib.c, 和 lstrlib.c 。


5.14 Lua 对象的引用


如果一个 C 代码需要在一个 C 函数的生存期之外保持一个 Lua 值的话, 它必须新建那个值的引用(reference)。下面的函数可以管理这种引用:

    int  lua_ref (lua_State *L, int lock);
    int  lua_getref (lua_State *L, int ref);
    void lua_unref (lua_State *L, int ref);

lua_ref 从栈顶弹出一个值,新建一个它的引用,并返回这个引用。一个 nil 值的引用永远为 LUA_REFNIL。(lua.h 也定义了一个不同于其它的有效引用的常量 LUA_NOREF 。)如果 lock 不是 0 的话,对象就被锁定:这意味着这个对象将不会被垃圾回收。没有锁定的引用可被垃圾回收。

当 C 中需要引用对象的时候,调用 lua_getref 把那个对象压栈;如果对象已经被垃圾回收了,lua_getref 返回 0 (不压栈任何东西)。

当一个引用不需要了,应该使用 lua_unref 调用来释放它。

注册 当 Lua 启动时,它在位置 LUA_REFREGISTRY 注意一个表。它可以通过宏来引用

  #define lua_getregistry(L) lua_getref(L, LUA_REFREGISTRY)

这个表可以被 C 库作为一个注册机制使用。任何 C 库可以在这个表中保存数据,只要它使用的 key 不同于其它的库。


6 标准库


标准库提供了一些有用的函数,这些函数直接由标准 API 实现。因此,它们并非语言必需的部分,并且作为单独的 C 模块被提供。目前,Lua 有以下标准库:

  • 基本库;
  • 字符串处理;
  • 数学函数(sin, log, 等)
  • 输入输出(和一些系统功能)

为了能使用这些库,宿主程序必须调用函数 lua_baselibopen, lua_strlibopen, lua_mathlibopen, 和 lua_iolibopen, 这几个都在 lualib.h 中声明。


6.1 基本库


基本库提供了一些核心功能。因此,如果在你的应该程序中不包含这些库的话,你应该小心地检查你是否需要为一些功能提供一些替代的实现。(例如,没有函数 _ERRORMESSAGE,Lua 就不能显示错误信息。)

_ALERT (message)

在标准错误输出 stderr 上打印它唯一的字符串参数。所有的 Lua 中的错误信息由保存于全局变量(参见 4.7 节)_ALERT 中的函数打印。因此,一个程序可以给这个变量赋另一个函数来改变这样的信息的显示方式(例如,对于没有 stderr 的系统)。

assert (v [, message])

产生一个 ``assertion failed!’’ 错误当它的参数 v 是 nil 时。这个函数和下面的 Lua 函数等价:

    function assert (v, m)
      if not v then
        m = m or ""
        error("assertion failed! " .. m)
      end
    end

call (func, arg [, mode [, errhandler]])

使用由表 arg 给定的参数调用函数 func 。这个调用和下面的等价

    func(arg[1], arg[2], ..., arg[n])

这里 n 是 getn(arg) 的结果(参见 6.1 节)。所有的 func 的结果简单的由 call 返回。

默认情况下,在一个错误产生于调用 func 时,这个错误会被传播出去。如果字符串 mode 包含 “x”,那么这个调用就是受保护的。在这种模式下,函数 call 不会传播错误,不管调用期间发生了什么。反而,它返回 nil 来标志错误(而且调用适当的错误处理)。

如果 errhandler 提供了,错误函数 _ERRORMESSAGE 临时设置给 errhandler ,当 func 执行时。特别地,如果 errhandler 为 nil,在执行被调用函数期间不会有错误信息被分发。

collectgarbage ([limit])

设置垃圾收集的临界值为给定的 limit(单位为千字节),并对比检查它和字节计数器。如果新的临界值比字节计数器小,那么 Lua 会立刻执行垃圾回收(参见 5.6 节)。如果省略 limit ,它的默认为 0 (因此强制执行一次垃圾回收)。

copytagmethods (tagto, tagfrom)

拷贝所有的标签方法从一个标签到另一个,返回 tagto。

dofile (filename)

接受一个文件名字,打开命名文件,并执行它的内容做为一个 Lua 块,或者做为预编译的块。当无参调用它时,它执行标准输入(stdin)的内容。如果执行文件时出现了任何错误,dofile 返回 nil。否则,它返回块返回的结果,或者一个非 nil 值如果块没有返回值。它会产生一个错误如果用一个非字符串参数调用它。

dostring (string [, chunkname])

执行一个给定的字符串做为一个 Lua 块。如果执行字符串时出现了任何错误,dostring 返回 nil。否则,它返回块返回的结果,或者一个非 nil 值如果块没有返回值。可选的参数 chunkname 是 ``name of the chunk’‘,被用在错误信息和调试信息中。

error (message)

调用错误处理(参见 4.7 节)并且终止最后一个受保护函数的调用(在 C 里是:lua_dofile, lua_dostring, lua_dobuffer, 或者 lua_callfunction; 在 Lua 里是:dofile, dostring, 或者 call 在保护模式里)。如果 message 是 nil,错误处理不会被调用。函数 error 从不返回。

foreach (table, func)

在表的所有元素上执行给定的 func。对于每一个元素,函数以参数 index 和 value 做为参数被调用。如果函数返回非 nil 的值,那么循环退出,这个值就就作为 foreach 最终的结果。这个函数可以被定义为 Lua:

    function foreach (t, f)
      for i, v in t do
        local res = f(i, v)
        if res then return res end
      end
    end

foreach 的行为是未定义的如果你在遍历过程中改变了表 t 。

foreachi (table, func)

在表的所有数值索引上执行给定的 func。对于每一个索引,函数以参数 index 和 value 做为参数被调用。索引按顺序被访问,从 1 到 n,这里 n 是 getn(table) 的结果(参见 6.1 节)。如果函数返回非 nil 的值,那么循环退出,这个值就就作为 foreachi 最终的结果。这个函数可以被定义为 Lua:

    function foreachi (t, f)
      for i=1,getn(t) do
        local res = f(i, t[i])
        if res then return res end
      end
    end
getglobal (name)

返回全局变量的值,或者调用一个``getglobal’‘标签方法。它的完整的语义在 4.8 描述。字符串 name 不需要是一个合乎语法的有效的变量名。

getn (table)

返回表的尺寸,当 table 被看作 list 的时候。如果表有一个 n 字段且值是一个数值型,这个值就是表的尺寸。否则,尺寸就是表中值不是 nil 的最大的索引。这个函数可以被定义为 Lua:

    function getn (t)
      if type(t.n) == "number" then return t.n end
      local max = 0
      for i, _ in t do
        if type(i) == "number" and i>max then max=i end
      end
      return max
    end
gettagmethod (tag, event)

返回一个给定对(tag, event 对)当前的标签方法。这个函数不可以被用来获得 ``gc’’ 事件的标签方法。(这样的标签方法只可以由 C 代码来操纵。)

globals ([table])

返回当前的全局表。如果参数 table 给定了,它也把当前的全局表设置为这个参数。

newtag ()

返回一个新的标签。

next (table, [index])

允许一个程序遍历一个表的所有字段。它的第一个参数是一个表,第二个参数是表中的索引。它返回表的下一个索引和索引所对应的值。当用 nil 做为第二个参数调用它时,函数返回表的第一个健值对。当用最后一个索引调用,或者用 nil 调用一个空表,均返回 nil。如果没有第二个参数,它被解释为 nil 。 Lua 中没有字段的声明;在语义上,表中一个字段不存在和字段的值为 nil 没有区别。所以,该函数只考虑非 nil 的字段。索引的枚举顺序没有规定,就算是数字索引的也没有规定(为遍历一个表以数字顺序,使用一个数值型 for 或者函数 foreachi)。 next 的行为是未定义的如果你在遍历过程中改变了表。

print (e1, e2, ...)

可以接受任意数量的参数,以 tostring 返回的字符串打印它们的值。这个函数不是为了格式化输出,只是为了以一种快速的方法显示一个值,例如打印一个调试信息。参见 6.4 节一个格式化输出函数。

rawget (table, index)

返回 table[index] 的真实值,避免唤起任何标签方法。table 必须是一个表,index 是一个不同于 nil 的值。

rawset (table, index, value)

以 value 设置 table[index] 的真实值,避免唤起任何标签方法。table 必须是一个表,index 是一个不同于 nil 的值,value 可以是任何 Lua 值。

setglobal (name, value)

设置具名全局变量为给定值,或者调用一个 ``setglobal’’ 的标签方法。它的完整的语义描述在 4.8 节。字符串 name 不需要是一个合乎语法的有效名字。

settag (t, tag)

设置给定表(参见 3 节)的标签。tag 必须是一个收 newtag 生成的值(参见 6.1 节)。settag 返回它的第一个参数的值(表)。为了宿主程序的安全性,在 Lua 中改变一个 userdata 的标签是不可能的。

settagmethod (tag, event, newmethod)

为新定的对(tag, event 对)设置一个新的标签方法并返回老的方法。如果 newmethod 为 nil,那么 settagmethod 恢复给定事件的默认行为。这个函数不可以用来为 ``gc’’ 事件设置标签方法。(这样的标签方法只能有 C 代码来操纵。)

sort (table [, comp])

以给定顺序排序表中的元素,就地排序,从 table[1] 到 table[n],这里 n 是 getn(table) 的结果(参见 6.1 节)。如果给定 comp,它就必须是一个函数可以接受两个表的元素,且返回 true (即,一个不同于 nil 的值)当第一个小于第二个的时候(所以 not comp(a[i+1], a[i]) 将为 true 在排序之后)。如果 comp 没有给定,那么标准的 Lua 运算符 < 会被使用。 sort 算法不稳定(即,元素由给定的顺序被认为是相等的可能会被 sort 改变它们的相对位置)。

tag (v)

允许 Lua 程序测试一个值的标签(参见 3 节)。它接受一个参数,返回它的标签(一个数字)。

tonumber (e [, base])

尝试把它的参数转换为一个数字。如果参数已经是一个数字或者是一个可以转换为数字的字符串,它返回那个数字;否则,返回 nil。 一个可选的参数指定解释数字时的 base 。base 可以是 2 到 36 之间的任意整数(含 2 和 36)。如果 base 大于 10,字母 A' (大小写均可)代表 10 ,B’ 代表 11,以此类推,`Z’ 代表 35 。在 base 为 10 (默认的)时,数字可以有一个小数部分,以及一个可选的指数部分(参见 4.2 节)。在其它的 base 的情况下,只可接受无符号整型。 tostring (e) 函数可以接受一个任何类型的值作为参数,并以一个合理的格式把它转化为字符串。可以使用函数 format 来完全控制数字如何转换。

tinsert (table [, pos] , value)

在 table 的位置 pos 插入元素,如果需要的话,移动其它的元素来开辟空间。pos 的默认值是 n+1,这里 n 是 getn(table)(参见 6.1 节)的结果,所以一个调用 tinsert(t,x) 会在表 t 尾部插入 x 。这个函数也会设置或者增加表的字段 n 为 n+1。这个函数和下面的 Lua 函数等价,除了使用表时全部为 raw (即,不唤起标签方法):

    function tinsert (t, ...)
      local pos, value
      local n = getn(t)
      if arg.n == 1 then
        pos, value = n+1, arg[1]
      else
        pos, value = arg[1], arg[2]
      end
      t.n = n+1;
      for i=n,pos,-1 do
        t[i+1] = t[i]
      end
      t[pos] = value
    end
tremove (table [, pos])

在 table 的位置 pos 移除元素,如果需要的话,移动其它的元素来整合空间。返回移除元素的值。pos 的默认值是 n,这里 n 是 getn(table)(参见 6.1 节)的结果,所以一个调用 tremove(t) 会移除表 t 尾部元素 。这个函数也会设置或者减少表的字段 n 为 n-1。 这个函数和下面的 Lua 函数等价,除了使用表时全部为 raw (即,不唤起标签方法):

    function tremove (t, pos)
      local n = getn(t)
      if n<=0 then return end
      pos = pos or n
      local value = t[pos]
      for i=pos,n-1 do
        t[i] = t[i+1]
      end
      t[n] = nil
      t.n = n-1
      return value
    end
type (v)

函数允许 Lua 测试一个值的类型。它接受一个参数,返回它的类型,以一个字符串表示。这个函数可能的返回值是: “nil” (一个字符串,不是 nil 值),“number”, “string”,“table”, “function” 和 “userdata”。


6.2 字符串处理


这个库提供字符串处理的通用函数,如查找,提取子串和模式匹配。在 Lua 中索引一个字符串的时候,第一个字符的索引是 1(不像 C 中是 0)。另外,索引可以为负数,负数被解释为逆向索引,从字符串的结尾开始。所以,最后一个字符位置是 -1,以此类推。

strbyte (s [, i])

返回 s 的第 i 个字符的内部数值码(例如:ASCII 码)。如果没有 i,它被认为是 1 。i 可以为负。

数值码在不同的平台不必兼容。

strchar (i1, i2, ...)

接受 0 个或者更多整数。返回一个字符串,长度和参数的个数相等,字符串中的每个字符的内部数值码(例如:ASCII 码)等于相应的参数。

数值码在不同的平台不必兼容。

strfind (s, pattern [, init [, plain]])

查找模式在 s 中第一次匹配。如果找到,strfind 返回这个匹配在 s 的开始结束索引;否则,返回 nil 。如果模式指定捕获(capture)(参见下面的 gsub),被捕获的字符串做为额外的结果返回。第三个可选的数字型参数指定从哪里开始检索;它的默认值为 1 ,并且可为负数。第四个可选参数 plain 为 1 时关闭模式匹配功能,函数只做一个简单的查找子串的操作,pattern 里不可没值。

strlen (s)

接受一个字符串返回它的长度。空字符串 “” 的长度为 0 。内嵌的 0 也被计数,所以 “a\000b\000c” 的长度为 5 。

strlower (s)

接受一个字符串,返回它的所有大写字母都转化为小写的拷贝。其它的字符保持不变。大写字母的定义依赖于当前的区域设置。

strrep (s, n)

返回一个字符串,该字符串是当前字符串 s 的 n 次拷贝的连接。

strsub (s, i [, j])

返回另一个字符串,它是 s 的子串,始于 i 终于 j ; i 和 j 可以为负数,如果 j 不指定,它被假定为 -1 (就是字符串的长度)。特别地,strsub(s,1,j) 调用返回 s 的 j 个字符的前缀,strsub(s, -i) 返回 s 的长度为 i 的后缀。

strupper (s)

接受一个字符串,返回它的所有小写字母都转化为大写的拷贝。其它的字符保持不变。小写字母的定义依赖于当前的区域设置。

format (formatstring, e1, e2, ...)

返回一个可变参数的格式化字符串,根据它的第一个参数描述(它必须是一个字符串)。格式字符串和标准 C 函数的 printf 族的规则一样。唯一不同的是选项/修饰符 *, l, L, n, p, 和 h 不支持,并且它还有一个额外的选择 q 。这个选项以一个合适的格式格式化一个字符串,该格式可以被 Lua 解释器安全的读回:写在双引号中的字符串,和所有的双引号,换行和反斜线都可以在写出时被正确的转义。 选项 c,d,E,e,f,g,i,o,u,X 和 x 都需要一个数值参数,q 和 s 需要一个字符串。例如,调用

    format('%q', 'a string with "quotes" and \n new line')

将生成字符串:

"a string with \"quotes\" and \
 new line"

转化可以应用于参数列表里的第 n 个参数,而不是下一个未使用的参数。在这种情况下,转化字符 % 被替换为序列 %d$,这里 d 是一个在 [1,9] 范围之内的十进制数,给定参数在参数列表中的位置。例如,调用 (“%2$d -> %1$03d”, 1, 34) 的结果为 “34 -> 001”。同样的参数可以多次用于转化中。

选项 c, d, E, e, f, g, G, i, o, u, X, 和 x 都期望一个数字做为参数,而 q 和 s 都期望一个字符串。* 修饰符可以通过建立适当的格式字符串来模拟。例如,”%*g” 可以由 “%”..width..”g” 来模拟。

格式化字符串或者将要被 %s 格式化的字符串值都不可以包含内嵌的 0 。%q 可处理内嵌 0 的字符串值。

gsub (s, pat, repl [, n])

返回一个 s 的拷贝,在拷贝中所有的模式 pat 出现的地方都被替换为一个由 repl 指定的字符串。总共替换的次数做为 gsub 返回的第二个值。

如果 repl 是一个字符串,那么它的值被用作替换用字符串。任意在 repl 中的形如 %n (n 在 1 到 9 之间)的序列代表第 n 个被捕获的子串的值。

如果 repl 是一个函数,那么这个函数会在每次一个匹配发生时被调用,所有被捕获的子串按顺序做为参数传递给它(参见下面)。如果这个函数返回的值是一个字符串,那么它被用做替换用字符串;否则,替换字符串为空符串。

最后,可选参数 n 限定了替换发生的最大次数。例如,当 n 为 1 时只有第一次出现的 pat 会被替换。

这里是一些例子:

   x = gsub("hello world", "(%w+)", "%1 %1")
   --> x="hello hello world world"

   x = gsub("hello world", "(%w+)", "%1 %1", 1) 
   --> x="hello hello world"

   x = gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1") 
   --> x="world hello Lua from"

   x = gsub("home = $HOME, user = $USER", "%$(%w+)", getenv) 
   --> x="home = /home/roberto, user = roberto" (for instance)

   x = gsub("4+5 = $return 4+5$", "%$(.-)%$", dostring) 
   --> x="4+5 = 9"

   local t = {name="lua", version="4.0"} 
   x = gsub("$name - $version", "%$(%w+)", function (v) return %t[v] end) 
   --> x="lua - 4.0"

   t = {n=0} gsub("first second word", "(%w+)", function (w) tinsert(%t, w) end) 
   --> t={"first", "second", "word"; n=3} 

模式 字符集: 一个字符集用来表示一个字符的集体。下面的组合可用来描述一个字符集:

  • x
    • (这里 x 是任意魔法字符 ^$()%.[]*+-?) - 代表字符 x 它自己。
  • .
    • (一个点) 代表所有的字符。
  • %a
    • 代表所有字母。
  • %c
    • 代表所有控制字符。
  • %d
    • 代表所有数字。
  • %l
    • 代表所有小写字母。
  • %p
    • 代表所有标点字符。
  • %s
    • 代表所有空白字符。
  • %u
    • 代表所有大写字母。
  • %w
    • 代表所有字母数字字符。
  • %x
    • 代表所有十六进制数字。
  • %z
    • 代表表示 0 的字符。
  • %x
    • (这里 x 是非字母数字字符); 代表字符 x. 这是避开魔法字符的标准方法。我们建议任何标点字符(即使是非魔法的)以 % 为前导字符当它在模式中表示它自己时。
  • [char-set]
    • 代表所有 char-set 中的字符的联合。一个字符范围可以由用一个 - 分割范围的结束字符指定。所有的上面描述的 %x 集合也可以用来作为一个 char-set 的组成部分。所有其它的 char-set 中的字符代表它们自己。例如,[%w_] (或 [_%w]) 代表所有的字母数字字符加上一个下划线,[0-7] 代表八进制数字,[0-7%l%-] 代表八进制字符加上小写字母加上一个中划线 - 字符。 范围和集合的相互作用没有定义。所以,模式像 [%a-z][a-%%] 没有意义。
  • [^char-set]
    • 代表 char-set 的补集,这里的 char-set 的解释如上所述。

对于所有的由单个字符代表的集合(%a, %c, …),相应的大写字母代表它的补集。例如,%S 代表所有的非空白字符。

字母,空间等的定义依赖于当前的区域设置。特别的,集合 [a-z] 可能不等于 %l 。对于可移植性来说,应首选第二种形式。

模式元素: 一个模式可以是

  • 一个字符集合,它匹配任意的集合中的单个字符;
  • 一个字符集合后跟 * ,它匹配 0 次或者多次集合中的字符的出现。这个重复出现元素总是匹配最长的序列;
  • 一个字符集合后跟 + ,它匹配 1 次或者多次集合中的字符的出现。这个重复出现元素总是匹配最长的序列;
  • 一个字符集合后跟 - ,它也是匹配 0 次或者多次集合中的字符的出现。不同于 * ,这个重复出现元素匹配最短的可能序列;
  • 一个字符集合后跟 ? ,它匹配 0 或者 1 次集合中的字符。
  • %n, n 从 1 到 9;这个元素匹配一个子串等于第 n 个被捕获的字符串(参见下面);
  • %bxy,这里 x 和 y 是两个不同的字符;这样的元素匹配字符串以 x 为开头,以 y 为结尾,并且 x 和 y 是平衡的。这意味着,如果你从左到右读这个字符串,遇到 x 计数 +1 ,遇到 y 计数 -1,结尾的 y 是使计数为 0 的第一个 y 。例如,元素 %b() 匹配圆括号平衡的表达式。

模式: 一个模式是一个模式元素的序列。一个 ^ 开头的模式确定目标字符串从开头匹配。一个 $ 结尾的模式确定目标字符串的结尾匹配。在其它位置,^ 和 $ 没有特殊的意思并且只表示他们自己。

捕获: 一个模式可以包含括在括号里的子模式,他们描述了捕获。当一个匹配成功了,匹配了的目标字符串的子串捕获被保存以备将来使用。捕获以它们的左括号编号。例如,在模式 “(a(.)%w(%s))”,匹配 “a(.)%w(%s)” 的字符串部分被保存为第一个捕获(因此它的编号为 1);字符 . 被捕获编号为 2, %s* 部分的编号为 3 。

一个模式不可以包含内嵌的 0 。用 %z 来代替。 (可能是由于自己对于模式部分不太了解,这部分翻译的比较乱,特别是对于有 capture 相关的。因为有的英文说法不好用同等意思的中文替换,有需要的还是参考英文原文吧。)


6.3 数学函数


这个库是到一些标准 C 数学函数库函数的一个接口。而且,它为二元幂操作符 ^ 注册了一个标签方法,当它应用于 x^y时,返回 x的 y次幂。 它提供了以下的函数:

    abs acos asin atan atan2 ceil cos deg exp floor log log10
    max min mod rad sin sqrt tan frexp ldexp random randomseed

还有一个全局变量 PI。它们中的绝大部分只是 C 函数库中同名函数的接口,不同之处是,在三角函数中,所有的角度是由角度表示,而不是弧度。函数 deg 和 rad 可以用来在弧度和角度之间转换。

函数 max 返回数字参数列表中的最大值,类似的,min 返回最小值。它们都可以使用 1 个, 2 个或者多个参数。

函数 random 和 randomseed 是到 ANSI C 提供的随机数生成器函数 rand 和 srand 的接口。(不保证它们的统计学属性。)函数 random ,无参调用时,返回一个 返回在[0,1) 区间的伪随机实数。当以一个数字 n 调用时,random 返回一个在[1,n] 区间的伪随机整数。当以两个参数 l 和 u 调用它时,random 返回一个在 [l,u] 区间的伪随机整数。


6.4 I/O


这些句柄被保存在 Lua 的两个全局变量中,叫做 _INPUT 和 _OUTPUT。全局变量 _STDIN, _STDOUT, 和 _STDERR 被初始化为文件描述符 stdin, stdout, 和 stderr。最初,_INPUT=_STDIN 且 _OUTPUT=_STDOUT。

一个文件句柄是一个包含文件流(FILE*)的 userdata ,它有一个由 I/O 库生成的独特的标签。

不像其它的规定,所有的 I/O 函数失败时返回 nil ,成功时返回其它的不同于 nil 的值。

openfile (filename, mode)

打开文件,由字符串 mode 指定打开模式。它返回一个新的文件句柄,或者,在出错的情况下,返回 nil 加上一个描述错误的字符串。这个函数不修改 _INPUT 或者 _OUTPUT 。

表示模式找字符串可以是下列之一:

  • ``r’‘

    读模式;

  • ``w’‘

    写模式;

  • ``a’‘

    追加模式;

  • ``r+’‘

    更新模式,所有之前的数据保留;

  • ``w+’‘

    更新模式,所有之前的数据被清除;

  • ``a+’‘

    追加更新模式,所有之前的数据保留,只允许在文件的结尾写。

模式字符串也可以后加一个 b ,为了在一些系统中用以加开二进制文件。这个字符串正是用标准 C 函数 fopen 。

closefile (handle)

关闭给定的文件。它不修改 _INPUT 或者 _OUTPUT 。

readfrom (filename)

这个函数可以以两种方法调用 。当以一个文件名调用时,它打开给定的文件,把它的句柄设置给 _INPUT,并返回它的值。它不关闭当前的输入文件。当无参调用时,它关闭 _INPUT 文件,并恢复 _INPUT 为标准输入。如果这个函数失败,它返回 nil 和一个描述错误的字符串。

如果文件名以一个竖线 | 开始,那么打开一个输入管道,通过函数 popen。并非所有的系统都实现了管道。此外,同时可以打开的文件数通常也是有限制的并且依赖于系统。

writeto (filename)

这个函数可以以两种方法调用 。当以一个文件名调用时,它打开给定的文件,把它的句柄设置给 _OUTPUT,并返回它的值。它不关闭当前的输出文件。注意,如果文件已经存在,它将会这个操作完全清除。当无参调用时,它关闭 _OUTPUT 文件,并恢复 _OUTPUT 为标准输出。如果这个函数失败,它返回 nil 和一个描述错误的字符串。

如果文件名以一个竖线 | 开始,那么打开一个输入管道,通过函数 popen。并非所有的系统都实现了管道。此外,同时可以打开的文件数通常也是有限制的并且依赖于系统。

appendto (filename)

打开一个名为 filename 的文件并且把它设置为 _OUTPUT。不像 writeto 操作,这个函数不会清除文件之前的内容;反而,所有的写给文件的内容追加到文件末尾。如果这个函数失败,它返回 nil 和一个描述错误的字符串。

remove (filename)

删除给定名字的文件。如果这个函数失败,它返回 nil 和一个描述错误的字符串。

rename(name1, name2)

把文件名从 name1 改为 name2。如果这个函数失败,它返回 nil 和一个描述错误的字符串。

flush ([filehandle])

保存所有的已写的数据到给定文件。如果 filehandle 没有指定,它将刷新所有的打开的文件。如果这个函数失败,它返回 nil 和一个描述错误的字符串。

seek (filehandle [, whence] [, offset])

设置和获取文件的位置,从文件的起始处开始单位为字节,以指定的字符串 whence 为基点,到给定位置为 offset 的地方,如下所示:

  • ``set’‘

    以位置 0 为基点 (文件的起始处);

  • ``cur’‘

    以当前位置为基点;

  • ``end’‘

    以文件尾为基点;

在成功的情况下,函数 seek 返回最终的文件位置,从文件的起始处开始单位为字节。如果这个函数失败,它返回 nil 和一个描述错误的字符串。

whence 的默认值为 “cur”,offset 的默认值为 0 。因此,调用 seek(file) 返回文件的当前位置,并不改变它;调用 seek(file, “set”) 设置位置到文件的起始处(并且返回 0 );调用 seek(file, “end”) 设置位置到文件的结尾处,并且返回它的尺寸。

tmpname()

返回一个文件名字符串,它可以安全地用做临时文件名。文件在使用前必须显式打开,在不使用时要移除。

read ([filehandle,] format1, ...)

读文件 _INPUT,或者 filehandle 如果参数给定的话,根据给定的格式,格式指定了读什么。对于每一个格式,函数返回一个字符串(或者一个数字),或者 nil 如果对于指定格式不能读取数据。当不使用格式调用时,它使用一个默认的格式来读取下一行(参见下面)。

可用的格式是

  • ``*n’‘

    读一个数字;这是唯一的返回数字而非字符串的格式。

  • ``*l’‘

    读下一行(跳过行尾),或者 nil 在文件的末尾。这是默认的格式。

  • ``*a’‘

    读取整个文件,从当前的位置。在文件的末尾,返回空字符串。

  • ``*w’‘

    读取下一个单词(最大的非空白字符序列),如果需要的话跳过空白,或者 nil 在文件的末尾。

  • number

    读取一个由 number 个字符的字符串,或者 nil 在文件的末尾。

write ([filehandle, ] value1, ...)

写每一个参数的值到 文件 _OUTPUT,或者 filehandle 如果参数给了。参数必须是字符串或者数字。可以使用 tostring 或者 format 在 write 之前来写其它值。如果这个函数失败,它返回 nil 和一个描述错误的字符串。


6.5 系统功能


clock ()

返回程序使用的 CPU 总时间的近似值,单位是秒。

date ([format])

返回一个字符串包含日期和时间,格式由给定的字符串 format 指定,和 ANSI C 函数 strftime 的规则一样。当无参调用时,它返回一个合理的日期和时间表示,依赖于宿主程序和当前的区域设置。

execute (command)

这个函数和 C 函数 system 是等价的。它把被执行的 command 传递给操作系统 shell。它返回一个状态码,状态码系统相关。

exit ([code])

调用 C 函数 exit,以一个可选的 code,以终止程序。code 的默认值是成功的状态码。

getenv (varname)

返回进程的环境变量 varname 的值,或者 nil 如果变量没有定义的话。

setlocale (locale [, category])

它是一个到 ANSI C 函数 setlocale 的接口。locale 是一个字符串指定了一个区域;category 是一个可选的字符串描述了将要改变的种类: “all”, “collate”, “ctype”, “monetary”, “numeric”, 或者 “time”;默认的 category 是 “all”。函数返回新的区域设置的名字,或者 nil 如果请求不被执行。


7 调试接口


Lua 没有内置的调试功能。而是借助于函数和钩子(hook)提供了一个特殊接口,可以用来构建不同种类的调试器,分析器(profile)和一些其它的需要解释器内部信息的工具。这个接口在 luadebug.h 文件中声明。


7.1 栈和函数信息


获得解释器栈信息的主要函数是:

    int lua_getstack (lua_State *L, int level, lua_Debug *ar);

它用给定层级上正在执行的一个活动记录标识填充部分 lua_Debug 结构。0 级是正在运行的函数,层级 n+1 是调用层级 n 的函数。通常,lua_getstack 返回 1 ;当以一个高于栈的深度的层次来调用时,它返回 0 。

结构 lua_Debug 用来携带一个活动函数的不同信息片段:

    typedef struct lua_Debug {
      const char *event;    /* "call", "return" */
      int currentline;      /* (l) */
      const char *name;     /* (n) */
      const char *namewhat; /* (n) global, tag method, local, field */
      int nups;             /* (u) number of upvalues */
      int linedefined;      /* (S) */
      const char *what;     /* (S) "Lua" function, "C" function, Lua "main" */
      const char *source;   /* (S) */
      char short_src[LUA_IDSIZE]; /* (S) */
      
      /* private part */
      ...
    } lua_Debug;

lua_getstack 只填充这个结构的私有部分,为后来使用。用有用信息填充 lua_Debug 其它的字段,调用

    int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);

这个函数出错时返回 0 (例如,一个 what 的无效选项)。字符串 what 中的每个字符选择一些被填充的 ar 的字段,如上面 lua_Debug 的定义中被一个括号中的字母标识出来的: S' 填充字段 source, linedefined, 和 what; l’ 填充字段 currentline, 等。而且, `f’ 压栈在给定层级上正在执行的函数。 为获得一个不活动函数的信息(即,不在栈里),可以把函数压栈,并字符 > 放在字符串 what 的起头。例如,为了获取一个函数 f 在哪里定义,你可以

    lua_Debug ar;
    lua_getglobal(L, "f");
    lua_getinfo(L, ">S", &ar);
    printf("%d\n", ar.linedefined);

lua_Debug 的字段有下列意思

  • source

    如果函数被定义在字符串中,source 就是那个字符串;如果函数定义在一个文件中,source 由 @ 开始并后跟文件名。

  • short_src

    一个 ``printable’’ 版本的 source,用在错误信息中。

  • linedefined

    函数定义开始处的等号。

  • what

    字符串 “Lua” 如果这是一个 Lua 函数, “C” 如果这是一个 C 函数, 或者 “main” 如果这是一个块的主要部分。

  • currentline

    给定函数正在执行的当前行。如果没有行信息, currentline 被设置为 -1.

  • name

    一个给定函数的合理的名字。因为函数在 Lua 中第一类值,它们没有一个固定的名字:一些函数可能是很多全局变量的值,而其它的可能只保存在一个表的字段中。lua_getinfo 函数检查给定的函数是标签方法或者是一个全局变量的名字。如果给定函数是一个标签方法,那么 name 指向相应的事件名。如果给定的函数是一个全局变量的值,那么 name 指向变量名字。如果给定函数既不是一个标签方法也不是一个全局变量,name 被设置为 NULL。

  • namewhat

    解释之前的字段。如果函数是一个全局变量,namewhat 是 “global”;如果函数是一个标签方法,namewhat 是 “tag-method”;否则,namewhat 是 “” (空字符串)。

  • nups

    一个函数的 upvalue 的个数。


7.2 局部变量的操作


luadebug.h 使用索引来操纵局部变量:第一个参数或者局部变量的索引为 1,以此类推,直到最后一个活动的局部变量。 下面的函数可以用来操纵给定活动记录的局部变量:

    const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
    const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);

参数 ar 必须是一个有效的活动记录,被之前的 lua_getstack 调用填充或者作为一个 hook 参数给定(参见 7.3 节)。函数 lua_getlocal 获得一个局部变量(n)的索引,把它的值压栈,并返回它的名字。对于 lua_setlocal,你把新值压栈,这个函数把新值赋给那个变量并返回它的名字。两个函数失败时均返回 NULL;如果索引大于活动局部变量的时候这处情况就会发生。 作为一个例子,下面的函数列出一个在栈的给定层次上的所有的局部变量的名字:

    int listvars (lua_State *L, int level) {
      lua_Debug ar;
      int i = 1;
      const char *name;
      if (lua_getstack(L, level, &ar) == 0)
        return 0; /* failure: no such level in the stack */
      while ((name = lua_getlocal(L, &ar, i++)) != NULL) {
        printf("%s\n", name);
        lua_pop(L, 1); /* remove variable value */
      }
      return 1;
    }

7.3 钩子 (Hooks)


Lua 解释器为调试提供了两个钩子(Hook),一个 call hook, 一个 line hook。它两个有相同的类型:

    typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);

你可以使用下面的函数来设置它们:

    lua_Hook lua_setcallhook (lua_State *L, lua_Hook func);
    lua_Hook lua_setlinehook (lua_State *L, lua_Hook func);

钩子是无效的如果它的值为 NULL ,NULL 也是两个钩子的默认值。函数 lua_setcallhook 和 lua_setlinehook 设置他们相应的钩子并返回它们之前的值。

当解释器进入或者离开一个函数时,call hook 会被调用。ar 的 event 字段的字符串是 “call” 或者 “return”。这个 ar 之后可以被用在调用 lua_getinfo, lua_getlocal, 和 lua_setlocal 去获得更多的关于函数和操纵局部变量的信息。

当解释器每次改变它执行的代码行号时会调用 line hook。ar 的字符 event 的字符串是 “line” , 并且 currentline 字段有行号。再次强调,你可以使用这个 ar 在其它的调用 API 的调用中。

当 Lua 在执行一个钩子时,它禁用了其它的钩子的调用。因此,如果一个钩子调用 Lua 去执行一个函数或者一个块,这个执行将不会再有其它的钩子调用。


7.4 自反的调用接口


库 ldblib 为 Lua 程序提供调试接口的功能。如果你想使用这个库,你的宿主应用程序必须打开它,通过调用 lua_dblibopen。

当使用这个库时你应该万分小心。这里提供的函数应该仅仅用于调试或者类似的任务(例如,性能分析)。请抵制把他们作为常用工具的诱惑。它们很慢并且违反了一些语言的安全性。(例如,局部变量的私密性)。作为一个一般性规则,如果你的程序不需要这个库,不要打开它。

getinfo (function, [what])

这个函数返回一个带有函数信息的表。你可以直接给出函数,或者你可以给出一个数字作为函数的值,它意味着函数正在栈上执行的函数的层次:Level 0 是当前的函数(getinfo 它自己);Level 1 是调用 getinfo 的函数;以此类推。如果函数的值比活动函数的个数多,那么 getinfo 返回 nil。 返回的表包含所有由 lua_getinfo 返回的字段,用一个字符串 what 来描述获取什么。what 的默认值是获得所有的可用信息。 例如,表达式 getinfo(1, “n”).name 返回当前函数的名字,如果可以找到一个合理的名字,getinfo(print) 返回一个关于 print 函数的所有可用信息的表。

getlocal (level, local)

函数返回局部变量的名字和值,局部变量的 index 为 local ,它位于栈上的层级 level 的函数中。(第一个参数或者局部变量的 index 为 1,以此类推,直到最后一个活动的局部变量。)函数返回 nil 如果没有给定索引的局部变量,并发生一个错误当以一个范围之外的 level 调用时。(你可以调用 getinfo 去检查 level 是否有效。)

setlocal (level, local, value)

函数设置局部变量的值为 value,局部变量的 index 为 local ,它位于栈上的层级 level 的函数中。函数返回 nil 如果没有给定索引的局部变量,并发生一个错误当以一个范围之外的 level 调用时。

setcallhook (hook)

设置函数 hook 为调用钩子;这个钩子在每次解释器开始或者退出一个函数执行时将会被调用。调用钩子唯一的参数是事件名称(”call” 或者 “return”)。你可以以 level 2 调用 getinfo 去获得更多的正在被调用或正在返回的函数信息(level 0 是 getinfo 函数,level1 是钩子函数)。当无参调用时,这个函数关闭调用钩子。setcallhook 返回老的钩子。

setlinehook (hook)

设置函数 hook 为行钩子;这个钩子在每次解释器改变正在执行的代码的行时将会被调用。行钩子的唯一参数是解释器将要执行的行号。当无参调用时,这个函数关闭调用钩子。setlinehook 返回老的钩子。


8 独立执行的 Lua


尽管 Lua 被设计作为一个扩展语言,被嵌入到一个 C 宿主程序,它也经常被做为一个独立的语言使用。一个作为独立语言使用的 Lua 解释器,叫做简单的 lua,在标准发布版中被提供。这个程序可以被以下的参数以任意顺序调用:

  • -sNUM 设置栈的大小为 NUM(如果存在,这必须是第一个选项);
  • - 把标准输入作为文件执行。
  • -c 调用 lua_close 在执行完所有参数之后;
  • -e \rmstat 执行字符串 stat;
  • -f filename 执行文件 filename 所有剩余的参数在表 arg 中;
  • -i 进入交互模式,显示一个提示符;
  • -q 进入交互模式,不显示提示符。
  • -v 打印版本信息;
  • var=value 设置全局变量 var 为字符串 “value”;
  • filename 执行文件 filename。

当无参调用时,lua 的行为和 lua -v -i 一样当标准输入是一个终端时,否则像 lua - 。 所有的参数按顺序处理,除了 -c 。例如,一个调用

    $ lua -i a=test prog.lua

将首先和用户交互直到一个 EOF 出现在标准输入中,然后设置 a 为 “test”,最后执行文件 prog.lua。(这里,$ 是一个 shell 提示符。你的提示符可能与此不同。) 当选项 -f filename 被使用时,所有命令行中剩余的参数放进一个叫做 arg 的表中被传给 Lua 程序 filename 。在这个表中,字段 n 获得最后的参数的索引,字段 0 获得 “filename”。例如,在调用

    $ lua a.lua -f b.lua t1 t3

解释器首先执行文件 a.lua,然后新建一个表

    arg = {"t1", "t3"; n = 2, [0] = "b.lua"}

最后,执行文件 b.lua。独立的解释器也提示了一个 getargs 函数可以用来存取所有的命令行参数。例如,如果你这样调用 Lua

    $ lua -c a b

那么一个 getargs 的调用在 a 或者 b 中将返回表

    {[0] = "lua", [1] = "-c", [2] = "a", [3] = "b", n = 3}

在交互模式中,一个多行的句子可以在行尾以反斜杠结束。如果全局变量 _PROMPT 被定义为一个字符串,那么它的值将被用做提示符。所以,提示符可以直接在命令行中修改:

    $ lua _PROMPT='myprompt> ' -i

或者在任意的 Lua 程序中给 _PROMPT 赋值。 在 Unix 系统中,Lua 脚本可以被设置为可执行的程序,使用 chmod +x 和 #! 格式,像在 #!/usr/local/bin/lua, 或者 #!/usr/local/bin/lua -f 去获得其它的参数。


鸣谢


作者要感谢 CENPES/PETROBROBAS 和 TeCGraf 一起,使用该系统的早期版本,并提出宝贵意见。作者还要感谢 Carlos Henrique Levy,为这个语言起了个名字。Lua 在葡萄牙语里是月亮的意思。


和之前版本的不兼容及索引


Lua 4.0 is a major revision of the language. We took a great care to avoid incompatibilities with the previous public versions of Lua, but some differences had to be introduced. Here is a list of all these incompatibilities.

Incompatibilities with version 3.2

Changes in the Language

  • All pragmas ($debug, $if, ...) have been removed.
  • for, break, and in are now reserved words.
  • Garbage-collection tag methods for tables is now obsolete.
  • There is now only one tag method for order operators.
  • In nested function calls like f(g(x)), all return values from g are passed as arguments to f. This only happens when g is the last or the only argument to f.
  • The pre-compiler may assume that some operators are associative, for optimizations. This may cause problems if these operators have non-associative tag methods.
  • Old pre-compiled code is obsolete, and must be re-compiled.

Changes in the Libraries

  • When traversing a table with next or foreach, the table cannot be modified in any way.
  • General read patterns are now obsolete.
  • The functions rawgettable and rawsettable have been renamed to rawget and rawset.
  • The functions foreachvar, nextvar, rawsetglobal, and rawgetglobal are obsolete. You can get their functionality using table operations over the table of globals, which is returned by globals. setglobal and sort no longer return a value; type no longer returns a second value.
  • The p option in function call is now obsolete.

Changes in the API

  • The API has been completely rewritten: It is now fully reentrant and much clearer.
  • The debug API has been completely rewritten.

Lua 完整的语法


chunk ::= {stat [`;']}

block ::= chunk

stat ::= varlist1 `=' explist1
  | functioncall
  | do block end
  | while exp1 do block end
  | repeat block until exp1
  | if exp1 then block {elseif exp1 then block} [else block] end
  | return [explist1]
  | break
  | for `name' `=' exp1 `,' exp1 [`,' exp1] do block end
  | for `name' `,' `name' in exp1 do block end
  | function funcname `(' [parlist1] `)' block end
  | local declist [init]

funcname ::= `name' | `name' `.' `name' | `name' `:' `name'

varlist1 ::= var {`,' var}

var ::= `name' | varorfunc `[' exp1 `]' | varorfunc `.' `name'

varorfunc ::= var | functioncall

declist ::= `name' {`,' `name'}

init ::= `=' explist1

explist1 ::= {exp1 `,'} exp

exp1 ::= exp

exp ::= nil | `number' | `literal' | var | function | upvalue
| functioncall | tableconstructor | `(' exp `)' | exp binop exp | unop exp

functioncall ::= varorfunc args | varorfunc `:' `name' args

args ::= `(' [explist1] `)' | tableconstructor | `literal'

function ::= function `(' [parlist1] `)' block end

parlist1 ::= `...' | `name' {`,' `name'} [`,' `...']

upvalue ::= `%' `name'

tableconstructor ::= `{' fieldlist `}' 
fieldlist ::= lfieldlist | ffieldlist | lfieldlist `;' ffieldlist | ffieldlist `;' lfieldlist 
lfieldlist ::= [lfieldlist1] 
ffieldlist ::= [ffieldlist1] 
lfieldlist1 ::= exp {`,' exp} [`,'] 
ffieldlist1 ::= ffield {`,' ffield} [`,'] 
ffield ::= `[' exp `]' `=' exp | `name' `=' exp

binop ::= `+' | `-' | `*' | `/' | `\^{ ' | `..'
| `<' | `<=' | `>' | `>=' | `==' | `\ { '=}
| and | or}

unop ::= `-' | not