转载出处:http://my.oschina.net/xhan/blog/310016
说明:
这个文档是 Lua1.1 的 doc 目录里的 manual.ps 文件。
原文版权归原作者所有,这篇翻译只是作为学习之用。如果翻译有不当之处,请参考原文。
-------------------以下是正文-------------------
编程语言 Lua 的参考手册
摘要:Lua 是嵌入式语言,被设计用来做为一个配置语言给其它程序使用。本文档描述 Lua 编程语言以及 Lua 程序和宿主 C 程序交互的 API。它展示了一些使用这些主要特点的例子。
--------------------------------------
1 简介
--------------------------------------
Lua 是一种嵌入式编程语言,支持过程式编程与数据描述功能。它经常做为别的程序的配置语言使用。Lua 是由 R. Ierusalimschy, L. H. de Figueiredo 和 W. Celes 设计, 由 W. Celes 实现。
Lua 被实现为由 C 语言写成的库。做为一个嵌入式语言,Lua 没有 "main" 函数的概念;它只能嵌入到宿主运行,叫做嵌入程序。宿主可以执行一段 Lua 代码,可以读写 Lua 变量,可以注册被 Lua 代码调用的 C 函数。通过注册 C 函数,Lua 可以扩展自己以应对不同的领域,从而创建可定制、共享语法框架的编程语言。
Lua 是自由分发的软件,正常提供没有什么保证。本文档里描述的这个实现可以从下面的匿名 ftp 得到:
www.lua.org/ftp/refman-1.1.ps.gz
--------------------------------------
2 环境和模块
--------------------------------------
Lua 中所有的语句都在一个全局环境中执行。这个全局环境持有所有的全局变量和函数,在嵌入语言一开始执行时进行初始化,并持续到结束。
这个全局环境可以用 Lua 代码或者嵌入程序来管理,可以通过 Lua 的实现库来读写全局变量。
全局变量不需要声明。所有的变量被认为是全局的除非明确声明为 local (详见 4.4.5 节,local 的声明)。在第一次赋值之前,全局变量的值为 nil。
Lua 的执行单元叫做模块(module) 。module 的语法为(注一):
module --> { statement | function }
一个模块可以包含语句和/或函数定义,可以在一个文件中或者在一个宿主程序的字符串中。当执行一个模块,首先它所有的函数和语句被编译,函数被添加到全局环境,然后语句按顺序执行。模块对于全局环境的所有修改是持久的,这些修改在模块结束后依然可见。修改包括全局变量的修改和新函数的定义(注二)
注一:这里如常见的扩展的 BNF 范式一样,{ a } 表示 0 个或多个 a,[ a ] 表示一个可选的 a,{ a } +(加号是右上角标)表示一个或多个 a。
注二:实际上,一个函数的定义就是给一个全局变量赋值。(详见第 3 节)。
--------------------------------------
3 类型
--------------------------------------
Lua 是动态类型语言。变量没有类型;只有值有类型。所有值含有自己的类型。所有,Lua 语言中没有类型定义。
Lua 中有七种基本数据类型:nil, number, string, function, Cfunction, userdata, 和 table。Nil 是值为 nil 的类型,它的主要性质就是和其它值不同。Number 表示实数(浮点型),string 是字符串。
函数是第一类值(first-class values)。这意味着他们可以存储在变量中,做为参数传递给其他函数或者做为结果返回。当函数被定义,它的函数体被编译并保存在一个给定名称的全局变量。Lua 可以调用(和操作)写在 Lua 或 C 中的函数;后者的类型是 Cfunction。
userdata 类型允许 Lua 变量保存任意的 C 指针(void*)。它相当于 C 语言中的 void* 型指针,所以在 Lua 中除分配和相等测试操作以外,其它的操作是无效的。
table 类型实现为关联数组,即可以用数字和字符串索引的数组。因此,该类型不仅可用于表示普通数组,也可以用于表示符号表,集合,记录等。为表示一个记录,Lua 使用字段名为下标。语言通过提供 a.name 这种表示作为 a["name"] 的语法糖。
需要特点注意的是 table 是对象而非值。变量不能包含 table,只能引用它。赋值,参数传递,返回总是会涉及到 table 的引用,而不会对 table 进行拷贝。而且,table 必须在使用前显式创建。详见 4.5.7 节。
--------------------------------------
4 语言
--------------------------------------
这节介绍 Lua 的词法,语法和语义。
-------------------
4.1 词法约定
-------------------
Lua 是区别大小写的语言。标识符可以是任何字母,数字,下划线组成的字符串,且首字母不可为数字。下面这些是保留的关键字,不可用做标识符:
and do else elseif end
function if local nil not
or repeat return until then while
下面的字符串留做它用:
~= <= >= < > = .. + - * / %
( ) { } [ ] @ ; , .
字符串常量可以由成对的单引号或双引号界定,并且可以包括 C 语言风格的转义序列 '\n', '\t' 和 '\r'。注释可在字符串外面的任何地方用两个连字符(--)开始,直到行尾。
数值常量可以由可选的小数部分,可选的指数部分写成。下面是一些有效的数值常量示例:
4 4. .4 4.57e-3 .3e12
-------------------
4.2 约定
-------------------
Lua 提供了一些自动转化。在字符串上的算术运算会试图把字符串转化为数值。更明确的说,字符串通过 C 函数 strtod 转化为数值。相反的,当一个数值参与字符串操作时,数值会被转化为字符串。按照以下规则:如果数值是一个整数,没有指数或小数点,则直接转化;否则,它和标准 C 函数 printf 用格式 "%g" 一样的格式转化。
-------------------
4.3 调整
-------------------
Lua 中的函数可以返回多个值。因为函数没有类型声明,编译器不知道函数会返回多少值。所以,有时候,值列表必须在运行时调整到给定长度:如果实际值多于所需,那么多余的值会被扔掉;如果需要的值多于实际的,根据需要在列表中进行 nil 扩展。调整也发生在其它地方,例如多重赋值。
-------------------
4.4 语句
-------------------
Lua 支持几乎所有常规的语句。常规的命令包括:赋值,控件结构和过程调用。非常规的命令包括 4.5.7 节中描述的表的构造函数,和局部变量的声明。
---------
4.4.1 块
---------
一个块(block)就是一个顺序执行的语句列表。任何语句都可以可选的后跟一个分号。
block --> { stat sc } [ret sc]
sc --> [ ';' ]
由于语义分析的原因, return 语句只能写作为一个块里的最后一句。这一个约束同时可以避免了“语句不可达”的错误。
---------
4.4.2 赋值
---------
Lua 支持多重赋值。所以,语法定义了赋值的左边可是一个变量列表,右边可以是一个表达式的列表。两个列表元素都以逗号分割。
stat --> varlist1 '=' explist1
varlist1 --> var { ',' var }
这个语句首先求出所有右边的值,再排列左边的变量,最后对其赋值。所以,可以这样交换两个变量的值,如下所示:
x, y = y, x
赋值前,值的列表被调整到和变量列表的长度相等(详见 4.3 节)。
一个名字可以指示一个全局变量或者局部变量。
var --> name
var --> var '[' exp1 ']' | var '.' |name
方括号用来索引 table。在这种情况下,var 经过求值必须是一个 table;否则,会有一个执行错误。语法 var.NAME 仅仅是 var['NAME'] 的语法糖。
---------
4.4.2 控制结构
---------
控制结构的条件表达式可以返回任何值。所有不是 nil 的值都被认为是真,nil 被认为是假。if, while 和 repeat 和别的语言中的意义一样。
stat --> while exp1 do block end
stat --> repeat block until exp1
stat --> if exp1 then block { elseif } [else block] end
elseif --> elseif exp1 then block
return 用于从函数中返回值。因为一个函数可以返回多个值,return 语句的语法是:
ret --> return explist
---------
4.4.2 表达式做为语句
---------
所有可能出现副作用的表达式都可以作为语句执行。这包括函数调用和列表的构造:
stat --> functioncall
stat --> tableconstructor
最后返回的值被丢弃。函数调用在 4.5.8 节解释,构造函数是 4.5.7 节的主题。
---------
4.4.2 局部声明
---------
局部变量可以在块中的任何位置声明。它的作用域从声明的地方开始,直到块结束。声明可以包含赋初始值。
stat --> local declist [init]
declist --> name { ',' name }
init --> '=' explist1
如果声明时含有赋初值操作,那么他和多重赋值有同样的语义。否则,所有的变量被初始化为 nil。
-------------------
4.5 表达式
-------------------
---------
4.5.1 简单表达式
---------
简单表达式是:
exp --> '(' exp ')'
exp --> nil
exp --> 'number'
exp --> 'literal'
exp --> var
数值(数值常量)和字符串常量在 4.1 节解释过了。变量在 4.4.2 节解释过了。
---------
4.5.2 算术运算符
---------
Lua 支持常见的算术运算符,意思也保持不变。这些操作符是二元操作符 +, -, *, / 和一元操作符 + 和 -。操作数必须是数值,或者可以根据 4.2 节中给出的规则转化为数值的字符串,
---------
4.5.3 关系运算符
---------
Lua 提供了以下的关系运算符:
< > <= >= ~= =
他们返回 nil 做为假,1 做为真。
相等首先比较两个操作数的类型。如果不同,结果为 nil。否则,比较它们的值。数值或字符串以常见的方式比较。表, Cfuntion 和函数按引用比较,也就是说,两个比较的表只有是同一个的时候才被认为是相等的。不等操作 ~= 和相等运算具有完全相反的结果。
其它的操作符只能应用于字符串或数值。如果一个参数是一个字符串,另一个也会被转化为字符串,并且它们的值以字典序进行比较。否则,两个都是数值的话,情况类似。
---------
4.5.4 逻辑运算符
---------
所有的逻辑运算符,如控制结构一样,认为 nil 为假而其它的都为真。像关系运算符一样,他们返回 nil 为假,1 为真。轮回运算符是:
and or not
and 和 or 是短路求值,也就是说,第二个操作数只在需要的时候才被求值。
---------
4.5.5 连接
---------
Lua 提供了一个字符串连接操作符“..",操作数必须是字符串或者数字,如果是数字的话,则按 4.2 节描述的规则进行转化。
---------
4.5.6 优先级
---------
操作符的优先级如下表所示,从低到高排列:
and or
< > <= >= ~= =
..
+ -
* /
not + (unary) - (unary)
二元操作符具体左结合性
---------
4.5.7 表的构造函数
---------
Table 的构造函数是创建表的构造函数。表的构造函数有不同的使用方法。最简单的一种是:
tableconstructor --> '@' '(' [exp1 ] ')'
这样的表达式会生成一个新的的空表。表的尺寸是可选的,并且可以给初始表的大小一个提示。Lua 中的表可以根据需要进行动态扩展无论初始大小是多大。
构造一个表并初始化一些字段可以用下面的语法:
tableconstructor --> '@' [name] fieldlist
这样的表达式会新建一个表,表的值是确定的,表中的一些字段根据 fieldlist 被初始化了。如果这里给出了 name,将会调用一个参数为这个表的 name 函数。该函数可以用来字段值,新建一些默认字段,或者用来做其它的有副作用的事。
fieldlist --> '{' [ffieldlist1 ] '}'
ffieldlist1 --> ffield { ',' ffield }
ffield --> name '=' exp
这些字段列表初始化表中具名的字段。如下面的例子:
a = @f { x = 1, y = 3 }
和下面的是等价的:
temp = @(2)
temp.x = 1
temp.y = 3
f(temp)
a = temp
可以使用下面的语法初始化列表
fieldlist --> '[' [lfieldlist1 ] ']'
lfieldlist1 --> exp { ',' exp }
例如:
a = @["v1", "vv"]
和下面的等价:
temp = @(2)
temp[1] = "v1"
temp[2] = "vv"
a = temp
在特定的情况下,下面的表达式是完全等价的:
@f{} @f[]
---------
4.5.8 函数调用
---------
函数调用语法如下:
functioncall --> var '(' [explist1 ] ')'
explist1 --> { exp1 ',' } exp
这里,var 可是是什么变量(全局的,局部的,下标索引的等),只要它的类型为 function 或 Cfunction. 所有的表达式参数在函数调用之前被从左到右求值;然后参数被调整到和函数调用所用的参数个数一致(详见 4.3 节);之后被赋值对实际参数。
因为一个函数可以返回多个值(详见 4.4.3 节),返回值的个数在使用之前会进行调整。如果一个函数作为语句使用(详见 4.4.4 节),它的返回结果会被调整到 0 个(也就是全部丢弃)。如果一个函数在需要一个值(语法中被表示为非终结的 exp1)的地方调用,它的返回结果会被调整到 1 个。如果一个函数在需要多个值的地方调用(语法上表示为非终结的 exp),不对返回结果进行调整。
-------------------
4.6 函数定义
-------------------
函数可以在模块中的任何全局层面定义;函数不可以定义在另一个函数中。函数定义的语法是:
function --> function name '(' [parlist1 ] ')' block end
当 Lua 发现一个函数定义,它把函数体编译为字节码保存在全局变量 name 中,类型为 function。
参数和局部变量的表现一样,由参数值进行初始化。
parlist1 --> 'name' { ',' name }
结果由 return 语句返回(见 4.4.3)节。如果执行到函数最后也没有 return 指令的话,函数不返回值。
(未完待续)