使用BNF语法,{}表示0次或多次,[]表示0次或1次。
Lua是一种free-form语言。它忽略token之间的空格,换行和注释。
名字可以包含任何字母、数字和下划线,不以数字开头。名字不能为保留词汇。名字可以用用作variables, table fields, labels。
保留的keywords:
and break do else elseif end
false for function goto if in
local nil not or repeat return
then true until while
Lua是一个大小写敏感的语言。
字符串使用单引号或双引号,可以包含类似c的转义,如\a \b \f \n \r \t \v \\ \"
行尾的反斜杠导致字符串中包括一个换行。
\z跳过空白字符包括换行,这在长字符串缩进时很有用。
1 2 3 | > print("abcdfg\z >> sadfasdf") abcdfgsadfasdf |
任何字符可以表示成
\xXX
\ddd
XX是两个16进制数字
ddd是10进制数,注意必须有3个数字。
字符串字面量也可以表示成long format,使用long brackets包起来。
[===[
]===]
中间可以有任何个=号,=的个数叫做level。
这种形式的长字符串可以夸多行。
任何换行形式的序列被转换成一个简单的换行,如\n \r \r\n \n\r 都被转换成newline。
为了简单,如果long bracket的开头立即跟着一个换行,那么这个换行不被包含在字符串中。
数字常量可以写成一个可选的小数部分,也一个可选的10的指数,使用e或者E标识。
支持16进制常数,以0x或者0X开头。16进制支持二进制指数,使用p或者P标识。
数字包含点或者指数被当成浮点数,否则当成整数。
3 0xff 0xBEBADA
3.0
314e-2
0x0.1E 这里的E是16进制F前面的E,不是指数。
0x1p2 等价于1*(2^2)
注释以--开头,
如果--后面跟的不是long bracket,则是单行注释
如果--后面跟的是long bracket,则是多行注释,以long bracket结束
如
单行注释
1 | -- commet |
多行注释
1 2 3 4 | --[[ multiple line commet multiple line commet ]] |
[[可以替换成任何level的long bracket,如[=[和]=]
Lua有3种变量
gloabl variables, local variables, table fields
单个名字表示全局变量或者局部变量(或者函数的参数,一种特殊的局部变量)。
任何变量如果没有被声明为local,都是全局变量。
变量在第一次赋值之前,其值为nil。
方括号用来索引一个表
prefixexp [ exp ]
或者
prefixexp . Name 等价于 prefixexp [ "Name" ]
访问全局变量x等价于访问_ENV.x
1 2 3 | > a = 2 > print(_ENV["a"]) 2 |
Lua支持的表达式
assignments
control structures
function calls
variable declarations
块是表达式的列表,即多个表达式组成的块。
Lua使用空表达式分开表达式,分号是一个空表达式。
块以一个分号,或者两个连续的分号开始。
函数调用和赋值可以以括号开始,这可能导致歧异,如:
1 2 | a = b + c (print or io.write)('done') |
可以翻译成:
1 2 | a = b + c (print or io.write)('done') a = b + c ; (print or io.write)('done') |
LUA翻译成前者,为了避免这种情况,最好在每个括号前加一个分号
1 | ; (print or io.write)('done') |
块可以显示地产生一个单个表达式来分开
1 | do block end |
显式块可以用来控制变量声明的作用范围,显式块也用来在另一个块的中间添加return语句。
Lua编译的单元是chunk,语法上,一个chunk是一个block。
Lua处理chunk为一个匿名函数,有可变的参数。因此,一个chunk可以定义局部变量,接收参数,返回值。
普通的赋值
a = 2
多个赋值
varlist = explist
list之间用逗号分开
如
1 2 | i = 3 i, a[i] = i+1, 20 |
设置a[3] = 20,而不是a[4],先评估左边的。
x , y = y , x
交换x和y的值
全局变量赋值
x = val 等价于 _ENV.x = val
控制结构if, while, repeat
1 2 3 | while exp do block end repeat block until exp if exp then block {elseif exp then block } [else block] end |
lua也有for语句,其有两个变种。
条件表达式可以返回任何值,false和nil被认为是false,其它值被认为是true,
(注意:数字0和空字符串也被认为是true)。
repeat-until的内部块在exp之后才结束,因此exp条件表达式可以访问内部块的局部变量。
goto语句跳到一个标签
::label::
定义一个标签
1 2 3 4 5 6 7 | #!/usr/bin/env lua local a = 1 ::again:: a = a + 1 print(a) if a < 9 then goto again end |
break语句跳出while, repeat, for的循环
return语句用来从一个函数后者一个chunk返回值。
返回可以返回多个值。
return [explist] [;]
return只能写到block的最后一个statement,
如果想在中间返回,使用explicit inner block
do return end
for语句有两种形式:数字和通用
1 | for name '=' exp ',' exp [',' exp] do block end |
第3个exp是可选的,默认为1,从第一个按第三个步进到第二个表达式。
这3个表达式只在开始的时候被评估一次。
可以使用break或者goto来退出循环。
name是loop body的局部变量,外部不可见。
第二种形式
1 | for namelist in explist do block end |
for var_1, ..., var_n in explist do block end
等价于:
1 2 3 4 5 6 7 8 9 | do local f, s, var = explist while true do local var_1, ···, var_n = f(s, var) if var_1 == nil then break end var = var_1 block end end |
注意:
explist只评估一次。它的结果是一个迭代函数,一个状态,一个初始值。
f, s, var是内部不可见的变量,这里只是用于解释。
可以使用break来退出循环
循环变量var_i是loop的本地变量。不能用在end之后。
函数调用可以被执行为语句
stat ::= functioncall
所有的返回值被丢弃。
stat ::= local namelist ['=' explist]
如果explist足够多,则是multiple assignment,不够的所有的变量被初始化为nil
chunk也是一个block,所以局部变脸可以声明在一个chunk,在任何显示的block之外。
lua中基本的表达式如下:
exp ::= prefixexp
exp ::= nil | false | true
exp ::= Numeral
exp ::= LiteralString
exp ::= functiondef
exp ::= tableconstructor
exp ::= '...'
exp ::= exp binop exp
exp ::= unop exp
prefixexp ::= var | functioncall | '(' exp ')'
3个点 ... 是vararg表达式,只能用来vararg function里面。
binary operator包括算术运算,位运算,移位运算,逻辑运算,concatenation operator
unary operator包括减、位非,逻辑非,unary length operator
函数调用和vararg expressions都可能导致multiple values,如果函数调用被用作一个表达式,它的return list被调整为zero elements,因此丢掉所有的返回值。
如果一个表达式用作为一个expressions list的最后一个(或者唯一的一个),则没有调整(除非表达式使用圆括号括起来。
所有其它情况,lua调整result list到一个元素。
f() | 0 results |
g(f(), x) | f() is adjusted to 1 result |
g(x, f()) | g gets x plus all results from f() |
a,b,c = f(), x | f() is adjusted to 1 result (c get nil) |
a,b = ... | a获得第一个参数,b获得第二个参数 |
a,b,c = x, f() | f() is adjusted to 2 results |
a,b,c = f() | f() is adjuested to 3 results |
return f() | returns all results from f() |
return ... | return all received vararg arguments |
return x,y,f() | return x, y and all results from f() |
{f()} | creates a list with all results from f() |
{...} | creates a list with all vararg arguments |
{f(), nil} | f() is adjusted to 1 result |
任何使用圆括号括起来的表达式总是导致只有一个值,例如f(x,y,z)总是去第一个返回值。如果f没有返回值则为nil。
+ 加
- 减
* 乘
/ 浮点除
// floor division
% 模
^ 指数
- unary minus
如果所有的操作数是整数,则结果是整数。如果是两个操作数是数字或者可以转换为数字的字符串,则按浮点数运算。
指数和浮点除总是转算为浮点数运算。
floor division (//) 是round到负无穷的除法。
& |
~ 异或
>> <<
~ 按位取反
位操作总是把浮点数转换乘整数。
指数和浮点除总是把整数转换乘浮点数。
其它的运算符支持mixed numbers (integers and floats),会将整数转换成浮点数。
当期待一个数字时,lua会将字符串转换成数字。
==
~= 不等
<
>
<=
>=
== 比较它的操作数,如果类型不同,结果为false。Tables/userdata/threads比较它们的引用。
and
or
not
所有的逻辑运算把false和nil当成false,其它任何值都当成true。
not 总是返回false或者true
and 返回它的第一个参数,如果其为false或者nil的话。否则返回第二个参数。
or 返回它的第一个参数,如果其不是nil,且不是false。否者返回第二个参数。
and or只在必要时才会评估第二个参数。
字符串连接使用两个点号 ..
长度运算是一个unary prefix operator #
字符串的长度是字符串的字节数
表的长度,是一个表的边界,如果 t[1]是nil的话,长度是0,否则border满足,t[border]不是nil,t[border+1]是nil。只有一个border的表,是一个sequence,例如table {10,20,30,40,50} 只有一个border 5。{nil, 20, 30, nil, nil, 60, nil}就有多个border。如果table不是sequence,则#可能返回任何一个border。
下面从低到高的优先级
1 2 3 4 5 6 7 8 9 10 11 12 | or and < > <= >= ~= == | ~ & << >> .. + - * / // % unary operators (not # - ~) ^ |
语法
1 2 3 4 | tableconstructor ::= ‘{’ [fieldlist] ‘}’ fieldlist ::= field {fieldsep field} [fieldsep] field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp fieldsep ::= ‘,’ | ‘;’ |
大括号括起来,多个元素用都好或者分号分开。元素可以是:
[index]=value
name=value
value
3种形式。
1 | a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 } |
等价于
1 2 3 4 5 6 7 8 9 10 11 | do local t = {} t[f(1)] = g t[1] = "x" -- 1st exp t[2] = "y" -- 2nd exp t.x = 1 -- t["x"] = 1 t[3] = f(x) -- 3rd exp t[30] = 23 t[4] = 45 -- 4th exp a = t end |
赋值的顺序是未定义的,不要使用依赖顺序的赋值。
如果最后一个域是一个表达式,表达式是一个函数调用,或者vararg expression,则表达式返回的所有值都进入列表。
语法:
functioncall ::= prefixexp args
如果prefixexp是一个function 类型,则这个调用这个函数。否则prefixexp的call metamethod被调用。prefixexp的值作为第一个参数。跟着后面的是args指定的参数。
functioncall ::= prefixexp ‘:’ Name args
这个形式,可以用来调用method。
参数有下列语法
args ::= ‘(’ [explist] ‘)’
args ::= tableconstructor
args ::= LiteralString
所有的参数在被调用前被evaluate。
f{fileds}表调用形式等价于:f({fileds})
字符串调用形式,等价于字符串是第一个参数。
return functioncall 形式的函数调用叫做tail call。只有return 跟一个函数调用的形式才是tail call,如
return (f(x)) 不是tail call,return 2 * f(x) 也不是tail call。
语法:
functiondef ::= function funcbody
funcbody ::= ‘(’ [parlist] ‘)’ block end
下列语法简化了函数定义:
stat ::= function funcname funcbody
stat ::= local function Name funcbody
funcname ::= Name {‘.’ Name} [‘:’ Name]
function f () body end
等价于
f = function () body end
function t.a.b.c.f () body end
等价于
t.a.b.c.f = function () body end
local function f () body end
等价于
local f; f = function () body end
而不是
local f = function () body end
变长参数使用3个点 ...
function g(a, b, ...) end
冒号用来定义method,函数有一个隐含的额外参数self
function t.a.b.c:f (params) body end
等价于
t.a.b.c.f = function (self, params) body end
Lua是一个lexically scoped language。
本地变量的scope,在声明语句之后,直到包含变量声明的innermost block的最后一个non-void statement。
1 2 3 4 5 6 7 8 9 10 11 12 | 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 print(x) --> 12 end print(x) --> 11 end print(x) --> 10 (the global one) |
注意每执行一次local 语句,都定义一个新的本地变量:
1 2 3 4 5 6 | a = {} local x = 20 for i=1,10 do local y = 0 a[i] = function () y=y+1; return x+y end end |
创建10个匿名函数,每个使用不同的y变量,但是x变量相同。