歡迎關注微信訂閱號

"Lua探索之旅"

Lua語法短小精悍,簡單易學,非開發人員也可以編寫一些簡單的指令碼,有開發經驗的人更是分分鐘上手。但麻雀雖小五臟俱全,要探索lua內部實現,必須全面掌握其語法。

推薦閱讀《lua5。1參考手冊》,該手冊為官方文件,由

雲風

(遊戲圈的大神,夢幻西遊技術負責人)翻譯。

Lua EBNF正規化

程式碼是程式設計思想的落地,核心在於設計思想,如非必要,不貼原始碼。

lua的語法規則是透過EBNF正規化定義的,理解了EBNF,也就理解了lua的語法分析,事實上lua的語法分析模組就是lua EBNF的程式碼實現。

lua EBNF如下圖所示,從本節開始將完整介紹各個推導式的含義。

重磅來襲 - Lua語法分析(1)

重磅來襲 - Lua語法分析(1)

chunk

-> { stat [`;`] }

{}表示0個或多個, []表示0個或1個,chunk可推導為

chunk -> stat

chunk -> stat; stat; 。。。

lua的一個執行單元稱為

chunk(資料塊)

,一個chunk可包含多條語句,比如

a

=

1

b

=

a

+

1

每一行表示一條

statement(語句)

,若一行有多條statement,用

分號

分隔

a

=

1

b

=

a

+

1

chunk可以儲存在一個檔案裡,也可以存入字串。當一個 chunk 被執行,首先它會被預編譯成虛擬機器中的指令序列,然後被虛擬機器解釋執行這些指令。

stat

-> ifstat | whilestat | dostat | forstat | repeatstat | funcstat | localstat | retstat | breakstat | exprstat

|表示或,一條statement可以有多種表達形式,比如賦值語句、if語句、for語句、return語句等

每條statement根據

第一個token

型別決定使用哪種產生式。比如

"a=1"

的第一個token為TK_NAME,

"if a>1 then ... end"

的第一個token為TOKEN_IF。

token型別和產生式的對應關係如下:

重磅來襲 - Lua語法分析(1)

重磅來襲 - Lua語法分析(1)

exprstat

-> func | assignment

除去if、while、local、return等控制語句,exprstat表示一條執行語句,包含賦值和函式呼叫兩類,如

"a=1"

"print()"

lua將表示式分為

主表示式 primaryexp

任意表達式 explist1

主表示式必須可以作為

左值

存在,能夠被賦值,比如

"a.b=1+2"

,其中

"a.b"

為主表示式,

"1+2"

不能為主表示式。

在exprstat裡,等號‘=’左邊為一個主表示式,用primaryexp解析,有4種表達形式:

exp

exp

exp

);

exp

exp

];

exp

exp

比如:

a; a(1+2); a["b"]; a.b

其他複雜形式為這4種形式的

遞迴

實現,如

a

b

c

=

a

exp1

exp1

=

b

c

再比如

a

b

c

)。

d

()

=

exp1

exp2

exp1

=

a

exp3

exp2

=

d

()

exp3

=

b

c

primaryexp先獲取一個

字首表示式 prefixexp

,如

"a()"

分解為

"a"

"()"

,其中

"a"

即為字首。

然後根據

第2個token

決定使用上面4種表達形式的哪一種,如

"a()"

使用

exp(exp)

形式解析。

primaryexp解析完後,判斷當前expr是否為函式呼叫,如:

"a()"

是函式呼叫

"a().b"

不是函式呼叫

"a().b()"

是函式呼叫

若為函式呼叫,exprstat使用

func

產生式,否則使用

assignment

產生式,作為賦值語句處理。

primaryexp

-> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs }

對應上面的4種表達形式

prefixexp。NAME 用於table取key值,如

"a.b"

等同於

"a['b']"

,這是lua的語法糖

prefixexp[exp] 用於table取key值,key可以為一個表示式

prefixexp:NAME() 用於table的成員函式呼叫,另一個語法糖,如

"a:b(c)"

等同於

"a.b(a,c)"

,自動將當前table物件作為第一個引數插入,類似c++的this指標

prefixexp() 用於函式呼叫,如

"a()"

prefixexp

-> NAME | `(` expr `)`

字首表示式可以為識別符號,也可以一個表示式

"a()"

,prefixexp使用

NAME

產生式

"(a+1).d=1"

,prefixexp使用

expr

產生式

不要懷疑,

"(a+1).d=1"

,在lua裡可以這樣寫,比如a為table,table又可以定義元方法,

過載+運算子

返回一個新table就可以了。

本節先介紹這幾個EBNF,敬請後續!