迷惘的码农。
分类: Java
2008-08-15 11:08:14
Lua支持语句的常规集,类似于Pascal或C。该集包括赋值、控制结构、函数调用和变量声明。
Lua的执行单元(unit)称为单元(chunk)。一个单元只是一系列的语句,它们被顺序执行。每条语句可随意地以一个分号结束:
chunk ::= {stat [`;´]}
不允许空语句,因而‘;;
’不合法。
Lua将单元视为带不定参数的匿名函数进行处理(见)。正因如此,单元可定义局部变量、接收参数以及返回值。
单元可以存储在一个文件中或者存在于宿主程序的一个字符串中。当单元被执行时,首先它被预编译为虚拟机中的指令,然后编译后的代码被虚拟机的解释器执行。
但愿也可被预编译为二进制形式;细节参考程序luac
。源码中的程序和编译后的形式可交换的;Lua自动检测文件类型进而作相应地处理。
语句块(block)是一系列语句;从语法上讲,语句块同单元一样:
block ::= chunk
语句块可被显式地分隔以产生单条语句:
stat ::= do block end
显式的语句块有助于控制变量声明的作用预。有时显式的语句块也用于在另一个语句块中加入return或break语句(见)。
Lua允许多重赋值。因此,赋值得语法定义为左边一系列变量和右边一系列表达式。两个系列中的元素都用逗号分隔:
stat ::= varlist `=´ explist
varlist ::= var {`,´ var}
explist ::= exp {`,´ exp}
表达式在中讨论。
赋值以前,值列表被调整匹配变量列表的长度。如果值比需要的多,多出的值被丢弃。如果值比需要的少,就用适当数量的nil扩展值列表。如果表达式列表以一个函数调用结束,那么调整之前该调用所返回的所有值都进入值列表(除非该调用被扩在圆括号中;见)。
赋值语句首先计算它的所有表达式,然后才会执行赋值操作。因此代码
i = 3
i, a[i] = i+1, 20
设置a[3]
为20,而不影响a[4]
,因为a[i]
中的i
在它被赋值为4之前被计算(为3)。同样地,代码行
x, y = y, x
交换x
和y
的值。
向全局变量和表字段赋值的含义可通过元表被改变。向索引的变量t[i] = val
赋值等价于settable_event(t,i,val)
。(函数settable_event
的完整说明见。在Lua中该函数并不存在或不可访问。我们这儿用它只作说明的目的。)
向全局变量x = val
赋值等价于赋值_env.x = val
,后者又等价于
settable_event(_env, "x", val)
此处_env
是当前运行函数的环境。(在Lua中未定义变量_env
。我们这儿用它只作说明的目的。)
控制结构if,while和repeat具有通常的含义和相似的语法:
stat ::= while exp do block end
stat ::= repeat block until exp
stat ::= if exp then block {elseif exp then block} [else block] end
Lua也有for语句,它有两种风格(见)。
控制结构的条件表达式可能返回任何值。false和nil都作为假。所有不同于nil和false的值作为真(特别要注意,数字0和空字符串也是真)。
在repeat–until循环中,内部代码块并非截止于until关键字,而是在条件之后。所以,条件也能引用在循环体中声明的本地变量。
return语句用来从函数或单元(其实也是函数)中返回一些值。 函数和单元可以返回多个值,所以return语句的语法是
stat ::= return [explist]
break语句用于终止while,repeat或for循环的执行,跳到循环之后的下一条语句:
stat ::= break
break终止最内层的循环。
return和break语句只能写作代码块的最后一条last语句。如果确实需要在代码块中部return或break,可以用do return end
和do break end
这种方式显式地加入内部代码块,这样return和break就是他们的(内部)代码块中的最后一条语句了。
for语句有两种形式:数字形式和一般形式。
数字形式的for循环随着控制变量在算术级数(等差级数,arithmetic progression)中移动,重复执行一块代码。语法如下:
stat ::= for Name `=´ exp `,´ exp [`,´ exp] do block end
当name从第一个exp的值开始,直到它以第三个exp为步长通过第二个exp为止,block被重复执行。更确切地说,for语句类似
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 <= 0 and var >= limit) do
local v = var
block
var = var + step
end
end
注意事项如下:
var
,limit
和step
是不可见变量。此处的名字只为方便说明。
v
是循环局部的;不能在for结束或被打断之后使用。如果需要该值,可在打断或退出循环之前赋给另一个变量。
一般形式的for语句重复检查称为迭代器(iterator)的函数。每次迭代,调用迭代器函数以产生一个新值,如该值为nil则停止。一般形式的for循环语法如下:
stat ::= for namelist in explist do block end
namelist ::= Name {`,´ Name}
for语句类似
for var_1, ···, var_n in explist do block end
等价于代码:
do
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
block
end
end
注意下列事项:
explist
只计算一次。其结果是一个迭代器函数、一个状态机(state),以及第一个迭代器变量(iterator variable)的初始值。
f
,s
,和var
是不可见变量。此处的名字只为方便说明。
var_i
是循环局部的;for结束后不可使用。如果需要它们,在打断或退出循环之前把它们赋给其他的变量。
为了允许使用可能的副作用,函数调用可作为语句执行:
stat ::= functioncall
此时所有的返回值被丢弃。函数调用在中解释。
可在代码块内部的任何位置声明局部变量。声明时可赋初值:
stat ::= local namelist [`=´ explist]
如果存在,赋初值同样有多重赋值的语义(见)。否则,所有变量初始化为nil。
单元也是代码块(见),因此可在任何显式的代码块外面声明局部变量。这种局部变量的作用域延伸到单元末尾。
局部变量的可见性规则在中说明。