# 3 – 語言定義
這一章描述了 Lua 的詞法、語法和句法。 換句話說,本章描述哪些符記是有效的, 它們如何被組合起來,這些組合方式有什么含義。
關于語言的構成概念將用常見的擴展 BNF 表達式寫出。 也就是這個樣子: {_a_} 表示 0 或多個 _a_, [_a_] 表示一個可選的 _a_。 可以被分解的非最終符號會這樣寫 non-terminal , 關鍵字會寫成這樣 **kword**, 而其它不能被分解的最終符號則寫成這樣 ‘**=**’ 。 完整的 Lua 語法可以在本手冊最后一章 [§9](#9) 找到。
## 3.1 – 詞法約定
Lua 語言的格式自由。 它會忽略語法元素(符記)間的空格(包括換行)和注釋, 僅把它們看作為名字和關鍵字間的分割符。
Lua 中的 _名字_ (也被稱為 _標識符_) 可以是由非數字打頭的任意字母下劃線和數字構成的字符串。 標識符可用于對變量、表的域、以及標簽命名。
下列 _關鍵字_ 是保留的,不可用于名字:
```
and break do else elseif end
false for function goto if in
local nil not or repeat return
then true until while
```
Lua 語言對大小寫敏感: `and` 是一個保留字,但 `And` 與 `AND` 則是兩個不同的有效名字。 作為一個約定,程序應避免創建以下劃線加一個或多個大寫字母構成的名字 (例如 [`_VERSION`](#pdf-_VERSION))。
下列字符串是另外一些符記:
```
+ - * / % ^ #
& ~ | << >> //
== ~= <= >= < > =
( ) { } [ ] ::
; : , . .. ...
```
_字面串_ 可以用單引號或雙引號括起。 字面串內部可以包含下列 C 風格的轉義串: '`\a`' (響鈴), '`\b`' (退格), '`\f`' (換頁), '`\n`' (換行), '`\r`' (回車), '`\t`' (橫項制表), '`\v`' (縱向制表), '`\\`' (反斜杠), '`\"`' (雙引號), 以及 '`\'`' (單引號)。 在反斜杠后跟一個真正的換行等價于在字符串中寫一個換行符。 轉義串 '`\z`' 會忽略其后的一系列空白符,包括換行; 它在你需要對一個很長的字符串常量斷行為多行并希望在每個新行保持縮進時非常有用。
Lua 中的字符串可以保存任意 8 位值,其中包括用 '`\0`' 表示的 0 。 一般而言,你可以用字符的數字值來表示這個字符。 方式是用轉義串 `\x_XX_`, 此處的 _XX_ 必須是恰好兩個字符的 16 進制數。 或者你也可以使用轉義串 `\_ddd_` , 這里的 _ddd_ 是一到三個十進制數字。 (注意,如果在轉義符后接著恰巧是一個數字符號的話, 你就必須在這個轉義形式中寫滿三個數字。)
對于用 UTF-8 編碼的 Unicode 字符,你可以用 轉義符 `\u{_XXX_}` 來表示 (這里必須有一對花括號), 此處的 _XXX_ 是用 16 進制表示的字符編號。
字面串還可以用一種 _長括號_ 括起來的方式定義。 我們把兩個正的方括號間插入 _n_ 個等號定義為 _第 n 級開長括號_。 就是說,0 級開的長括號寫作 `[[` , 一級開長括號寫作 `[=[` , 如此等等。 _閉長括號_也作類似定義; 舉個例子,4 級反的長括號寫作 `]====]` 。 一個 _長字面串_ 可以由任何一級的開長括號開始,而由第一個碰到的同級的閉長括號結束。 這種方式描述的字符串可以包含任何東西,當然特定級別的反長括號除外。 整個詞法分析過程將不受分行限制,不處理任何轉義符,并且忽略掉任何不同級別的長括號。 其中碰到的任何形式的換行串(回車、換行、回車加換行、換行加回車),都會被轉換為單個換行符。
字面串中的每個不被上述規則影響的字節都呈現為本身。 然而,Lua 是用文本模式打開源文件解析的, 一些系統的文件操作函數對某些控制字符的處理可能有問題。 因此,對于非文本數據,用引號括起來并顯式按轉義符規則來表述更安全。
為了方便起見, 當一個開長括號后緊接一個換行符時, 這個換行符不會放在字符串內。 舉個例子,假設一個系統使用 ASCII 碼 (此時 '`a`' 編碼為 97 , 換行編碼為 10 ,'`1`' 編碼為 49 ), 下面五種方式描述了完全相同的字符串:
```
a = 'alo\n123"'
a = "alo\n123\""
a = '\97lo\10\04923"'
a = [[alo
123"]]
a = [==[
alo
123"]==]
```
_數字常量_ (或稱為 _數字量_) 可以由可選的小數部分和可選的十為底的指數部分構成, 指數部分用字符 '`e`' 或 '`E`' 來標記。 Lua 也接受以 `0x` 或 `0X` 開頭的 16 進制常量。 16 進制常量也接受小數加指數部分的形式,指數部分是以二為底, 用字符 '`p`' 或 '`P`' 來標記。 數字常量中包含小數點或指數部分時,被認為是一個浮點數; 否則被認為是一個整數。 下面有一些合法的整數常量的例子:
```
3 345 0xff 0xBEBADA
```
以下為合法的浮點常量:
```
3.0 3.1416 314.16e-2 0.31416E1 34e1
0x0.1E 0xA23p-4 0X1.921FB54442D18P+1
```
在字符串外的任何地方出現以雙橫線 (`--`) 開頭的部分是 _注釋_ 。 如果 `--` 后沒有緊跟著一個開大括號, 該注釋為 _短注釋_, 注釋到當前行末截至。 否則,這是一段 _長注釋_ , 注釋區一直維持到對應的閉長括號。 長注釋通常用于臨時屏蔽掉一大段代碼。
## 3.2 – 變量
變量是儲存值的地方。 Lua 中有三種變量: 全局變量、局部變量和表的域。
單個名字可以指代一個全局變量也可以指代一個局部變量 (或者是一個函數的形參,這是一種特殊形式的局部變量)。
```
var ::= Name
```
名字指 [§3.1](#3.1) 中定義的標識符。
所有沒有顯式聲明為局部變量(參見 [§3.3.7](#3.3.7)) 的變量名都被當做全局變量。 局部變量有其 _作用范圍_ : 局部變量可以被定義在它作用范圍中的函數自由使用(參見 [§3.5](#3.5))。
在變量的首次賦值之前,變量的值均為 **nil**。
方括號被用來對表作索引:
```
var ::= prefixexp ‘**[**’ exp ‘**]**’
```
對全局變量以及表的域之訪問的含義可以通過元表來改變。 以索引方式訪問一個變量 `t[i]` 等價于 調用 `gettable_event(t,i)`。 (參見 [§2.4](#2.4) ,有一份完整的關于 `gettable_event` 函數的說明。 這個函數并沒有在 lua 中定義出來,也不能在 lua 中調用。這里我們把提到它只是方便說明問題。)
`var.Name` 這種語法只是一個語法糖,用來表示 `var["Name"]`:
```
var ::= prefixexp ‘**.**’ Name
```
對全局變量 `x` 的操作等價于操作 `_ENV.x`。 由于代碼塊編譯的方式, `_ENV` 永遠也不可能是一個全局名字 (參見 [§2.2](#2.2))。
## 3.3 – 語句
Lua 支持所有與 Pascal 或是 C 類似的常見形式的語句, 這個集合包括賦值,控制結構,函數調用,還有變量聲明。
### 3.3.1 – 語句塊
語句塊是一個語句序列,它們會按次序執行:
```
block ::= {stat}
```
Lua 支持 _空語句_, 你可以用分號分割語句,也可以以分號開始一個語句塊, 或是連著寫兩個分號:
```
stat ::= ‘**;**’
```
函數調用和賦值語句都可能以一個小括號打頭, 這可能讓 Lua 的語法產生歧義。 我們來看看下面的代碼片斷:
```
a = b + c
(print or io.write)('done')
```
從語法上說,可能有兩種解釋方式:
```
a = b + c(print or io.write)('done')
a = b + c; (print or io.write)('done')
```
當前的解析器總是用第一種結構來解析, 它會將開括號看成函數調用的參數傳遞開始處。 為了避免這種二義性, 在一條語句以小括號開頭時,前面放一個分號是個好習慣:
```
;(print or io.write)('done')
```
一個語句塊可以被顯式的定界為單條語句:
```
stat ::= **do** block **end**
```
顯式的對一個塊定界通常用來控制內部變量聲明的作用域。 有時,顯式定界也用于在一個語句塊中間插入 **return** (參見 [§3.3.4](#3.3.4))。
### 3.3.2 – 代碼塊
Lua 的一個編譯單元被稱為一個 _代碼塊_。 從句法構成上講,一個代碼塊就是一個語句塊。
```
chunk ::= block
```
Lua 把一個代碼塊當作一個擁有不定參數的匿名函數 (參見[§3.4.11](#3.4.11))來處理。 正是這樣,代碼塊內可以定義局部變量,它可以接收參數,返回若干值。 此外,這個匿名函數在編譯時還為它的作用域綁定了一個外部局部變量 `_ENV` (參見 [§2.2](#2.2))。 該函數總是把 `_ENV` 作為它唯一的一個上值, 即使這個函數不使用這個變量,它也存在。
代碼塊可以被保存在文件中,也可以作為宿主程序內部的一個字符串。 要執行一個代碼塊, 首先要讓 Lua _加載_ 它, 將代碼塊中的代碼預編譯成虛擬機中的指令, 而后,Lua 用虛擬機解釋器來運行編譯后的代碼。
代碼塊可以被預編譯為二進制形式; 參見程序 `luac` 以及函數 [`string.dump`](#pdf-string.dump) 可獲得更多細節。 用源碼表示的程序和編譯后的形式可自由替換; Lua 會自動檢測文件格式做相應的處理 (參見 [`load`](#pdf-load))。
### 3.3.3 – 賦值
Lua 允許多重賦值。 因此,賦值的語法定義是等號左邊放一個變量列表, 而等號右邊放一個表達式列表。 兩邊的列表中的元素都用逗號間開:
```
stat ::= varlist ‘**=**’ explist
varlist ::= var {‘**,**’ var}
explist ::= exp {‘**,**’ exp}
```
表達式放在 [§3.4](#3.4) 中討論。
在作賦值操作之前, 那值列表會被 _調整_ 為左邊變量列表的個數。 如果值比需要的更多的話,多余的值就被扔掉。 如果值的數量不夠需求, 將會按所需擴展若干個 **nil**。 如果表達式列表以一個函數調用結束, 這個函數所返回的所有值都會在調整操作之前被置入值列表中 (除非這個函數調用被用括號括了起來;參見 [§3.4](#3.4))。
賦值語句首先讓所有的表達式完成運算, 之后再做賦值操作。 因此,下面這段代碼
```
i = 3
i, a[i] = i+1, 20
```
會把 `a[3]` 設置為 20,而不會影響到 `a[4]` 。 這是因為 `a[i]` 中的 `i` 在被賦值為 4 之前就被計算出來了(當時是 3 )。 簡單說 ,這樣一行
```
x, y = y, x
```
會交換 `x` 和 `y` 的值, 及
```
x, y, z = y, z, x
```
會輪換 `x`,`y`,`z` 的值。
對全局變量以及表的域的賦值操作的含義可以通過元表來改變。 對 `t[i] = val` 這樣的變量索引賦值, 等價于 `settable_event(t,i,val)`。 (關于函數 `settable_event` 的詳細說明,參見 [§2.4](#2.4)。 這個函數并沒有在 Lua 中定義出來,也不可以被調用。 這里我們列出來,僅僅出于方便解釋的目的。)
對于全局變量 `x = val` 的賦值等價于 `_ENV.x = val` (參見 [§2.2](#2.2))。
### 3.3.4 – 控制結構
**if**, **while**, and **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** 語句,它有兩種形式 (參見 [§3.3.5](#3.3.5))。
控制結構中的條件表達式可以返回任何值。 **false** 與 **nil** 兩者都被認為是假。 所有不同于 **nil** 與 **false** 的其它值都被認為是真 (特別需要注意的是,數字 0 和空字符串也被認為是真)。
在 **repeat**–**until** 循環中, 內部語句塊的結束點不是在 **until** 這個關鍵字處, 它還包括了其后的條件表達式。 因此,條件表達式中可以使用循環內部語句塊中的定義的局部變量。
**goto** 語句將程序的控制點轉移到一個標簽處。 由于句法上的原因, Lua 里的標簽也被認為是語句:
```
stat ::= **goto** Name
stat ::= label
label ::= ‘**::**’ Name ‘**::**’
```
除了在內嵌函數中,以及在內嵌語句塊中定義了同名標簽,的情況外, 標簽對于它定義所在的整個語句塊可見。 只要 goto 沒有進入一個新的局部變量的作用域,它可以跳轉到任意可見標簽處。
標簽和沒有內容的語句被稱為_空語句_,它們不做任何操作。
**break** 被用來結束 **while**、 **repeat**、或 **for** 循環, 它將跳到循環外接著之后的語句運行:
```
stat ::= **break**
```
**break** 跳出最內層的循環。
**return** 被用于從函數或是代碼塊(其實它就是一個函數) 中返回值。 函數可以返回不止一個值,所以 **return** 的語法為
```
stat ::= **return** [explist] [‘**;**’]
```
**return** 只能被寫在一個語句塊的最后一句。 如果你真的需要從語句塊的中間 **return**, 你可以使用顯式的定義一個內部語句塊, 一般寫作 `do return end`。 可以這樣寫是因為現在 **return** 成了(內部)語句塊的最后一句了。
### 3.3.5 – For 語句
**for** 有兩種形式:一種是數字形式,另一種是通用形式。
數字形式的 **for** 循環,通過一個數學運算不斷地運行內部的代碼塊。 下面是它的語法:
```
stat ::= **for** Name ‘**=**’ exp ‘**,**’ exp [‘**,**’ exp] **do** block **end**
```
_block_ 將把 _name_ 作循環變量。 從第一個 _exp_ 開始起,直到第二個 _exp_ 的值為止, 其步長為第三個 _exp_ 。 更確切的說,一個 **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
_var_ = _var_ - _step_
while true do
_var_ = _var_ + _step_
if (_step_ >= 0 and _var_ > _limit_) or (_step_ < 0 and _var_ < _limit_) then
break
end
local v = _var_
_block_
end
end
```
注意下面這幾點:
* 所有三個控制表達式都只被運算一次, 表達式的計算在循環開始之前。 這些表達式的結果必須是數字。
* `_var_`,`_limit_`,以及 `_step_` 都是一些不可見的變量。 這里給它們起的名字都僅僅用于解釋方便。
* 如果第三個表達式(步長)沒有給出,會把步長設為 1 。
* 你可以用 **break** 和 **goto** 來退出 **for** 循環。
* 循環變量 `v` 是一個循環內部的局部變量; 如果你需要在循環結束后使用這個值, 在退出循環前把它賦給另一個變量。
通用形式的 **for** 通過一個叫作 _迭代器_ 的函數工作。 每次迭代,迭代器函數都會被調用以產生一個新的值, 當這個值為 **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_)
if _var_1_ == nil then break end
_var_ = _var_1_
_block_
end
end
```
注意以下幾點:
* `_explist_` 只會被計算一次。 它返回三個值, 一個 _迭代器_ 函數, 一個 _狀態_, 一個 _迭代器的初始值_。
* `_f_`, `_s_`,與 `_var_` 都是不可見的變量。 這里給它們起的名字都只是為了解說方便。
* 你可以使用 **break** 來跳出 **for** 循環。
* 環變量 `_var_i_` 對于循環來說是一個局部變量; 你不可以在 **for** 循環結束后繼續使用。 如果你需要保留這些值,那么就在循環跳出或結束前賦值到別的變量里去。
### 3.3.6 – 函數調用語句
為了允許使用函數的副作用, 函數調用可以被作為一個語句執行:
```
stat ::= functioncall
```
在這種情況下,所有的返回值都被舍棄。 函數調用在 [§3.4.10](#3.4.10) 中解釋。
### 3.3.7 – 局部聲明
局部變量可以在語句塊中任何地方聲明。 聲明可以包含一個初始化賦值操作:
```
stat ::= **local** namelist [‘**=**’ explist]
```
如果有初始化值的話,初始化賦值操作的語法和賦值操作一致 (參見 [§3.3.3](#3.3.3) )。 若沒有初始化值,所有的變量都被初始化為 **nil**。
一個代碼塊同時也是一個語句塊(參見 [§3.3.2](#3.3.2)), 所以局部變量可以放在代碼塊中那些顯式注明的語句塊之外。
局部變量的可見性規則在 [§3.5](#3.5) 中解釋。
## 3.4 – 表達式
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.1](#3.1) 中解釋; 變量在 [§3.2](#3.2) 中解釋; 函數定義在 [§3.4.11](#3.4.11) 中解釋; 函數調用在 [§3.4.10](#3.4.10) 中解釋; 表的構造在 [§3.4.9](#3.4.9) 中解釋。 可變參數的表達式寫作三個點('`...`'), 它只能在有可變參數的函數中直接使用;這些在 [§3.4.11](#3.4.11) 中解釋。
二元操作符包含有數學運算操作符(參見 [§3.4.1](#3.4.1)), 位操作符(參見 [§3.4.2](#3.4.2)), 比較操作符(參見 [§3.4.4](#3.4.4)), 邏輯操作符(參見 [§3.4.5](#3.4.5)), 以及連接操作符(參見 [§3.4.6](#3.4.6))。 一元操作符包括負號(參見 [§3.4.1](#3.4.1)), 按位非(參見 [§3.4.2](#3.4.2)), 邏輯非(參見 [§3.4.5](#3.4.5)), 和取長度操作符(參見 [§3.4.7](#3.4.7))。
函數調用和可變參數表達式都可以放在多重返回值中。 如果函數調用被當作一條語句(參見 [§3.3.6](#3.3.6)), 其返回值列表被調整為零個元素,即拋棄所有的返回值。 如果表達式被用于表達式列表的最后(或是唯一的)一個元素, 那么不會做任何調整(除非表達式被括號括起來)。 在其它情況下, Lua 都會把結果調整為一個元素置入表達式列表中, 即保留第一個結果而忽略之后的所有值,或是在沒有結果時, 補單個 **nil**。
這里有一些例子:
```
f() -- 調整為 0 個結果
g(f(), x) -- f() 會被調整為一個結果
g(x, f()) -- g 收到 x 以及 f() 返回的所有結果
a,b,c = f(), x -- f() 被調整為 1 個結果 (c 收到 nil)
a,b = ... -- a 收到可變參數列表的第一個參數,
-- b 收到第二個參數(如果可變參數列表中
-- 沒有實際的參數,a 和 b 都會收到 nil)
a,b,c = x, f() -- f() 被調整為 2 個結果
a,b,c = f() -- f() 被調整為 3 個結果
return f() -- 返回 f() 的所有返回結果
return ... -- 返回從可變參數列表中接收到的所有參數parameters
return x,y,f() -- 返回 x, y, 以及 f() 的所有返回值
{f()} -- 用 f() 的所有返回值創建一個列表
{...} -- 用可變參數中的所有值創建一個列表
{f(), nil} -- f() 被調整為一個結果
```
被括號括起來的表達式永遠被當作一個值。 所以, `(f(x,y,z))` 即使 `f` 返回多個值, 這個表達式永遠是一個單一值。 (`(f(x,y,z))` 的值是 `f` 返回的第一個值。 如果 `f` 不返回值的話,那么它的值就是 **nil** 。)
### 3.4.1 – 數學運算操作符
Lua 支持下列數學運算操作符:
* **`+`:** 加法
* **`-`:** 減法
* **`*`:** 乘法
* **`/`:** 浮點除法
* **`//`:** 向下取整除法
* **`%`:** 取模
* **`^`:** 乘方
* **`-`:** 取負
除了乘方和浮點除法運算, 數學運算按如下方式工作: 如果兩個操作數都是整數, 該操作以整數方式操作且結果也將是一個整數。 否則,當兩個操作數都是數字或可以被轉換為數字的字符串 (參見 [§3.4.3](#3.4.3))時, 操作數會被轉換成兩個浮點數, 操作按通常的浮點規則(一般遵循 IEEE 754 標準) 來進行,結果也是一個浮點數。
乘方和浮點除法 (`/`) 總是把操作數轉換成浮點數進行,其結果總是浮點數。 乘方使用 ISO C 函數 `pow`, 因此它也可以接受非整數的指數。
向下取整的除法 (`//`) 指做一次除法,并將商圓整到靠近負無窮的一側, 即對操作數做除法后取 floor 。
取模被定義成除法的余數,其商被圓整到靠近負無窮的一側(向下取整的除法)。
對于整數數學運算的溢出問題, 這些操作采取的策略是按通常遵循的以 2 為補碼的數學運算的 _環繞_ 規則。 (換句話說,它們返回其運算的數學結果對 _2<sup>64</sup>_ 取模后的數字。)
### 3.4.2 – 位操作符
Lua 支持下列位操作符:
* **`&`:** 按位與
* **`|`:** 按位或
* **`~`:** 按位異或
* **`>>`:** 右移
* **`<<`:** 左移
* **`~`:** 按位非
所有的位操作都將操作數先轉換為整數 (參見 [§3.4.3](#3.4.3)), 然后按位操作,其結果是一個整數。
對于右移和左移,均用零來填補空位。 移動的位數若為負,則向反方向位移; 若移動的位數的絕對值大于等于 整數本身的位數,其結果為零 (所有位都被移出)。
### 3.4.3 – 強制轉換
Lua 對一些類型和值的內部表示會在運行時做一些數學轉換。 位操作總是將浮點操作數轉換成整數。 乘方和浮點除法總是將整數轉換為浮點數。 其它數學操作若針對混合操作數 (整數和浮點數)將把整數轉換為浮點數; 這一點被稱為 _通常規則_。 C API 同樣會按需把整數轉換為浮點數以及 把浮點數轉換為整數。 此外,字符串連接操作除了字符串,也可以接受數字作為參數。
當操作需要數字時,Lua 還會把字符串轉換為數字。
當把一個整數轉換為浮點數時, 若整數值恰好可以表示為一個浮點數,那就取那個浮點數。 否則,轉換會取最接近的較大值或較小值來表示這個數。 這種轉換是不會失敗的。
將浮點數轉為整數的過程會檢查 浮點數能否被準確的表達為一個整數 (即,浮點數是一個整數值且在整數可以表達的區間)。 如果可以,結果就是那個數,否則轉換失敗。
從字符串到數字的轉換過程遵循以下流程: 首先,遵循按 Lua 詞法分析器的規則分析語法來轉換為對應的 整數或浮點數。 (字符串可以有前置或后置的空格以及一個符號。) 然后,結果數字再按前述規則轉換為所需要的類型(浮點或整數)。
從數字轉換為字符串使用非指定的人可讀的格式。 若想完全控制數字到字符串的轉換過程, 可以使用字符串庫中的 `format` 函數 (參見 [`string.format`](#pdf-string.format))。
### 3.4.4 – 比較操作符
Lua 支持下列比較操作符:
* **`==`:** 等于
* **`~=`:** 不等于
* **`<`:** 小于
* **`>`:** 大于
* **`<=`:** 小于等于
* **`>=`:** 大于等于
這些操作的結果不是 **false** 就是 **true**。
等于操作 (`==`)先比較操作數的類型。 如果類型不同,結果就是 **false**。 否則,繼續比較值。 字符串按一般的方式比較。 數字遵循二元操作的規則: 如果兩個操作數都是整數, 它們按整數比較; 否則,它們先轉換為浮點數,然后再做比較。
表,用戶數據,以及線程都按引用比較: 只有兩者引用同一個對象時才認為它們相等。 每次你創建一個新對象(一張表,一個用戶數據,或一個線程), 新對象都一定和已有且存在的對象不同。 相同引用的閉包一定相等。 有任何可察覺的差異(不同的行為,不同的定義)一定不等。
你可以通過使用 "eq" 元方法(參見 [§2.4](#2.4)) 來改變 Lua 比較表和用戶數據時的方式。
等于操作不會將字符串轉換為數字,反之亦然。 即,`"0"==0` 結果為 **false**, 且 `t[0]` 與 `t["0"]` 指代著表中的不同項。
`~=` 操作完全等價于 (`==`) 操作的反值。
大小比較操作以以下方式進行。 如果參數都是數字, 它們按二元操作的常規進行。 否則,如果兩個參數都是字符串, 它們的值按當前的區域設置來比較。 再則,Lua 就試著調用 "lt" 或是 "le" 元方法 (參見 [§2.4](#2.4))。 `a > b` 的比較被轉譯為 `b < a`, `a >= b` 被轉譯為 `b <= a`。
### 3.4.5 – 邏輯操作符
Lua 中的邏輯操作符有 **and**, **or**,以及 **not**。 和控制結構(參見 [§3.3.4](#3.3.4))一樣, 所有的邏輯操作符把 **false** 和 **nil** 都作為假, 而其它的一切都當作真。
取反操作 **not** 總是返回 **false** 或 **true** 中的一個。 與操作符 **and** 在第一個參數為 **false** 或 **nil** 時 返回這第一個參數; 否則,**and** 返回第二個參數。 或操作符 **or** 在第一個參數不為 **nil** 也不為 **false** 時, 返回這第一個參數,否則返回第二個參數。 **and** 和 **or** 都遵循短路規則; 也就是說,第二個操作數只在需要的時候去求值。 這里有一些例子:
```
10 or 20 --> 10
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
```
(在這本手冊中, `-->` 指前面表達式的結果。)
### 3.4.6 – 字符串連接
Lua 中字符串的連接操作符寫作兩個點('`..`')。 如果兩個操作數都是字符串或都是數字, 連接操作將以 [§3.4.3](#3.4.3) 中提到的規則把其轉換為字符串。 否則,會調用元方法 `__concat` (參見 [§2.4](#2.4))。
### 3.4.7 – 取長度操作符
取長度操作符寫作一元前置符 `#`。 字符串的長度是它的字節數(就是以一個字符一個字節計算的字符串長度)。
程序可以通過 `__len` 元方法(參見 [§2.4](#2.4)) 來修改對字符串類型外的任何值的取長度操作行為。
如果 `__len` 元方法沒有給出, 表 `t` 的長度只在表是一個 _序列_ 時有定義。 序列指表的正數鍵集等于 _{1..n}_ , 其中 _n_ 是一個非負整數。 在這種情況下,_n_ 是表的長度。 注意這樣的表
```
{10, 20, nil, 40}
```
不是一個序列,因為它有鍵 `4` 卻沒有鍵 `3`。 (因此,該表的正整數鍵集不等于 _{1..n}_ 集合,故而就不存在 _n_。) 注意,一張表是否是一個序列和它的非數字鍵無關。
### 3.4.8 – 優先級
Lua 中操作符的優先級寫在下表中,從低到高優先級排序:
```
or
and
< > <= >= ~= ==
|
~
&
<< >>
..
+ -
* / // %
unary operators (not # - ~)
^
```
通常, 你可以用括號來改變運算次序。 連接操作符 ('`..`') 和乘方操作 ('`^`') 是從右至左的。 其它所有的操作都是從左至右。
### 3.4.9 – 表構建
表構造子是一個構造表的表達式。 每次構造子被執行,都會構造出一張新的表。 構造子可以被用來構造一張空表, 也可以用來構造一張表并初始化其中的一些域。 一般的構造子的語法如下
```
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 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
```
構造子中賦值的次序未定義。 (次序問題只會對那些鍵重復時的情況有影響。)
如果表單中最后一個域的形式是 `exp` , 而且其表達式是一個函數調用或者是一個可變參數, 那么這個表達式所有的返回值將依次進入列表 (參見 [§3.4.10](#3.4.10))。
初始化域表可以在最后多一個分割符, 這樣設計可以方便由機器生成代碼。
### 3.4.10 – 函數調用
Lua 中的函數調用的語法如下:
```
functioncall ::= prefixexp args
```
函數調用時, 第一步,prefixexp 和 args 先被求值。 如果 prefixexp 的值的類型是 _function_, 那么這個函數就被用給出的參數調用。 否則 prefixexp 的元方法 "call" 就被調用, 第一個參數是 prefixexp 的值, 接下來的是原來的調用參數 (參見 [§2.4](#2.4))。
這樣的形式
```
functioncall ::= prefixexp ‘**:**’ Name args
```
可以用來調用 "方法"。 這是 Lua 支持的一種語法糖。 像 `v:name(args)` 這個樣子, 被解釋成 `v.name(v,args)`, 這里的 `v` 只會被求值一次。
參數的語法如下:
```
args ::= ‘**(**’ [explist] ‘**)**’
args ::= tableconstructor
args ::= LiteralString
```
所有參數的表達式求值都在函數調用之前。 這樣的調用形式 `f{fields}` 是一種語法糖用于表示 `f({fields})`; 這里指參數列表是一個新創建出來的列表。 而這樣的形式 `f'_string_'` (或是 `f"_string_"` 亦或是 `f[[_string_]]`) 也是一種語法糖,用于表示 `f('_string_')`; 此時的參數列表是一個單獨的字符串。
`return _functioncall_` 這樣的調用形式將觸發一次 _尾調用_。 Lua 實現了 _完全尾調用_(或稱為 _完全尾遞歸_): 在尾調用中, 被調用的函數重用調用它的函數的堆棧項。 因此,對于程序執行的嵌套尾調用的層數是沒有限制的。 然而,尾調用將刪除調用它的函數的任何調試信息。 注意,尾調用只發生在特定的語法下, 僅當 **return** 只有單一函數調用作為參數時才發生尾調用; 這種語法使得調用函數的所有結果可以完整地返回。 因此,下面這些例子都不是尾調用:
```
return (f(x)) -- 返回值被調整為一個
return 2 * f(x)
return x, f(x) -- 追加若干返回值
f(x); return -- 返回值全部被舍棄
return x or f(x) -- 返回值被調整為一個
```
### 3.4.11 – 函數定義
函數定義的語法如下:
```
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
```
(這個差別只在函數體內需要引用 `f` 時才有。)
一個函數定義是一個可執行的表達式, 執行結果是一個類型為 _function_ 的值。 當 Lua 預編譯一個代碼塊時, 代碼塊作為一個函數,整個函數體也就被預編譯了。 那么,無論何時 Lua 執行了函數定義, 這個函數本身就進行了 _實例化_(或者說是 _關閉_了)。 這個函數的實例(或者說是 _閉包_)是表達式的最終值。
形參被看作是一些局部變量, 它們將由實參的值來初始化:
```
parlist ::= namelist [‘**,**’ ‘**...**’] | ‘**...**’
```
當一個函數被調用, 如果函數并非一個 _可變參數函數_, 即在形參列表的末尾注明三個點 ('`...`'), 那么實參列表就會被調整到形參列表的長度。 變長參數函數不會調整實參列表; 取而代之的是,它將把所有額外的參數放在一起通過 _變長參數表達式_傳遞給函數, 其寫法依舊是三個點。 這個表達式的值是一串實參值的列表, 看起來就跟一個可以返回多個結果的函數一樣。 如果一個變長參數表達式放在另一個表達式中使用, 或是放在另一串表達式的中間, 那么它的返回值就會被調整為單個值。 若這個表達式放在了一系列表達式的最后一個, 就不會做調整了 (除非這最后一個參數被括號給括了起來)。
我們先做如下定義,然后再來看一個例子:
```
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, ... --> (nothing)
g(3, 4) a=3, b=4, ... --> (nothing)
g(3, 4, 5, 8) a=3, b=4, ... --> 5 8
g(5, r()) a=5, b=1, ... --> 2 3
```
結果由 **return** 來返回(參見 [§3.3.4](#3.3.4))。 如果執行到函數末尾依舊沒有遇到任何 **return** 語句, 函數就不會返回任何結果。
關于函數可返回值的數量限制和系統有關。 這個限制一定大于 1000 。
_冒號_ 語法可以用來定義 _方法_, 就是說,函數可以有一個隱式的形參 `self`。 因此,如下語句
```
function t.a.b.c:f (_params_) _body_ end
```
是這樣一種寫法的語法糖
```
t.a.b.c.f = function (self, _params_) _body_ end
```
## 3.5 – 可見性規則
Lua 語言有詞法作用范圍。 變量的作用范圍開始于聲明它們之后的第一個語句段, 結束于包含這個聲明的最內層語句塊的最后一個非空語句。 看下面這些例子:
```
x = 10 -- 全局變量
do -- 新的語句塊
local x = x -- 新的一個 'x', 它的值現在是 10
print(x) --> 10
x = x+1
do -- 另一個語句塊
local x = x+1 -- 又一個 'x'
print(x) --> 12
end
print(x) --> 11
end
print(x) --> 10 (取到的是全局的那一個)
```
注意這里,類似 `local x = x` 這樣的聲明, 新的 `x` 正在被聲明,但是還沒有進入它的作用范圍, 所以第二個 `x` 指向的是外面一層的變量。
因為有這樣一個詞法作用范圍的規則, 局部變量可以被在它的作用范圍內定義的函數自由使用。 當一個局部變量被內層的函數中使用的時候, 它被內層函數稱作 _上值_,或是 _外部局部變量_。
注意,每次執行到一個 **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`。