【转贴】Lua 5.0 参考手册

作者: Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

Copyright © 2003 Tecgraf, PUC-Rio. All rights reserved.

译者:ShiningRay Nicholas @ NirvanaStudio

给予支持

1 - 绪论

Lua是一种为支持有数据描述机制的一般过程式编程语言而设计的扩展编程语言。它同样可以对面向对象语言、函数式程序设计(Functional Programming,如Lisp)以及数据驱动编程(data-driven programming)提供很好的支持。它的目标是被用作一种强大的、轻型的配置语言。Lua目前已经被实现为一个扩展库,是用clean C (ANSI C/C++的一个通用子集)编写的。

作为一个扩展语言,Lua没有"Main"函数的概念:它仅仅是嵌入一个宿主程序进行工作,可以称之为 嵌入式编程 或者简单的说是 宿主编程。这个宿主程序可以调用函数来执行Lua的代码片断,可以设置和读取Lua的变量,可以注册C函数让Lua代码调用。Lua的能力可以扩展到更大范围,在不同的领域内,这样就在同样的语法框架下创建了你自定义的编程语言。

Lua的发行版包括一个独立的嵌入式程序,lua,他使用Lua的扩展库来提供一个完全的Lua解释器。

Lua是自由软件,通常不提供任何担保,如它的版权说明中叙述的那样。 手册中描述的实现在Lua的官方网站可以找到,www.lua.org

如果需要知道Lua设计背后的一些决定和讨论,可以参考以下论文,它们都可以在Lua的网站上找到。

  • R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua---an extensible extension language. Software: Practice & Experience26 #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 Journal21 #12 (Dec 1996) 26-33.
  • R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. The evolution of an extension language: a history of Lua, Proceedings of V Brazilian Symposium on Programming Languages (2001) B-14-B-28.

Lua在葡萄牙语中的意思是“月亮”,发音是 LOO-ah。

2 - 语言

这一章将描述Lua的词法、语法和语义结构。换句话说,这一章会讲什么标记是合法的,他们是如何组合的,以及他们的组合是什么含义。

语言结构会使用常用的扩展BNF范式来解释,如{a} 表示0或多个a, [a] 表示a是可选的(0个或1个)。非终端字体(不能显示的)用 斜体表示,关键字是粗体,其他终端符号用typewriter(等宽)字体,并用单引号引出。

2.1 - 词法约定

Lua中的标识符(Identifiers)可以是任意的数字、字符和下划线“_”,但不能以数字开头。这条规则符合大多数编程语言中的标识符的定义。(字符的具体定义要根据系统的地区设置:任何区域设置可以认同的字母表中的字母都可以用在标识符中。)

下面的关键字(keywords)为保留关键字不可以作为标识符出现:

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

Lua对大小写敏感:and是一个保留字,但是 AndAND 是两个不一样的、但都合法的标识符。习惯上来说,以下划线开始且后面跟着大写字母的标识符 (例如 _VERSION) 是为Lua内部变量所保留的。

下面的字符(串)是其他的一些标记:

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

字符串(Literal strings) 以单引号或者双引号定界,同时可以包含以下C语言风格的转义字符:

  • \a --- 铃声(bell)
  • \b --- 回退(backspace)
  • \f --- form feed
  • \n --- 新行(newline)
  • \r --- 回车(carriage return)
  • \t --- 水平制表符(horizontal tab)
  • \v --- 垂直制表符(vertical tab)
  • \\ --- 反斜杠(backslash)
  • \" --- 双引号(quotation mark)
  • \' --- 单引号(apostrophe)
  • \[ --- 左方括号(left square bracket)
  • \] --- 右方括号(right square bracket)

另外,一个 `\newline´ (一个反斜杠加上一个真正的换行符)会导致字符串内的分行。字符串中的字符也可以使用转义字符`\ddd´通过数字值来指定。ddd 是最多为3个十进制数字的序列。Lua中的字符串也可以包含8进制数字,包括嵌入零,它可以表示为 `\0´。

字符串也可以用双方括号来定界[[ · · · ]]。这种括号方式的语法,字符串可以跨越多行,也可以包含嵌套的,同时不会转义任何序列。方便起见,当开始的 `[[´ 后面紧跟着一个换行符的话,这个换行符不会包括在字符串内。举个例子:在一个使用ASCII编码(其中`a´ 的编码是 97,换行符是 10,字符`1´ 是 49)的系统中,以下四种格式得到的都是同一个字符串:

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

数值常量(Numerical constants) 可以有一个可选的底数部分和一个可选的指数部分。以下是有效的数值常量:

       3     3.0     3.1416  314.16e-2   0.31416E1

注释(Comments) 可以在任何地方出现,必须在最前面加上双减号 (--)。如果紧接着 -- 的文本不是 [[,那么会认为是一个 短注释(short comment), 这一行往后到行尾都是注释。否则,会认为是一个 常注释(long comment),注释直到相应的 ]]结束。长注释可以跨越多行,同时可以包含嵌套的 [[ · · · ]] 括号对。

为了方便起见,文件的第一行如果是以#开始,这个机制允许Lua在Unix系统中用做一个脚本解释器(见 6)。

2.2 - 值和类型

Lua是一种 动态类型语言(dynamically typed language)。这意味着变量是没有类型的;只有值才有。语言中没有类型定义。所有的值都包含他自身的类型。

Lua中有八种基本类型:nil, boolean, number, string, function, userdata, threadtableNil 空类型只对应 nil值,他的属性和其他任何值都有区别;通常它代表没有有效的值。 Boolean 布尔类型有两种不同的值 false and true。在Lua中, nil and false 代表成假条件;其他任何值都代表成真条件。 Number 数字类型表示实数(双精度浮点数)。(构建Lua解释器时也可以很容易地用其他内部的表示方式表示数字,如单精度浮点数或者长整型)。 String 字符串类型表示一个字符的序列。Lua 字符串可以包含8位字符,包括嵌入的 ('\0') (见 2.1)。

函数是Lua中的 第一类值(first-class values)。也就是说函数可以保存在变量中,当作参数传递给其他函数,或者被当作结果返回。Lua可以调用(和处理)Lua写的函数和C写的函数 (见 2.5.7)。

用户数据类型(userdata) 提供了让任意C数据储存在Lua变量中的功能。这种类型直接对应着一块内存,Lua中也没有任何预先定义的操作,除了赋值和一致性比较。然而,通过使用 元表(metatables),程序员可以定义处理userdata的操作。(见 2.8)。 Userdata 值不能在Lua中建立或者修改,只能通过 C API。这保证了宿主程序的数据完整性。

线程(thread) 类型代表了相互独立的执行线程,用来实现同步程序。

表(table) 类型实现了联合数组,也就是说,数组不仅可以使用数字,还能使用其他的值(除了 nil)。 而且,tables 可以是 互异的(heterogeneous),他们可以保存任何类型的值(除了 nil)。 Tables 是Lua中唯一的数据结构机制;他们可以用来表示一般数组,特征表,集合,记录,图,树等等。如果要表示记录,Lua使用字段名作为索引。语言支持 a.name 这种比较优美的表示方式,还有 a["name"]。在Lua中有几种建立表的简便方法 (见 2.5.6)。

就像索引一样,表字段的值也可以是任何类型(除了 nil)。特别需要注意地是,由于函数是第一型的值,表字段也可以包含函数。这样表也可以支持 方法(methods) (见 2.5.8)。

表,函数,和用户数据类型的值都是 对象(objects):变量不会包含他们的实际值,只是一个他们的引用(references)。 赋值,参数传递和函数返回只是操作这些值的引用,这些操作不会暗含任何拷贝。

库函数 type 返回一个字符串描述给出值所表示的类型 (见 5.1)。

2.2.1 - 类型转换

Lua提供运行时的数字和字符串值得自动转换。任何对字符串的算术操作都会现尝试把字符串转换成数字,使用一般规则转换。反过来,当一个数值用在需要字符串的地方时,数字会自动转换成字符串,遵循一种合理的格式。如果要指定数值如何转换成字符串,请使用字符串库中的 format 函数(见 5.3)。

2.3 - 变量

变量是储存值的地方。Lua中有三种不同的变量:全局变量,局部变量和表字段。

一个名称可以表示全局变量或局部变量(或者一个函数的正式参数,一种局部变量的特殊形式):

	var ::= Name

Lua假设变量是全局变量,除非明确地用local进行声明 (见 2.4.7)。局部变量有 词义范围(lexically scoped):局部变量可以被在它们范围内的函数自由访问 (见 2.6)。

在变量第一次赋值之前,它的值是 nil

方括号用于对表进行检索:

	var ::= prefixexp `[´ exp `]´

第一个表达式 (prefixexp)结果必须是表;第二个表达式 (exp) 识别表中一个特定条目。给出表的表达式有一个限制语法;详细见 2.5。

var.NAME 语法是 var["NAME"] 的较好形式:

	var ::= prefixexp `.´ Name

访问全局变量和表字段的实质可以通过元表进行改变。对索引变量 t[i] 的访问等同于调用 gettable_event(t,i)。(关于 gettable_event 的完整描述见 2.8。这个函数并没有在Lua中定义,也无法调用。我们在这里仅仅用来解释原理)。

所有的全局变量存在一个普?ǖ腖ua表中,称之为 环境变量表(environment tables) 或简称 环境(environments)。由C写的并导入到Lua中的函数 (C 函数) 全部共享一个通用 全局环境(global environment)。Lua写的每个函数 (a Lua 函数) 都有一个它自己的环境的引用,这样这个函数中的所有的全局变量都会指向这个环境变量表。当新创建一个函数时,它会继承创建它的函数的环境。要改变或者获得Lua函数的环境表,可以调用 setfenv or getfenv (见 5.1)。

访问全局变量 x 等同于 _env.x,又等同于

       gettable_event(_env, "x")

_env 是运行的函数的环境。(_env 变量并没有在Lua中定义。我们这里仅仅用来解释原理)

2.4 - 语句

Lua支持一种很通俗的语句集,和Pascal或者C中的很相似。他包括赋值,控制结构,过程调用,表构造和变量声明。

2.4.1 - 语句段

Lua执行的最小单元称之为一个 段(chunk)。一段语句就是简单的语句的序列,以顺序执行。每一个语句后面都可以加上一个分号(可选):

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

Lua将语句段作为一个匿名函数 (见 2.5.8) 的本体进行处理。这样,语句段可以定义局部变量或者返回值。

一段语句可以储存在文件内或者宿主程序的一个字符串中。当语句段被执行时,他首先被预编译成虚拟机使用的字节码,然后虚拟机用一个解释器执行被编译的代码。

语句段也可以被预编译为二进制代码;详情参看 luac 程序。源代码和编译形态可以互相转换;Lua自动监测文件类型然后作相应操作。

2.4.2 - 语句块

一个语句块是一系列语句;从语句构成上来看,语句块等同于语句段:

	block ::= chunk

一个语句块可以明确定界来替换单个语句:

	stat ::= do block end

显式语句块可以很好地控制变量的声明范围。显示语句块有时也常会在另一个语句块的中间添加 returnbreak 语句 (见 2.4.4)。

2.4.3 - 赋值

Lua允许多重赋值。因此,赋值的语法定义为:等号左边是一个变量表,右边是一个表达式表。两边的表中的元素都用逗号分隔开来:

	stat ::= varlist1 `=´ explist1
	varlist1 ::= var {`,´ var}
	explist1 ::= exp {`,´ exp}

我们将在 2.5 讨论表达式。

在赋值之前,值的表长度会被 调整 为和变量的表一样。如果值比需要的多,多出的值就会被扔掉。如果值的数量不够,就会用足够多的 nil 来填充表直到满足数量要求。如果表达式表以一个函数调用结束,那么在赋值之前,函数返回的所有的值都会添加到值的表中(除非把函数调用放在括号里面;见 2.5)。

赋值语句首先计算出所有的表达式,然后才会执行赋值,所以代码:

       i = 3
       i, a[i] = i+1, 20

设置 a[3] 为 20,但不影响 a[4]。因为在 a[i] 中的 i 在赋值为4之前是等于3。同样的,下面这行:

       x, y = y, x

可以交换 xy 的值。

对全局变量和表字段的赋值可以看作是通过元表进行的。对一个索引变量的赋值 t[i] = val 等同于 settable_event(t,i,val)。 (settable_event详细介绍参看 2.8 ,Lua中并未定义该函数,他也无法直接调用。我们这里只是用它来进行解释。)

对全局变量的赋值 x = val 等同于赋值语句 _env.x = val,像前面也等同于:

       settable_event(_env, "x", val)

_env 是运行函数的环境。(_env 变量并未在Lua中定义。我们这里只是用来进行解释。)

2.4.4 - 控制结构

控制结构 if, whilerepeat 具有通用的含义和类似的语法:

	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)。

控制结构的条件表达式 exp 可以返回任意值。falsenil 都表示假。所有其他的值都认为是真(特别要说明的:数字0和空字符串也表示真)。

语句 return 用来从函数或者是语句段中返回一个值。函数和语句段都可以返回多个值,所以 return 语句的语法为:

	stat ::= return [explist1]

break 语句可以用来终止while, repeat 或者 for 循环的执行,直接跳到循环后面的语句。

	stat ::= break

break 结束最里面的一个循环。

由于语法的原因, returnbreak 语句只能作为语句块的 最后一个 语句。如果确实需要在语句块的中间使用 return 或者 break,需要使用一个显示语句块: `do return end´ 和 `do break end´,这样现在 returnbreak 就成为他们(内部)语句块中的最后一个语句了。实际上,这两种用法一般只用在调试中。

2.4.5 - For 语句

for 语句有两种形式:数值形式和一般形式。

数值形式的 for 循环根据一个控制变量用算术过程重复一语句块。语法如下:

	stat ::= for Name `=´ exp `,´ exp [`,´ exp] do block end

block 语句块根据 name 以第一个 exp 的值开始,直到他以第三个 exp 为步长达到了第二个 exp。一个这样的 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 是不可见的变量。这里只是为了进行解释。
  • 如果你在程序块内给 var 赋值,结果行为将会不确定。
  • 如果没有给出第三个表达式(步长),那么默认为1。
  • 你可以使用 break 来退出 for 循环。
  • 循环变量 var 是局部变量;你不可以在 for 循环结束之后继续使用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。

for 的语句的一般形式是操作于函数之上的,称之为迭代器(iterators)。每一个迭代过程,它调用迭代函数来产生新的值,直到新的值是 nil 。一般形式 for 循环有如下语法:

	stat ::= for Name {`,´ Name} in explist1 do block end

一个这样的 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 只会计算一次。他的结果是一个 迭代 函数,一个 状态,和给第一个 迭代变量的一个初始值。
  • _f_s 是不可见的变量。这里只是用来进行解释说明。
  • 如果你在语句块中给 var_1 赋值,那么行为就会变得不确定。
  • 你可以使用 break 来退出 for 循环。
  • 循环变量 var_i 是局部变量;你不可以在 for 循环结束之后继续使用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。

2.4.6 - 语句式函数调用

如果要忽略可能的影响,函数调用可以按照语句执行:

	stat ::= functioncall

I在这里,所有的返回值都会被忽略。函数调用将在 2.5.7 详细解释。

2.4.7 - 局部变量声明

局部变量可以在语句块中任何地方声明。声明时也可以添加一个初始赋值:

	stat ::= local namelist [`=´ explist1]
	namelist ::= Name {`,´ Name}

如果出现初始赋值,他的语法和多重赋值语句一样(见 2.4.3)。否则,所有的变量都会初始化为 nil

一个语句段也是一个语句块(见 2.4.1),所以语句段之内的任何显式语句块之外也可以声明局部变量。这种局部变量在语句段结束就会销毁。

局部变量的可见规则会在 2.6解释。

2.5 - 表达式

Lua中有以下几种基本表达式:

	exp ::= prefixexp
	exp ::= nil | false | true

	exp ::= Number
	exp ::= Literal
	exp ::= function
	exp ::= tableconstructor
	prefixexp ::= var | functioncall | `(´ exp `)´

数字和字符串已经在 2.1 中解释;变量在 2.3 中解释;函数定义在 2.5.8;函数调用在 2.5.7;表构造器在 2.5.6。

一个用括号括起的表达式只会返回一个值。这样,(f(x,y,z)) 将只会返回单一的一个值,即使 f 可以返回多个值,((f(x,y,z)) 的值将是 f 返回的第一个值或者如果 f 没有返回任何值就是 nil )。

表达式也可以使用各种算术运算符,关系运算符和逻辑运算符,下面几节就会讲到。

2.5.1 - 算术运算符

Lua支持常见的几种运算符:二元 + (加), - (减), * (乘), / (除), 以及 ^ (指数运算); 一元 - (负号)。如果操作数是数字,或者是可以转换成数字的字符串(见 2.2.1),那么所有的操作都和算术意义上的运算一致(除了指数)。指数运算其实是调用一个全局函数 __pow,否则一个合适的元方法将会被调用(见 2.8)。标准数学库定义了函数 __pow,给出了指数运算的定义(见 5.5)。

2.5.2 - 关系运算符

Lua中的关系运算符有

       ==    ~=    <     >     <=    >=

这些运算只会产生 falsetrue值。

等于 (==) 先比较操作数的类型。如果类型不一样,结果便是 false。否则,再比较操作数的值。对象(表,用户数据,线程,和函数)是按照引用进行比较:只有两个对象是同一个对象的时候,才认为是相等。每次你创建一个新的对象(表,用户数据,或者是函数)。这个新的对象将不同于前面存在的任何对象。

你可以用"eq"元方法改变Lua比较表的方式(见 2.8)。

2.2.1 的转换规则 不适用 于相等比较。这样," "0"==0 结果是 false ,同样 t[0]t["0"] 给出的是表中不同的字段。

而操作符 ~= 是等于 (==) 的相反的操作。

T操作符的执行顺序如下。如果两个参数都是数字,那么它们就直接进行比较。如果,两个参数都是字符串,那么它们的值会根据当前的区域设置进行比较。否则,Lua尝试调用"lt"或者 "le" 元方法(见 2.8)。

2.5.3 - 逻辑运算符

Lua中的逻辑运算符是:

       and   or    not

和控制结构一样(见 2.4.4),所有的逻辑操作符认为 falsenil 都?羌伲渌闹刀际钦妗?

not 操作符总是返回 falsetrue

合取运算 and 如果第一个参数是 false 或者 nil 则返回第一个参数;否则 and 返回第二个参数。析取运算 or 如果第一个参数不是 nilfalse 则返回第一个参数,否则 or 返回第二个参数。 andor 都使用截取计算,也就是,只有有必要的情况下才计算第二个参数。例如:

       10 or error()       -> 10
       nil or "a"          -> "a"
       nil and 10          -> nil
       false and error()   -> false
       false and nil       -> false
       false or nil        -> nil
       10 and 20           -> 20

2.5.4 - 串联接

在Lua中字符串连接操作符是两个点 (`..´)。如果两边的操作数都是字符或者数字,他们就都会按照 2.2.1的规则被转换成字符串。否则,将调用 "concat" 元方法(见 2.8)。

2.5.5 - 优先级

Lua中的操作符的优先级如下表所示,从低到高优先级:

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

表达式中,你可以使用括号来改变优先顺序。串联接符 (`..´) 和指数符 (`^´) 都是右结合的。其他二元操作都是左结合的。

2.5.6 - 表构造器

表构造器是创建表的表达式。当计算构造器的时候,就会创建一个新的表。构造器可以用来创建空的表,或者创建表并初始化一些字段。一般的语法如下:

	tableconstructor ::= `{´ [fieldlist] `}´
	fieldlist ::= field {fieldsep field} [fieldsep]
	field ::= `[´ exp `]´ `=´ exp | Name `=´ exp | exp
	fieldsep ::= `,´ | `;´

[exp1] = exp2 形式的每一个添加到新表中的字段条目以 exp1 为键并以 exp2 为值。name = exp 形式的字段,等同于 ["name"] = exp。最后,exp 形式的字段等同于 [i] = exp 其中 i 是连续的整数,从1开始。其它格式的字段不会影响它的计数。例如:

       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

如果列表中最后一个字段的形式是 exp 同时表达式又是一个函数调用,那么调用返回的所有值会依次进入列表(见 2.5.7)。如果要避免这种情况,在函数调用两边加上括号(见 2.5)。

字段列表可以有一个结尾的分隔符,这个对由机器生成的列表十分方便。

2.5.7 - 函数调用

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

	functioncall ::= prefixexp args

在函数调用中,首先会计算 prefixexpargs 。如果 prefixexp 的值是 function 类型,那么那个函数就会被调用,同时使用给出的参数。否则,他的 "call" 元方法就会被调用,第一个参数是 prefixexp 的值,接下来是原来的调用参数(见 2.8)。

形式

	functioncall ::= prefixexp `:´ Name args

可以用来调用“方法”("methods")。调用 v:name(...) 语法上比 v.name(v,...),要好一些,除非表达式 v 只计算一次。

参数可以有以下几种语法:

	args ::= `(´ [explist1] `)´
	args ::= tableconstructor
	args ::= Literal

所有的参数表达式都会在实际调用之前进行计算。f{...} 的调用形式在语法上较 f({...}) 要好,是因为,参数列表示一个单独的新表。 f'...' (或者 f"..." 或者 f[[...]]) 较 f('...') 要好,是因为参数列表是一个单独的字符串。

因为函数可以返回任意个结果(见 2.4.4),结果的数量必须在使用它们前进行调整。如果函数按照语句进行调用(见 2.4.6),那么它的返回列表就会被调整为零个元素,这样就舍弃了所有的返回值。如果调用函数时,他是一个表达式列表的最后一个元素,那么不会做调整(除非调用时加了括号)。

以下是一些例子:

       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() 被调整为两个结果
       a,b,c = f()        -- f() 被调整为3个结果
       return f()         -- 返回所有 f() 返回的值
       return x,y,f()     -- 建立一个表包含所有 f() 返回的值
       {f()}              -- creates a list with all values returned by f()
       {f(), nil}         -- f() 被调整为一个结果

如果你用括号括起调用的函数,那么它就会被调整为返回一个值。

       return x,y,(f())   -- returns x, y, and the first value from f()
       {(f())}            -- creates a table with exactly one element

作为Lua语法自由格式的一个例外,你不能在函数调用的 `(´ 前面加入一个换行。这个限制可以避免语言中的一些二义性。如果你写:

       a = f
       (g).x(a)

Lua会读作 a = f(g).x(a)。这样,如果你想执行为两条语句,你必须在中间加分号。如果你实际上想调用 f,你就必须删除 (g) 前面的换行。

return functioncall 的调用格式称之为 尾部调用(tail call)。Lua实现了proper tail calls;在一个尾部调用中,被调用的函数将会重新使用调用程序的栈。因此,程序执行对嵌套尾部调用的次数没有任何限制。然而,尾部调用会清楚调用函数的调试信息。注意尾部调用只有在特殊的语法中才能出现,也就是 return 只有一个函数调用作为参数,这种语法保证了调用函数确切返回被调用函数的返回值。所以,下面的例子都不是尾部调用:

  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 - 函数定义

函数定义的语法是:

	function ::= function funcbody
	funcbody ::= `(´ [parlist1] `)´ block end

下面较好的语法简化了函数定义:

	stat ::= function funcname funcbody
	stat ::= localfunction Name funcbody
	funcname ::= Name {`.´ Name} [`:´ Name]

语句

       function f () ... end

会被翻译为

       f = function () ... end

语句

       function t.a.b.c.f () ... end

会被翻译为

       t.a.b.c.f = function () ... end

语句

       local function f () ... end

会被翻译为

       local f; f = function () ... end

一个函数定义是一个可执行的表达式,他的类型为 函数(function) 。当Lua预编译语句段的时候,他的函数体也会被预编译。这样,当Lua执行函数定义的时候,函数被 实例化封装 closed)。这个函数实例(或闭包 closure)是表达式的最终结果。同一个函数的不同的实例可以引用不同的外部局部变量也可以有不同的环境表。

形式参数(代表参数的变量,简称形参)就像用实际参数值(简称实参)初始化的局部变量一样。

	parlist1 ::= namelist [`,´ `...´]
	parlist1 ::= `...´

当调用一个函数时,实参表会调整为和形参一样的长度,除非函数是 variadic 或者 变长参数函数(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 语句返回(见 2.4.4)。如果控制到达了函数尾部而没有遇到 return 语句,那么函数没有返回值。

冒号(:) 语法是用来定义 methods 的,也就是,函数有一个隐含的额外参数 self. 。这样,语句:

       function t.a.b.c:f (...) ... end

相对以下是较好的形式:

       t.a.b.c.f = function (self, ...) ... end

2.6 - 可见性规则

Lua是一个有词法范围的语言。变量的范围从声明语句后的第一个语句开始到包含声明的最内部的语句块为止。例如:

  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 尚未进入范围,所以第二个 x 指代的是外面的变量。

由于词法范围的规则,在局部变量的范围内定义的函数可以任意访问这些变量。例如:

  local counter = 0
  function inc (x)
    counter = counter + x
    return counter
  end

内部函数使用的局部变量在函数内部称之为 上值(upvalue),或者 外局部变量(external local variable)

注意每个 local 语句执行时会定义一个新的局部变量。看以下例子:

  a = {}
  local x = 20
  for i=1,10 do
    local y = 0
    a[i] = function () y=y+1; return x+y end
  end

循环产生了十个闭包(也就是,十个匿名函数的实例)。每个闭包使用不同的 y 变量,但他们共享同一个 x 变量。

2.7 - 错误处理

因为Lua是一个扩展语言,所有的Lua动作都是从宿主程序中调用Lua库中函数的C代码开始的(见 3.15)。无论错误发生在Lua编译过程时或执行时,控制返回C,然后可以做相应的处理(比如打印一个错误)。

Lua代码可以通过调用error函数来产生一个错误(见 5.1)。如果你要在Lua中捕获错误,你可以使用 pcall 函数(见 5.1)。

2.8 - 元表 (Metatables)

Lua中的每一个表和用户数据都可以拥有一个 元表(metatable)。这个 元表 是一个普通的Lua表,定义了在特定操作下原始表和用户数据的行为。你可以通过设置一个对象的元表中的特定字段来更改它某些方面的行为。例如,当一个对象是一个加法的操作数时,Lua检查它的元表中的 "__add" 字段是不是一个函数。如果是,Lua调用它来执行加法。

我们称元表中的键(字段名,key)为 事件(events) ,值为 元方法(metamethods)。在上一个例子中, "add" 是事件,执行加法的函数是元方法。

你可以通过 set/getmetatable 函数来查询和更改一个对象的元表(见 5.1)。

元表可以控制对象在算术操作、比较、串连接、索引取值中如何运行。元表也可以定义一个函数当收集内存垃圾时调用。每一个操作这里Lua都用一个特定的键关联,称之为事件。当Lua对一个表或是一个用户数据执行上面中的一个操作时,它先检查元表控制的操作已经罗列在下面。每个操作有一个相应的名称,代表了他的含义。他们在元表中的键是由名称前加上两条下划线;如,操作 "add" 的键是 "__add"。这些操作的语义

这里给出的Lua代码仅仅是说明性的;真正的行为是硬编码在解释器中的,比下面的的模拟的效率要高很多。描述中用到的函数 (rawget, tonumber, 等等) 在 5.1 中会对他们进行描述。特别地,要获得一个给定对象的元方法,我们使用这个表达式:

  metatable(obj)[event]

这个要读作:

  rawget(metatable(obj) or {}, event)

也就是,访问元方法时不会调用其它元方法,同时调用没有元表的对象不会出错(它返回一个 nil值)。

  • "add": + 加法操作。

    下面的 getbinhandler 函数定义了Lua如何给一个二元操作选择一个处理器。首先,Lua尝试第一个操作数。如果它的类型没有定义这个操作的处理器,那么然后Lua尝试第二个操作数。

     function getbinhandler (op1, op2, event)
       return metatable(op1)[event] or metatable(op2)[event]
     end
    

    利用该函数,op1 + op2 的行为方式可看作是

     function add_event (op1, op2)
       local o1, o2 = tonumber(op1), tonumber(op2)
       if o1 and o2 then  -- 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
    
  • "sub": - 操作。行为方式类似 "add" 操作。
  • "mul": * 操作。行为方式类似 "add" 操作。
  • "div": / 操作。行为方式类似 "add" 操作。
  • "pow": ^ (指数) 操作

     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
    
  • "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 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": == 操作。函数 getcomphandler 定义了Lua是如何为比较操作选择一个元方法的。只有当参与比较的两个对象属于同一类型而且需要的元方法一样时,才会选择这个元方法。

     function getcomphandler (op1, op2, event)
       if type(op1) ~= type(op2) then return nil end
       local mm1 = metatable(op1)[event]
       local mm2 = metatable(op2)[event]
       if mm1 == mm2 then return mm1 else return nil end
     end
    

    事件如下定义:

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

  • "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 h = getcomphandler(op1, op2, "__lt")
         if h then
           return h(op1, op2)
         else
           error("...");
         end
       end
     end
    
    

    a > b is equivalent to b < a.

  • "le": <= 操作。

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

  • "index": 通过索引访问 table[key]

     function gettable_event (table, key)
       local h
       if type(table) == "table" then
         local v = rawget(table, key)
         if v ~= nil then return v end
         h = metatable(table).__index
         if h == nil then return nil end
       else
         h = metatable(table).__index
         if h == nil then
           error("...");
         end
       end
       if type(h) == "function" then
         return h(table, key)      -- call the handler
       else return h[key]          -- or repeat operation on it
     end
    
  • "newindex": 给表的索引赋值 table[key] = value

     function settable_event (table, key, value)
       local h
       if type(table) == "table" then
         local v = rawget(table, key)
         if v ~= nil then rawset(table, key, value); return end
         h = metatable(table).__newindex
         if h == nil then rawset(table, key, value); return end
       else
         h = metatable(table).__newindex
         if h == nil then
           error("...");
         end
       end
       if type(h) == "function" then
         return h(table, key,value)    -- call the handler
       else h[key] = value             -- or repeat operation on it
     end
    
    
  • "call": 当Lua调用某个值时调用。

     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
    

2.9 - 垃圾收集

Lua 会自动进行内存管理。这意味着你不需要担心新对象的内存分配问题,也不需要释放不用的对象。Lua 通过不断地运行 垃圾收集器 收集 dead objects (也就是那些Lua中无法访问的对象)来自动管理内存。Lua中所有的对象都是自动管理的目标:表,用户数据,函数,线程,和字符串。Lua使用两个数字控制垃圾收集循环。一个数字表示Lua使用的动态内存的字节数,另一个是阀值。当内存字节数到达阀值时,Lua就运行垃圾收集器,来释放死对象的空间。一旦字节计数器被调整,那么阀值就会被设为字节计数器新值的两倍。

通过C API,你可以查询和更改阀值(见 3.7)。将阀值设为零时会强制立刻进行垃圾收集,同时把他设为足够大就可以停止垃圾收集。仅使用Lua代码中的 gcinfocollectgarbage 函数 (见 5.1)可以获得一定程度上对垃圾收集循环的控制。

2.9.1 - 垃圾收集元方法 (Garbage-Collection Metamethods)

使用 C API,你可以对用户数据设置一个垃圾收集元方法(见 2.8)。这些元方法也称为 终结器(finalizers)。终结器允许你用外部的资源管理来调整Lua的垃圾收集(如关闭文件,网络或数据库连接,或者释放你自己的内存。

用元表中包含 __gc 字段的自由用户数据不会立即被垃圾收集器回收。而是,Lua把它们放在一个列表中。收集完毕之后,Lua会对这个列表中的用户数据执行和以下函数相等的操作:

 function gc_event (udata)
   local h = metatable(udata).__gc
   if h then
     h(udata)
   end
 end

在每个垃圾收集过程最后,调用用户数据的终结器的顺序,将按照他们在收集过程中添加到列表中的相反顺序进行。也就是,第一个被调用的终结器是和在程序中创建的最后一个用户数据相关的那个终结器。

2.9.2 - 弱表

一个 弱表(weak table) 是一个包含的元素是 弱引用(weak references)的表。垃圾收集器会忽略弱引用。换句话说,如果指向一个对象的引用只有弱引用,那么这个对象还是要被垃圾收集器回收。

弱表可以包含弱的键,弱的值,或者两者皆有。一个包含弱键的表允许它的键被回收,但值不可以。一个同时包含弱键和弱值的表允许键和值的回收。无论哪种情况,只要键或者值中的一个被回收了,那么这一对键值将会从表中删除。这个表的弱属性是由它的元表的 __mode 字段控制的。如果 __mode 字段是一个包含字符 `k´的字符串,那么表中的键是弱键。如果 __mode 字段是一个包含字符 `v´ 的字符串,那么表中的值是弱值。

在你将表用作元表之后,你不应该更改 __mode 字段的值。否则,这个元表控制的表的弱表行为将会不确定。

2.10 - 同步程序

Lua支持同步程序,也称为 半同步程序(semi-coroutines)协同多线程(collaborative multithreading)。Lua中的一个同步程序代表了一个独立的执行线程。然而,不像在多线程系统中的线程那样,一个同步程序只有在调用了一个yield(产生结果)函数才能挂起它的执行。

你可以调用 coroutine.create 来创建一个同步程序。它唯一的一个参数是一个函数,代表同步程序的主函数。create 函数仅仅建立一个新的同步程序然后返回一个它的句柄 (一个线程 thread 类型的对象);它不会启动该同步程序。

当你第一次调用 coroutine.resume,将 coroutine.create 返回的线程对象作为第一个参数传递给它,然后同步程序就启动了,从它的主函数的第一行开始。传给 coroutine.resume 的额外的参数会作为同步程序主函数的参数传递过去。在同步程序开始执行之后,它一直运行到它结束或产生结果。

一个同步程序通过两种方式结束它的运行:正常情况下,当它的主函数返回(显式地或隐式的,在最后一个指令之后)时结束;异常地,如果有未保护的错误。第一各情况下,coroutine.resume 返回 true,加上同步程序主函数返回的其它值。在有错误的情况下,coroutine.resume 返回 false ,并附上错误信息。

一个同步程序通过调用 coroutine.yield 来产生结果。当一个同步程序产生结果,相应的 coroutine.resume 就立刻返回,即使操作发生在嵌套函数调用中(也就是,不在主函数中,而在被主函数直接或间接调用的函数中)。在这种情况下, coroutine.resume 也返回 true,以及传给 coroutine.yield。的所有参数。下次你继续同一个同步程序时,它会从它原来yield的地方继续执行,而 coroutine.yield 将返回给主程序传给 coroutine.resume 的额外参数。

coroutine.wrap 函数创建一个和 coroutine.create 一样的同步程序,但它不返回同步程序本身,而是返回一个继续同步程序的函数(当调用的时候)。传递给这个函数的参数作为继续resume的额外参数。函数将返回resume返回的所有值,出除了第一个(布尔值的错误代码)。不像 coroutine.resume,这个函数不捕获错误;出现任何错误都传回给调用者。

请考虑以下例子:

function foo1 (a)
  print("foo", a)
  return coroutine.yield(2*a)
end

co = 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       10
foo     2
main    true    4
co-body r
main    true    11      -9
co-body x       y
main    true    10      end
main    false   cannot resume dead coroutine

3 - 应用程序接口

这一节描述Lua中的C API,这是对于宿主程序可用的C函数集合,用以和Lua通讯。所有的API函数及其相关类型和常量都声明在头文件lua.h中。

即便每次我都使用“函数”这个词,任何设施在API里面都可能被一个宏所替代。所有这些宏(macro)都只使用一次它的参数(除了第一个参数、这个每次总是一个Lua状态),所以不会产生隐藏的副作用。

3.1 - 状态

Lua库是可重入的(reentrant)的:它没有全局变量。整个Lua解释器的状态(全局变量、栈、等等)储存在一个动态分配的 lua_State 结构类型中。一个指向这个状态的指针必须作为库中每一个函数的第一个参数,除了 lua_open 这个函数。该函数从最开始创建一个Lua状态。

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

       lua_State *lua_open (void);

调用 lua_close 去释放这个由 lua_open 创建的状态:

       void lua_close (lua_State *L);

这个函数销毁所有被给予Lua状态的对象(调用相应的垃圾收集元方法)并且释放那个状态使用的所有动态内存。在个别的平台上,你或许不需要调用这个函数,因为当宿主程序结束的时候会自然的释放所有的资源。另一方面,长时间运行的程序,像一些守护进程或者Web服务器,可能需要立即释放那些不需要的状态资源,以避免占用太多内存。

3.2 - 堆栈和索引

Lua使用一个来自于C语言的 虚拟栈(virtual stack) 传递值。栈里面的每一个元素都代表一个Lua值 (nil, number, string, etc.)。

只要Lua调用C语言函数,这个所调用的函数将得到一个新的栈,这个栈将独立于先前的栈以及那些仍然活跃的C函数的栈。这个栈最初包含了C函数的所有参数,并且这也会存放C函数的返回值(见 3.16)。

为了方便起见,大多数查询操作的API不需要遵守一个严格的栈定义(注:即不需要遵循FILO)。他们可以使用 索引(index) 引用任何栈中元素:一个正数索引代表了栈中的绝对位置(从1开始);一个负数索引代表了从栈顶的偏移量。更特别的是,如果栈有 n 个元素,那么索引 1 代表第一个元素(这就是说,这个元素首先入栈)并且索引 n 代表了最后一个元素;索引 -1 也代表了最后一个元素(也就是栈顶)并且索引 -n 代表了第一个元素。我们说一个索引存在于 1 和栈顶之间是有效的,换句话说,如果 1 <= abs(index) <= top

在任何时间里,你可以调用 lua_gettop 得到栈顶元素的索引:

       int lua_gettop (lua_State *L);

因为索引从 1 开始,lua_gettop 的结果等于栈中的元素数量(如果是0就意味着栈为空)。

当你与Lua API交互的时候,你有责任控制堆栈以避免溢出。。这个函数

       int lua_checkstack (lua_State *L, int extra);

使栈的大小增长为 top + extra 个元素;如果无法将栈增加到那个大小将返回false。这个函数从不对栈进行收缩;如果栈已经比新的大小更大,它将不产生任何作用那个。

只要Lua调用C 函数,它必须至少保证 LUA_MINSTACK 这个栈中的位置是可用的。LUA_MINSTACK 定义在 lua.h 中,它的值是 20,所以你不需要总担心栈空间除非你的代码通过循环将元素压入栈。

大多数插叙函数接受指向有效栈空间的索引,那就是说,索引达到栈空间的最大值是你需要使用 lua_checkstack。这样的索引称为可接受索引(acceptable indices)。更正规的说法,我们给出一个严格的定义如下:

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

注意,0永远不是一个可接受索引。

除非另外说明,任何函数接受有效索引可以被称为是 伪索引(pseudo-indices),这些索引代表一些Lua值可以被C 代码访问但是却不存在于栈中。假索引通常用于访问全局环境变量,注册表,和一个C 函数的上值(见 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,并且将该索引设置为栈顶。如果新的栈顶比旧的更大,那么新元素被填上 nil 值。如果索引为 0,那么所有栈元素会被清除。在 lua.h 里面定义了一个有用的宏

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

用以从栈中弹出 n 个元素。

lua_pushvalue 将一个索引指向的元素的拷贝压入栈。 lua_remove 删除指定位置的元素,将该元素上方的所有元素下移以填满空缺。lua_insert 将栈顶元素移动到指定位置,将该位置以上的元素上移。lua_replace 将栈顶元素移动到指定位置而不移动其他任何其他元素(因此替代了给定位置的元素的值)。所有这些函数只接受有效的索引。(你不能使用伪索引调用 lua_removelua_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_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 - 堆栈查询

下面的函数可以用来检测栈内元素的类型:

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

这些函数只能使用可接受的索引。

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。下面的函数将这些常量转换成字符串:

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

lua_is* 函数返回 1 当对象与所给类型兼容的时候,其他情况返回 0。 lua_isboolean 是一个例外:它只针对布尔值时才会成功(否则将是无用的,因为任何值都是一个布尔值)。这些函数对于无效引用返回 0。 lua_isnumber 接受数字和用数字表示的字符串;lua_isstring 接受字符串和数字(见 2.2.1);lua_isfunction 接受Lua函数和C函数; lua_isuserdata 接受完整的和轻量的用户数据。要区分C 函数和Lua 函数,你可以使用 lua_iscfunction。要区分用户数据,你可以使用 lua_islightuserdata。要区分数字还是用数字表示的字符串,你可以使用 lua_type

这些API还包含了用于比较栈中的两个值的操作:

       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_equallua_lessthan 在比较他们的副本的时候是等效的(见 2.5.2)。 lua_rawequal 用于比较基本类型但不包括元方法。如果有任何形式的无效索引,这些函数都返回 0(false)。

3.5 - 堆栈取值

为了将一个栈中的值转变为指定的C语言类型,你需要使用以下的转换函数:

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

这些函数由任何可接受索引作为参数进行调用。当遇到一个无效索引,函数表现为就好像接受了一个错误类型的值。

lua_toboolean 将索引指向的Lua值转换为C语言类型的布尔值(0 或 1)。就像所有Lua中的测试一样,任何不等于 false 或者 nil 的Lua值通过 lua_toboolean 都将返回 1;否则将返回 0。当然,如果是一个无效索引,也将返回 0。(如果你只想接受真实的布尔值,使用 lua_isboolean 去测试值的类型。)

lua_tonumber 将索引指向的Lua值转换成一个数字(默认情况下,lua_Numberdouble类型)。Lua值必须是一个数字或者可转化为数字的字符串(见 2.2.1);否则,lua_tonumber 返回 0。

lua_tostring 将索引指向的Lua值转换成字符串(const char*)。Lua值必须是一个字符串或者数字;否则,函数返回 NULL。如果值是一个数字,lua_tostring 会将栈中的真实值变成一个字符串类型。(当 lua_tostring 应用于键时这个改变将引起 lua_next 的混乱。)lua_tostring 在Lua 状态内部返回一个字符串的指针。这个字符串总是以 0('\0')结尾,就像C 语言里的一样,但是也可能包含其他 0 在其中。如果你不知道一个字符串中是否存在 0 ,你可以使用 lua_strlen 得到它的实际长度。因为Lua具有垃圾收集机制,所以不能保证 lua_tostring 返回的指针仍然有效,当相应的值从栈中删除之后。如果你在当前函数返回之后还需要这个字符串,你需要复制它并且将它存入注册表(见 3.18)。

lua_tocfunction 将栈中的值转换为C 函数。这个值必须是一个C 函数;否则,lua_tocfunction 返回 NULL。类型 lua_CFunction 在 3.16 中有详细解释。

lua_tothread 将栈中的值转换为Lua线程(被描绘成 lua_State *)。这个值必须是一个线程;否则;lua_tothread 返回 NULL

lua_topointer 将栈中?闹底晃ㄓ玫腃 语言指针(void *)。这个值可能是一个用户数据、表、线程、或者函数;否则,lua_topointer 返回 NULL。Lua保证同种类型的不同对象将返回不同指针。没有直接的方法将指针转换回原来的值。这个函数通常用于调试。

lua_touserdata 在 3.8 中有详细解释。

3.6 - 将值压入堆栈

以下的API函数将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);

这些函数接受一个C 语言值,将其转换成相应的Lua 值,并且将结果压入栈。需要特别注意的是,lua_pushlstringlua_pushstring 将对所给的字符串做一个内部拷贝。lua_pushstring 只能压入合适的C 语言字符串(也就是说,字符串要以 '\0' 结尾,并且不能包含内嵌的 0);否则,你需要使用更通用的 lua_pushlstring 函数,它可以接受一个指定的大小。

你可以压入“格式化的”字符串:

       const char *lua_pushfstring  (lua_State *L, const char *fmt, ...);
       const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);

这些函数将格式化的字符串压入栈并且返回这个字符串的指针。它们和 sprintfvsprintf 类似,但是有一些重要的不同之处:

  • 你不需要为结果分配空间:结果是Lua字符串并且Lua会关心内存分配问题(和内存释放问题,通过垃圾收集机制)。
  • 转换受到限制。这里没有标志、宽度或精度。转换操作的修饰符可以是简单的`%%´(在字符串中插入一个`%´),`%s´(插入一个没有大小限制的以 0 结尾的字符串),`%f´(插入一个 lua_Number),`%d´(插入一个 int),`%c´(插入一个 int 作为一个字符)。

这个函数

       void lua_concat (lua_State *L, int n);

连接栈顶的 n 个值,将它们弹出,并且将结果留在栈顶。如果 n 为 1,结果是单个字符串(也就是说,函数什么也不做);如果 n 是 0,结果是空字符串。连接的完成依据Lua的语义(见 2.5.4)。

3.7 - 控制垃圾收集

Lua使用两个数字控制垃圾收集循环。一个数字表示Lua使用的动态内存的字节数,另一个是阀值。(见 2.9)。一个数字表示Lua使用的动态内存的字节数,另一个是阀值。当内存字节数到达阀值时,Lua就运行垃圾收集器,来释放死对象的空间。一旦字节计数器被调整,那么阀值就会被设为字节计数器新值的两倍。

你可以通过以下的函数得到这两个量的当前值:

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

它们的返回值的单位都是千字节(K bytes)。你可以通过下面的函数改变阀值

       void  lua_setgcthreshold (lua_State *L, int newthreshold);

然后,新的阀值得单位也是千字节。当你调用这个函数,Lua设置阀新值并且和字节计数器作比较。如果新的阀值小于字节计数器,Lua将立刻运行垃圾收集器。特别是 lua_setgcthreshold(L,0) 强迫进行垃圾收集。在这之后,一个新值根据先前的规则被设置。

3.8 - 用户数据类型 (Userdata)

用户数据代表了Lua中使用的C语言值。Lua支持两种用户数据:完整用户数据(full userdata)轻量用户数据(light userdata)

一个完整用户数据代表了一块内存。它是一个对象(像一个表):你必须创建它,它有自己的元表,当它被回收的时候你可以检测到。一个完整用户数据只能与自己相等(基于原始的相等规则)。

一个轻量用户数据代表一个指针。它是?桓鲋担ㄏ褚桓鍪郑耗悴⒚挥写唇ㄋ裁挥性怼ⅲ荒鼙换厥眨ㄒ蛭游幢淮唇ǎG崃坑没菹嗟鹊奶跫侵刚胫赶虻牡刂废嗤?

在Lua 代码里,没办法测试用户数据类型是完整的还是轻量的;两者都是 用户数据类型。在C 代码里,如果是完整用户数据,lua_type 返回 LUA_TUSERDATA,反之,返回 LUA_TLIGHTUSERDATA

你可以通过下面的函数创建完整用户数据:

       void *lua_newuserdata (lua_State *L, size_t size);

这个函数根据指定大小分配一个内存块,将用户数据的地址压入栈并且返回这个地址。

要将轻量用户数据压入栈,你需要使用 lua_pushlightuserdata(见 3.6)。

lua_touserdata (见 3.5)用来取回用户数据的值。当你用在完整用户数据的时候,它返回这个块的地址,当你用在轻量用户数据的时候,它返回它的指针,当你用在非用数据的时候,返回 NULL

当Lua回收一个完整用户数据,它调用该用户数据的 gc 元方法,然后释放该用户数据相应的内存。

3.9 - 元表 (Metatables)

下面的函数允许你操作对象的元表:

       int lua_getmetatable (lua_State *L, int index);
       int lua_setmetatable (lua_State *L, int index);

lua_getmetatable 将所给对象的元表压入栈。如果索引无效,或这个对象不含有元表,该函数返回 0 并且不对栈进行任何操作。

lua_setmetatable 从栈中弹出一张表并且为所给对象设置一个新的元表。当无法给所给对象设置元表的时候该函数返回 0(也就是说,这个对象既不是一个用户数据也不是一张表);尽管那样,它仍从栈中弹出这张表。

3.10 - 加载Lua语句段

你可以通过 lua_load 加载一个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 --- 没有错误
  • LUA_ERRSYNTAX --- 预编译时句法错误
  • LUA_ERRMEM --- 内存分配错误

如果没有错误,lua_load 将编译过的语句段作为Lua 函数压入栈顶。否则,它将压入一个错误信息。

lua_load 自动检测语句段的类型是文本还是二进制数据,并且根据类型将其载入(见程序 luac)。

lua_load 使用一个用户提供的 reader 函数读取语句段的内容。当需要调用其它段时,lua_load 调用 reader,传递其 data 参数。必须返回指向语句段所在的新内存块的指针,并将段大小设置为 0。为了标志块尾,reader 必须返回 NULL。reader 函数可以返回任何大于零的值。

在当前的实现中,reader 函数不能调用任何Lua 函数;为了保证这一点,它总是会得到为 NULL 的Lua状态。

语句段名(chunkname) 用于错误信息和调试信息(见 4)。

参考辅助库 (lauxlib.c) 了解如何使用 lua_load 以及如何使用现成的函数从文件和字符串中加载语句段。

3.11 - 表操作

通过调用以下函数可以创建表:

       void lua_newtable (lua_State *L);

这个函数创建一张新的空表,并将其压入栈。

要从栈中的表里读取值,使用:

       void lua_gettable (lua_State *L, int index);

index 代表表的位置。lua_gettable 从栈中弹出一个键,并且返回该键对应的值,表仍然留在堆栈中。在Lua中,这个函数可能触发一个针对 index 事件的元方法(见 2.8)。想要在不调用任何元方法的情况下得到表主键所对应的真实值,使用这个原始(raw)版本:

       void lua_rawget (lua_State *L, int index);

要将一个值储存到栈中的一张表中,你需要将键压入栈,再将值压入栈,调用:

       void lua_settable (lua_State *L, int index);

index 代表表的位置。lua_settable 从栈中弹出主键和值。表仍然留在栈中。在Lua中,这个操作可能触发针对 settable 或者 newindex 事件的元方法。想要不受这些元方法的影响并且为任意表设置值,使用这个原始(raw)版本:

       void lua_rawset (lua_State *L, int index);

你可以通过这个函数遍历一张表:

       int lua_next (lua_State *L, int index);

index 指向需要被遍历的表。这个函数从堆栈中弹出一个键,从表中取一对键-值压入栈(所给键的下一对)。如果没有更多的元素,lua_next 返回 0(对栈不进行操作)。使用一个 nil 键标示遍历的开始。

一个典型的遍历操作看起来像这样:

       /* 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 */
       }

当遍历一张表的时候,不要在键上直接调用 lua_tostring,除非你知道这个键确实是一个字符串。再次调用 lua_tostring 改变了所给索引指向的值;这使 lua_next 的调用发生混乱。

3.12 - 环境变量操作 (Manipulating Environments)

所有的全局变量保存在普通的Lua 表中,叫做环境变量。初始的环境变量被称作全局环境变量。这张表总是在 LUA_GLOBALSINDEX 这个伪索引处。

要访问或改变全局变量的值,你可以对环境变量表使用常规的表操作。举个例子,存取一个全局变量的值:

       lua_pushstring(L, varname);
       lua_gettable(L, LUA_GLOBALSINDEX);

你可以改变一个Lua 线程的全局环境变量通过 lua_replace 函数。

以下的函数提供获取、设置Lua函数的环境变量的功能:

       void lua_getfenv (lua_State *L, int index);
       int  lua_setfenv (lua_State *L, int index);

lua_getfenv 将堆栈中 index 索引指向的函数的环境变量表压入栈。如果函数是一个C 函数,lua_getfenv 将全局环境变量压入栈。lua_setfenv 从栈中弹出一张表并且将其设置为栈中 index 索引处的函数的新环境变量。如果给定索引处的对象不是一个Lua 函数,lua_setfenv 返回 0。

3.13 - 将表作为数组使用 Using Tables as Arrays

有一些 API 能够帮助我们将Lua 表作为数组使用,也就是说,表只由数字作为索引:

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

lua_rawgeti 将表中的第 n 个元素放入堆栈中的指定位置 indexlua_rawseti 将堆栈中指定位置 index 处的表中的第 n 个元素的值设定为栈顶的值,并将原来的值从栈中删除。

3.14 - 调用函数

定义在Lua 中的函数和C语言函数经过注册就可以被宿主程序调用。这些调用必须遵循以下协议:首先,被调用的函数被压入栈;然后,函数的参数必须顺序(direct order)输入,也就是说,第一个参数需要被第一个输入。最后,函数通过下面的方法调用:

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

nargs 是你压入栈的参数的数量。所有参数和函数值从堆栈中弹出,并且函数结果被压入栈。返回值的数量被调整为 nresults,除非 nresultsLUA_MULTRET。在那种情况下,所有函数结果都被压入栈。Lua 会检测返回值是否适合栈空间。函数返回值按顺序被压入栈(第一个返回值首先入栈),所以调用结束后最后一个返回值在栈顶。

下面的例子展示宿主程序如何可以和这个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 */

注意上面的代码是“平衡的”:在它结束时,堆栈返回原来的配置。这个被认为是良好的编程实践。

(为了展示细节,我们只用Lua 提供的原始 API 完成这个例子。通常程序员定义并使用几个宏和辅助库函数在Lua 中提供高级存取功能。请参考例子中标准库函数的源代码。)

3.15 - 受保护调用 Protected Calls

当你通过 lua_call 调用一个函数,所调用函数内部产生的错误将向上传递(通过一个 longjmp)。如果你需要处理错误,你应该使用 lua_pcall

       int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);

nargsnresultslua_call 中有相同的意义。如果调用过程中没有错误,lua_pcall 的行为非常像 lua_call 。然而,如果有错误,lua_call 会捕获它,将一个单一值(错误信息)压入栈,并且返回错误代码。像 lua_calllua_pcall 总是从栈中删除函数和它的参数。

如果 errfunc 是 0,所返回的错误信息就是原始的错误信息。另外,errfunc 给出一个指向错误处理函数(error handler function)的栈索引。(在当前的实现中,索引不能为伪索引。)假设运行时错误,函数将和错误信息一起被调用,并且他的返回值将是 lua_pcall 返回的信息。

错误处理函数被用来为错误信息增加更多的调试信息,例如栈的记录。这样的信息在 lua_pcall 调用返回后将不能被收集,因此栈已经被解开了。

如果 lua_pcall 函数调用成功返回 0,否则返回以下的一个错误代码(定义在 lua.h):

  • LUA_ERRRUN --- 运行时错误
  • LUA_ERRMEM --- 内存分配错误。这样的错误下,Lua 不调用错误处理函数
  • LUA_ERRERR --- 运行错误处理函数时发生的错误

3.16 - 定义C 函数

Lua可以通过C 语言写的函数进行扩展,这些函数必须是 lua_CFunction 类型的,作为以下定义:

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

一个C 函数接收一个Lua 状态并且返回一个整数,数值需要返回给Lua。

为了正确的和Lua 通讯,C 函数必须遵循以下协议,它定义了参数和返回值传递的方法:一个C 函数在它的堆栈中从Lua获取顺序(第一个参数首先入栈)参数。所以,当函数开始时,第一个参数在索引位置 1。为了将返回值传递给Lua,一个C 函数将它们顺序压入栈,并且返回它们的数量。任何在堆栈中位于返回值以下的值都将被Lua 适当的解除。就像Lua 函数一样,一个C 函数被Lua 调用也可以返回很多结果。

作为一个例子,下面的函数接收一个任意数量的数字参数并且返回它们的平均值和总合:

       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 函数:

       #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 中的函数名和一个指向函数的指针。这样,上面的C 函数foo可以在Lua中被注册为 average 并被调用。

       lua_register(L, "average", foo);

3.17 - 定义C 函数关闭 Defining C Closures

当一个C 函数被创建后,它可以与一些值关联,这样创建了一个 C 闭包(C closure);这些值可以被随时被函数访问。为了使值和C 函数关联,首先这些值要被压入栈(有多个值时,第一个值先入),然后这个函数

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

被用来将C 函数压入栈,通过参数 n 告知应该有多少个值和该函数关联(lua_pushcclosure 将这些值从堆栈中弹出);事实上,这个宏 lua_pushcfunction 被定义作为 lua_pushcfunctionn 设置为 0。

然后,无论何时C 函数被调用,那些值被定为于指定的伪索引处。那些伪索引有一个宏 lua_upvalueindex 产生。第一个和函数关联的值在 lua_upvalueindex(1) 处,其他的以此类推。当 n 比当前函数的上值大时,lua_upvalueindex(n) 会产生一个可接受的索引(但是无效)。

C语言函数和关闭的例子,请参考Lua官方发行版中的标准库(src/lib/*.c)。

3.18 - 注册表 Registry

Lua提供了一个注册表,一张可以被所有C 代码用来储存任何需要储存的Lua值的预定义表,特别是如果C 代码需要维护C 函数以外存活的Lua值。这张表总是位于 LUA_REGISTRYINDEX 这个为索引处。任何C 语言库可以将数据储存在这张表中,只要它选择的键和其他库不同。典型的做法是你应该使用字符串作为主键包含你的库名或者在你的代码中使用一个包含C 对象地址的轻量用户数据。

在注册表中的整数键被引用机制所使用,由辅助库实现,因此不应该被用作其它用途。

3.19 - C 中的错误处理 Error Handling in C

总的来说,Lua使用C longjmp 机制来处理错误。当Lua面对任何错误(例如内存分配错误,类型错误,句法错误)它 升起(raises) 一个错误,也就是说,它做了一个长跳跃。一个受保护的环境使用 setjmp 设置一个恢复点;任何错误跳至最近最活跃的恢复点。

如果错误发生在任何受保护的环境,Lua调用 panic 函数 并且随后调用exit(EXIT_FAILURE)。你可以将panic 函数变为以下内容。

       lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);

你的新panic 函数可以避免程序因为没有返回(例如通过一个长跳跃)而退出。否则,相应的Lua 状态将不一致;唯一安全的操作就是关闭它。

几乎所有的API 函数都可能引起错误,例如导致一个内存分配错误。:lua_open, lua_close, lua_loadlua_pcall 这些的函数运行在保护模式下(也就是说,它们创建了一个受保护的环境并在其中运行),所以它们从不会引起错误。

有另外一个函数将所给的C 函数运行在保护模式下:

       int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);

lua_cpcall 在保护模式下调用 funcfunc 由一个包含 ud 的轻量用户数据开始。在错误问题上,lua_cpcalllua_pcall 一样返回相同的错误代码(见 3.15),加上在栈顶的一个错误对象;否则,返回 0,并且不对堆栈进行任何操作。任何由 func 返回的值都被丢弃。

C 代码可以通过调用下面的函数产生一个Lua错误:

       void lua_error (lua_State *L);

错误信息(实际上可以是任何类型的对象)必须在栈顶。这个函数进行一个长跳跃,因此从来不会返回。

3.20 - 线程

Lua 提供了操作线程的部分支?帧H绻阌卸嘞叱滩僮鞯腃 语言库,那么Lua能够与其协作并且在Lua中实现相同的机制。同样,Lua在线程之上实现自己的协同程序系统。以下函数用来在Lua中创建一个线程:

       lua_State *lua_newthread (lua_State *L);

这个函数将线程压入栈并且返回代表新线程的 lua_State 指针。这个返回的新状态与所有全局对象(例如表)共享初始状态,但是有一个独立的运行时堆栈。

每个线程都有自己独立的全局环境表。当你创建一个线程,这张表就和所给状态一样,但是你可以独自更改它们。

没有明确的函数可以关闭或者销毁一个线程。线程和其他Lua对象一样受垃圾收集程序的支配:

要像协同程序一样操作线程,Lua提供了以下函数:

       int lua_resume (lua_State *L, int narg);
       int lua_yield  (lua_State *L, int nresults);

你需要创建一个线程以便启动协同程序;然后你将函数体和事件参数压入堆栈;然后调用 lua_resumenarg 的值代表参数的数量。当同步程序暂停或者结束执行,函数将返回。当它返回后,栈中包含的所有值传递给 lua_yield,或者有主体函数返回。如果同步程序运行无误,lua_resume 返回 0,否则返回一个错误代码(见 3.15)。对于错误,堆栈只包含错误信息。要重起同步程序,将作为结果传递给 yield 的值压入堆栈,并且调用 lua_resume

lua_yield 函数只能像C 函数的返回表达式一样被调用,就像下面所展示的:

       return lua_yield (L, nresults);

如果C 函数像这样调用 lua_yield,正在运行的同步程序暂停它的执行,并且调用 lua_resume 开始让这个协同程序返回。nresults 这个参数代表了在堆栈中作为结果传递给 lua_resume 的值的数量。

要在不同线程中交换值,你可以使用 lua_xmove

       void lua_xmove (lua_State *from, lua_State *to, int n);

它从堆栈 from 中弹出 n 个值,并将其压入堆栈 to

4 - 调试接口 The Debug Interface

Lua 没有内置的调试设施。它使用一种特殊的接口,这种接口依赖函数和 钩子(hooks)。该接口允许构造不同种类的调试器,分析器以及其他工具用以从解释器得到所需的信息。

4.1 - 堆栈及函数信息 Stack and Function Information

得到解释程序运行时堆栈信息的主要函数是:

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

这个函数用一个指定等级的函数的 activation record 的标示符填充一个 lua_Debug 结构,等级 0 是当前运行函数,然而等级 n+1 是在等级 n 上调用的函数。当没有错误发生时,lua_getstack 返回 1;当在比栈更深的等级上调用的时候,它返回 0;

lua_Debug 结构被用来携带一个处于活动状态的函数的各种信息:

      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 只填充结构的私有部分以备之后使用。要填充 lua_Debug 其他有用信息,调用

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

这个函数发生错误是返回 0 (举个例子,一个无效的 what 选项)。what 字符串中的每个字符选择填充一些 ar 结构的字段,把上面在 lua_Debug 定义中用圆括号括起来的字母作为指示: `S´ 填充在 source, linedefinedwhat 字段中;`l´ 填充在 currentline 字段中,等等...。而且,`f´ 将正在运?性谒燃渡系暮谷攵颜弧?

想要从一个不处于活动状态的函数那得到信息(就是不在栈上的函数),你需要将其压入栈并且用 >´ 作为 what 字符串的开始。举个例子,要知道函数 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 如果函数在一个字符串中定义,那么 source 就是那个字符串。如果函数定义在一个文件中,source 开始于一个 `@´ 后面跟随文件名。
  • short_src 一个可打印版的 source,用于错误信息。
  • linedefined 函数定义起始的行号。
  • what 如果这是一个Lua函数,显示 "Lua" 字符串, "C" 为C 函数,"main" 如果这是一个语句段的main部分,"tail" 如果这是一个做了尾部调用的函数。在后面的情况里,Lua 没有其他关于这个函部的信息。
  • currentline 代表当前函数执行到的行数。如果没有行信息可用,currentline 被设置为 -1
  • name 一个所给函数合理的函数名。因为函数在Lua中属于第一类值,它们没有固定的名字:一些函数可能是多个全局变量的值,其他的可能只储存在一个表字段里。lua_getinfo 函数检测函数如何被调用或者是否为一个全局变量的值以寻找一个合适的名字。如果找不到合适的名字,name 被设置为 NULL
  • namewhat name 字段的解释。根据函数如何被调用,namewhat 的值可以是 "global", "local", "method", "field" 或者 "" (空字符串)。(当没有其他可选项的时候Lua使用空字符串代替)
  • nups 函数上值的数量。

4.2 - 操作局部变量和上值 Manipulating Local Variables and Upvalues

为了更多的操作局部变量和上值,调试接口使用索引:第一个参数或者局部变量索引为 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 调用填充的有效的激活记录或者作为一个钩子函数的参数(见 4.3)。lua_getlocal 获得一个局部变量的索引 n,将变量的值压入栈,并且返回变量名。lua_setlocal 从栈顶分配一个值给变量并且返回变量名。当索引超过活动的局部变量的数量时,两个函数都返回 NULL

以下的函部可以操作所给函数的上值(不像局部变量,函数的上值即使在函数不处于活动状态的时候都可以被访问):

       const char *lua_getupvalue (lua_State *L, int funcindex, int n);
       const char *lua_setupvalue (lua_State *L, int funcindex, int n);

这些函数可以作为Lua 函数使用也可以作为C 函数使用。(作为Lua 函数,上值是函数外部使用的局部变量,因此它被包含在函数闭包中。)funcindex 指向栈中的一个函数。lua_getupvalue 得到一个上值的索引 n,将上值的值压入栈,并返回其变量名。lua_setupvalue 从栈顶分配一个值给上值并返回变量名。当索引大于上值数量时,两个函数都返回 NULL。对于C 函数来说,这些函数使用空字符串作为所有上值的变量名。

作为一个例子,下面的函数列举了所给等级的栈中的函数的所有局部变量名和上值变量名:

       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 functions that are called during the program execution. A hook may be called in four 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 about to start the execution of a new line of code, or when it jumps back in the code (even to the same line). (This event only happens while Lua is executing 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.

5 - 标准库

The standard libraries provide useful functions that are implemented directly through the C API. Some of these functions provide essential services to the language (e.g., type and getmetatable); others provide access to "outside" services (e.g., I/O); and others could be implemented in Lua itself, but are quite useful or have critical performance to deserve an implementation in C (e.g., sort).

All libraries are implemented through the official C API and are provided as separate C modules. Currently, Lua has the following standard libraries:

  • 基本库 basic library;
  • 字符串操作 string manipulation;
  • 表操作 table manipulation;
  • 数学函数 (sin, log 等等)mathematical functions (sin, log, etc.);
  • 输入输出 input and output;
  • 操作系统机制 operating system facilities;
  • 调试机制 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 - 基本函数 Basic Functions

The basic library provides some core functions to Lua. If you do not include this library in your application, you should check carefully whether you need to provide some alternative implementation for some of its 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 smaller than 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).

error (message [, level])

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.

getmetatable (object)

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 Lua is 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 no errors, 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 only available on some platforms (Windows, Linux, Solaris, BSD, plus other Unix systems that support the dlfcn standard).

loadstring (string [, chunkname])

Loads a string as a Lua chunk (without running it). If there are no errors, 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 first argument 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.

pcall (f, arg1, arg2, ...)

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 tostring function to convert them to strings. This function is not intended for formatted output, but only as a quick way to show a value, typically for 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 require returns 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.

setfenv (f, table)

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 the numeral. The base may be any integer between 2 and 36, inclusive. In bases 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 a reasonable 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.

type (v)

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 containing the 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 err function with the original error object, 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, 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. f must be a Lua function. Returns a function that resumes the coroutine each time it is called. Any arguments passed to the function behave as the 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 coroutine cannot be running neither a C function, nor a metamethod, nor an iterator. 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. When indexing a string in Lua, the first character is at position 1 (not at 0, as in C). Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the string. Thus, the last character 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 to the number of arguments, in which each character has the internal numerical 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 all uppercase letters changed to lowercase. All other characters are left unchanged. The definition of what is an uppercase letter depends on the current 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 all lowercase letters changed to uppercase. All other characters are left unchanged. The definition of what is a lowercase letter depends on the current locale.

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

Returns a formatted version of its variable number of arguments following the description given in its first argument (which must be a string). 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 q option formats a string in a form suitable to be safely read back by the Lua interpreter: The string is written between double quotes, and all double quotes, newlines, and backslashes in the string are correctly 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
  end

string.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 called every time a match occurs, with all captured substrings passed as arguments, in order; if the pattern specifies no captures, then the whole match is passed as a sole argument. If the value returned by this function 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 `*´, which matches 0 or more repetitions of characters in the class. These repetition items will always match the longest possible sequence;
  • a single character class followed by `+´, which matches 1 or more repetitions of characters in the class. These repetition 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 expressions 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 table represents an array or a list. For those functions, an important concept 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.

table.getn (table)

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 comp is 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. Returns the 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 the standard C math library. (Some have slightly different names.) It provides 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 only interfaces to the corresponding functions in the C library. All trigonometric functions work in radians (previous versions of Lua used degrees). 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 are operations to set a default input file and a default output file, and all input/output operations are over those default files. The second style 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 text mode), and sets its handle as the default input file. When called with a file handle, it simply sets that file handle as the default input file. When called without parameters, it returns the current default input 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 iterator function that, each time it is called, returns a new line from the file. 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 the end, 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 a string (or a number) with the characters read, or nil if it cannot read data with the specified format. When called without formats, it uses a default format that reads the entire next line (see below).

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 time representing the date and time specified by the given table. This table must 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 your system. In Posix, Windows, and some other systems, this number counts the number of seconds since some given start time (the "epoch"). In other systems, the meaning is not specified, and the number returned by time 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 temporary file. The file must be explicitly opened before its use and removed when no longer needed.

This function is equivalent to the tmpnam C function, and many people (and even some compilers!) advise against its use, because between the time you call this function and the time you open the file, it is possible for another process to create a file with the same name.

5.8 - The Reflexive Debug Interface

The debug library provides the functionality of the debug interface to Lua programs. You should exert care when using this library. The functions provided here should be used exclusively for debugging and similar tasks, such as profiling. Please resist the temptation to use them as a usual programming tool: They can be very slow. 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 that the user enters. Using simple commands and other debug facilities, the user can inspect global and local variables, change their values, evaluate expressions, 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 hook function, the current hook mask, and the current hook count (as set by the debug.sethook function).

debug.getinfo (function [, what])

This function returns a table with information about a function. You can give the function directly, or you can give a number as the value of 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 expression 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 level of the stack. (The first parameter or local variable has index 1, and so 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.

6 - Lua 独立程序 Lua Stand-alone

尽管Lua被设计为一种内嵌于C 语言宿主程序中的扩展语言,它还是经常被用作一个独立程序语言。一个Lua的解释程序将Lua作为一个独立的语言,我们称之为简化的 lua,它提供了标准的发行版本。独立的解释器包含了所有标准库加上反射性的调试接口。它的使用方式如下:

      lua [options] [script [args]]

options 可以是以下内容:

  • - 标准输入(stdin) 当作文件执行;
  • -e stat 执行字符串 stat
  • -l file “需要”file 文件;
  • -i 在运行脚本后进入交互模式;
  • -v 打印版本信息;
  • -- 停止处理选项。

在停止处理选项后,lua 运行所给的脚本,传递所给参数 args。当无参数调用时,lua 就像 stdin 是程序的终结时 lua -v -i 所表现的一样,而且还与 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 n gets the number of arguments after the script name. Any arguments before 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.

在交互模式中,如果你写入了一个不完整的语句,解释器将等待你的完成。

If the global variable _PROMPT is defined as a string, then its value is used as the prompt. Therefore, the prompt can be 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系统中,Lua脚本可以用 chmod +x 将其变成可执行程序,并且通过 #! 形式,例如

#!/usr/local/bin/lua

(当然,Lua解释器的位置可能有所不同,如果 lua 在你的 PATH 中,那么

#!/usr/bin/env lua

就是一个更通用的解决方案。)

致谢

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 of omitting several names, we also thank the following individuals for supporting, contributing to, and spreading the word about Lua: Alan Watson. André Clinio, André Costa, Antonio Scuri, Asko Kauppi, Bret Mogilefsky, 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, Norman Ramsey, Philippe Lhoste, Renata Ratton, Renato Borges, Renato Cerqueira, Reuben Thomas, Stephan Herrmann, Steve Dekorte, Thatcher Ulrich, Tomás Gorham, Vincent Penquerc'h. Thank you!


与以前版本的不兼容性 Incompatibilities with Previous Versions

Lua 5.0 是一个主版本,所有与 Lua 4.0 有一些地方不兼容。

与 v4.0 的不兼容性 Incompatibilities with version 4.0

语言上的变动

  • 整个标签方法模式被元表所替代。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 expression 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 上的改动

  • lua_open 不再需要堆栈大小作为参数(堆栈是动态的)。
  • lua_pushuserdata 已经被废除了。使用 lua_newuserdatalua_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

转载于:https://www.cnblogs.com/lancidie/archive/2010/09/24/1833939.html

你可能感兴趣的:(lua,shell,嵌入式)