1、Lua 是一个大小写敏感的语言: and 是一个保留字,但是 And 和 AND 则是两个不同的合法的名字。一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。下面这些是其它的 token : + - * / % # = = = = ( ) ; : , . . .字符串既可以用一对单引号引起,也可以是双引号,里面还可以包含类似 C 的转义符: a (响铃), b (退格), f (表单), n (换行), r (回车), t (横向制表), v (纵向制表), (反斜杠), (双引号),以及 (单引号)。而且,如果在一个反斜杠后跟了一个真正的
2、换行符,其结果就是在字符串中产生一个换行符。我们还可以用反斜杠加数字的形式 ddd 来描述一个字符。这里, ddd 是一串最多三位的十进制数字。(注意,如果需要在这种描述方法后接一个是数字的字符,那么反斜杠后必须写满三个数字。)Lua 中的字符串可以包含任何 8 位的值。包括用 0 表示的零。只有在你需要把不同的引号、换行、反斜杠、或是零结束符这些字符置入字符串时,你才必须使用转义符。别的任何字符都可以直接写在文本里。(一些控制符可以会影响文件系统造成某些问题,但是不会引起 Lua 的任何问题。) 字符串还可以用一种长括号括起来的方式定义。我们把两个正的方括号间插入 n 个等号定义为第 n 级
3、正长括号。就是说,0 级正的长括号写作 ,一级正的长括号写作 = ,如此等等。反的长扩展也作类似定义;举个例子,4 级反的长括号写作 = 。一个长字符串可以由任何一级的正的长括号开始,而由第一个碰到的同级反的长括号结束。整个词法分析过程将不受分行限制,不处理任何转意符,并且忽略掉任何不同级别的长括号。这种方式描述的字符串可以包含任何东西,当然特定级别的反长括号除外。另一个约定是,当正的长括号后面立即跟了一个换行符,这个换行符就不包含在这个字符串内。举个例子,假设一个系统使用 ASCII 码(这时,a 编码为 97 ,换行符编码为 10 ,1 编码为 49 ),下面五种方式描述了完全相同的字符串
4、: a = alon123 a = alon12397lo1004923 a = alo 123 a = = alo=数字常量可以分两部分写,十进制底数部分和十进制的指数部分。指数部分是可选的。 Lua 也支持十六进制整数常量,只需要在前面加上前缀 0x 。下面是一些合法的数字常量的例子: 3 3.0 3.1416 314.16e-2 0.31416E1 0xff 0x56注释可以在除字符串内的任何地方是以两横 (-) 开始。如果跟在两横后面的不是一个长括号,这就是一个短注释,它的作用范围直到行末;否则就是一个长注释,其作用范围直到遇到反的长括号。长注释通常被用来临时屏蔽代码块。2.2 - 值
5、与类型Lua 是一种 动态类型语言。这意味着变量没有类型,只有值才有类型。语言中不存在类型定义。而所有的值本身携带它们自己的类型信息。Lua 中的所有值都是一致 (first-class) 的。这意味着所有的值都可以被放在变量里,当作参数传递到另一个函数中,并被函数作为结果返回。Lua 中有八种基本类型: nil, boolean, number, string, function, userdata, thread, and table. Nil 类型只有一种值 nil ,它的主要用途用于标表识和别的任何值的差异;通常,当需要描述一个无意义的值时会用到它。 Boolean 类型只有两种值:f
6、alse 和 true。 nil 和 false 都能导致条件为假;而另外所有的值都被当作真。 Number 表示实数(双精度浮点数)。(编译一个其它内部数字类型的 Lua 解释器是件很容易的事;比如把内部数字类型改作单精度浮点数或长整型。参见文件 luaconf.h 。) String 表示一串字符的数组。 Lua 是 8-bit clean 的:字符串可以包含任何 8 位字符,包括零结束符 () (参见 2.1)。Lua 可以调用(和处理)用 Lua 写的函数以及用 C 写的函数(参见 2.5.8). userdata 类型用来将任意 C 数据保存在 Lua 变量中。这个类型相当于一块原生
7、的内存,除了赋值和相同性判断,Lua 没有为之预定义任何操作。然而,通过使用 metatable (元表) ,程序员可以为 userdata 自定义一组操作(参见 2.8)。 userdata 不能在 Lua 中创建出来,也不能在 Lua 中修改。这样的操作只能通过 C API。这一点保证了宿主程序完全掌管其中的数据。thread 类型用来区别独立的执行线程,它被用来实现 coroutine (协同例程)(参见 2.11)。不要把 Lua 线程跟操作系统的线程搞混。 Lua 可以在所有的系统上提供对 coroutine 的支持,即使系统并不支持线程。table 类型实现了一个关联数组。也就是说
8、,数组可以用任何东西(除了nil)做索引,而不限于数字。 table 可以以不同类型的值构成;它可以包含所有的类型的值(除 nil 外)。 table 是 lua 中唯一的一种数据结构;它可以用来描述原始的数组、符号表、集合、记录、图、树、等等。用于表述记录时,lua 使用域名作为索引。语言本身采用一种语法糖,支持以 a.name 的形式表示 aname。有很多形式用于在 lua 中创建一个 table (参见 2.5.7)。跟索引一样, table 每个域中的值也可以是任何类型(除 nil外)。特别的,因为函数本身也是值,所以 table 的域中也可以放函数。这样 table 中就可以有一些
9、 methods 了 (参见see 2.5.9)。table, function ,thread ,和 (full) userdata 这些类型的值是所谓的对象:变量本身并不会真正的存放它们的值,而只是放了一个对对象的引用。赋值,参数传递,函数返回,都是对这些对象的引用进行操作;这些操作不会做暗地里做任何性质的拷贝。库函数 type 可以返回一个描述给定值的类型的字符串。2.2.1 - 强制转换Lua 提供运行时字符串到数字的自动转换。任何对字符串的数学运算操作都会尝试用一般的转换规则把这个字符串转换成一个数字。相反,无论何时,一个数字需要作为字符串来使用时,数字都会以合理的格式转换为字符串。
10、需要完全控制数字怎样转换为字符串,可以使用字符串库中的 format 函数(参见 string.format)。2.3 - 变量写上变量的地方意味着当以其保存的值来替代之。 Lua 中有三类变量:全局变量,局部变量,还有 table 的域。一个单一的名字可以表示一个全局变量,也可以表示一个局部变量 (或者是一个函数的参数,这是一种特殊形式的局部变量): var := NameName 就是 2.1 中所定义的标识符。任何变量都被假定为全局变量,除非显式的以 local 修饰定义 (参见 2.4.7)。局部变量有其作用范围:局部变量可以被定义在它作用范围中的函数自由使用(参见 2.6)。在变量的
11、首次赋值之前,变量的值均为 nil。方括号被用来对 table 作索引:= prefixexp exp 对全局变量以及 table 域之访问的含义可以通过 metatable 来改变。以取一个变量下标指向的量 ti 等价于调用 gettable_event(t,i)。(参见 2.8 ,有一份完整的关于 gettable_event 函数的说明。这个函数并没有在 lua 中定义出来,也不能在 lua 中调用。这里我们把它列出来只是方便说明。var.Name 这种语法只是一个语法糖,用来表示 varName:= prefixexp . Name所有的全局变量都是放在一个特定 lua table 的
12、诸个域中,这个特定的 table 叫作 environment (环境)table 或者简称为 环境 (参见 2.9)。每个函数都有对一个环境的引用,所以一个函数中可见的所有全局变量都放在这个函数所引用的环境表(environment table)中。当一个函数被创建出来,它会从创建它的函数中继承其环境,你可以调用 getfenv 取得其环境。如果想改变环境,可以调用 setfenv。(对于 C 函数,你只能通过 debug 库来改变其环境;参见 5.9)。对一个全局变量 x 的访问等价于 _env.x,而这又可以等价于 gettable_event(_env, x)这里,_env 是当前运行
13、的函数的环境。(函数 gettable_event 的完整说明参见 2.8。这个函数并没有在 lua 中定义出来,也不能调用。当然,_env 这个变量也同样没有在 Lua 中定义出来。我们在这里使用它们,仅仅只是方便解释而已。2.4 - 语句段(Statement)Lua 支持惯例形式的语句段,它和 Pascal 或是 C 很相象。这个集合包括赋值,控制结构,函数调用,还有变量声明。2.4.1 - Chunk(语句组)Lua 的一个执行单元被称作 chunk。一个 chunk 就是一串语句段,它们会被循序的执行。每个语句段可以以一个分号结束: chunk := stat ;这儿不允许有空的语句
14、段,所以 ; 是非法的。lua 把一个 chunk 当作一个拥有不定参数的匿名函数(参见 2.5.9)处理。正是这样,chunk 内可以定义局部变量,接收参数,并且返回值。chunk 可以被保存在一个文件中,也可以保存在宿主程序的一个字符串中。当一个 chunk 被执行,首先它会被预编译成虚拟机中的指令序列,然后被虚拟机解释运行这些指令。chunk 也可以被预编译成二进制形式;细节参考程序 luac。用源码形式提供的程序和被编译过的二进制形式的程序是可以相互替换的; Lua 会自动识别文件类型并做正确的处理。2.4.2 - 语句块语句块是一列语句段;从语法上来说,一个语句块跟一个 chunk
15、相同: block := chunk一个语句块可以被显式的写成一个单独的语句段: stat := do block end显式的语句块对于控制变量的作用范围很有用。有时候,显式的语句块被用来在另一个语句块中插入 return 或是 break (参见 2.4.4)。2.4.3 - 赋值 先映射后操作Lua 允许多重赋值。因此,赋值的语法定义是等号左边放一系列变量,而等号右边放一系列的表达式。两边的元素都用逗号间开:= varlist1 = explist1 varlist1 := var , var explist1 := exp , exp表达式放在 2.5 里讨论。在作赋值操作之前,那一系
16、列的右值会被对齐到左边变量需要的个数。如果右值比需要的更多的话,多余的值就被扔掉。如果右值的数量不够需求,将会按所需扩展若干个 nil。如果表达式列表以一个函数调用结束,这个函数所返回的所有值都会在对齐操作之前被置入右值序列中。(除非这个函数调用被用括号括了起来;2.5)。赋值段首先会做运算完所有的表达式,然后仅仅做赋值操作。因此,下面这段代码 i = 3 i, ai = i+1, 20会把 a3 设置为 20,而不会影响到 a4 。这是因为 ai 中的 i 在被赋值为 4 之前就被拿出来了(那时是 3 )。简单说 ,这样一行 x, y = y, x可以用来交换 x 和 y 中的值。对全局变量
17、以及 table 中的域的赋值操作的含义可以通过 metatable 来改变。对变量下标指向的赋值,即 ti = val 等价于 settable_event(t,i,val)。(关于函数 settable_event 的详细说明,参见 这个函数并没有在 Lua 中定义出来,也不可以被调用。这里我们列出来,仅仅出于方便解释的目的) 对于全局变量的赋值 x = val 等价于 _env.x = val,这个又可以等价于 settable_event(_env, , val)这里,_env 指的是正在运行中的函数的环境。(变量 _env 并没有在 Lua 中定义出来。我们仅仅出于解释的目的在这里写
18、出来。2.4.4 - 控制结构if、 while、以及 repeat 这些控制结构符合通常的意义,而且也有类似的语法:= while exp do block end= repeat block until exp= if exp then block elseif exp then block else block endLua 也有一个 for 语句,它有两种形式(参见 2.4.5)。控制结构中的条件表达式可以返回任何值。 false 和 nil 两者都被认为是假条件。所有不同于 nil 和 false 的其它值都被认为是真(特别需要注意的是,数字 0 和空字符串也被认为是真)。在 rep
19、eatuntil 循环中,内部语句块的结束点不是在 until 这个关键字处,它还包括了其后的条件表达式。因此,条件表达式中可以使用循环内部语句块中的定义的局部变量。return 被用于从函数或是 chunk(其实它就是一个函数)中返回值。 函数和 chunk 可以返回不只一个值,所以 return 的语法为 = return explist1break 被用来结束 while、 repeat、或 for 循环,它将忽略掉循环中下面的语句段的运行:= breakbreak 跳出最内层的循环。return 和 break 只能被写在一个语句块的最后一句。如果你真的需要从语句块的中间 return
20、 或是 break ,你可以使用显式的声名一个内部语句块。一般写作 do return end 或是 do break end,可以这样写是因为现在 return 或 break 都成了一个语句块的最后一句了。2.4.5 - For 语句for 有两种形式:一种是数字形式,另一种是一般形式。数字形式的 for 循环,通过一个数学运算不断的运行内部的代码块。下面是它的语法:= for Name = exp , exp , exp do block endblock 将把 name 作循环变量。从第一个 exp 开始起,直到第二个 exp 的值为止,其步长为第三个 exp 。更确切的说,一个 fo
21、r 循环看起来是这个样子 for v = e1, e2, e3 do block end这等价于代码: do local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3) if not (var and limit and step) then error() end while (step 0 and var = limit) or (step = limit) do local v = var block var = var + step end注意下面这几点: 所有三个控制表达式都只被运算一次,表达式的计算在循环开始之
22、前。这些表达式的结果必须是数字。 var 、limit 、以及 step 都是一些不可见的变量。这里给它们起的名字都仅仅用于解释方便。 如果第三个表达式(步长)没有给出,会把步长设为 1 。 你可以用 break 来退出 for 循环。 循环变量 v 是一个循环内部的局部变量;当 for 循环结束后,你就不能在使用它。如果你需要这个值,在退出循环前把它赋给另一个变量。一般形式的 for 通过一个叫作迭代器(iterators)的函数工作。每次迭代,迭代器函数都会被调用以产生一个新的值,当这个值为 nil 时,循环停止。一般形式的 for 循环的语法如下:= for namelist in ex
23、plist1 do block end namelist := Name , Namefor 语句好似这样 for var_1, , var_n in explist do block end它等价于这样一段代码: local f, s, var = explist while true do local var_1, , var_n = f(s, var) var = var_1 if var = nil then break end注意以下几点: explist 只会被计算一次。它返回三个值, 一个迭代器函数,一个状态,一个迭代器的初始值。 f、 s、 以及 var 都是不可见的变量。这里
24、给它们起的名字都只是为了解说方便。 你可以使用 break 来跳出 for 循环。 循环变量 var_i 对于循环来说是一个局部变量;你不可以在 for 循环结束后继续使用。如果你需要保留这些值,那么就在循环结束前赋值到别的变量里去。2.4.6 - 把函数调用作为语句段为了允许使用可能的副作用,函数调用可以被作为一个语句段执行:= functioncall在这种情况下,所有的返回值都被舍弃。函数调用在 2.5.8 中解释。2.4.7 - 局部变量声名局部变量可以在语句块中任何地方声名。声名可以包含一个初始化赋值操作:= local namelist = explist1如果有的话,初始化赋值操
25、作的行为等同于赋值操作(参见 2.4.3)。否则,所有的变量将被初始化为 nil。一个 chunk 同时也是一个语句块(参见 2.4.1),所以局部变量可以放在 chunk 中那些显式注明的语句块之外。这些局部变量的作用范围从声明起一直延伸到 chunk 末尾。局部变量的可见规则在 2.6 中解释。2.5 - 表达式Lua 中有这些基本表达式: exp := prefixexp= nil | false | true= Number= String= function= tableconstructor= .= exp binop exp= unop exp prefixexp := var | functioncall | ( exp )数字和字符串在 2.1 中解释;变量在 2.3 中解释;函数定义在 2.5.9 中解释;2.5.8 中解释; table 的构造在 2.5.7 中解释;可变参数的表达式写作三个点 (.) ,它只能被用在有可变参数的函数中;这些在 2.5.9 中解释。二元操作符包含有数学运算操作符(参见 2.5.1),比较操作符(参见 2.5.2),逻辑操作符(参见 2.5.3),以及连接操作符(参见 2.5.4)。一元操作符包括负号(参见see 2.5.1),取反 not(参见 2.5.3),和取长度操作符(参见
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1