重磅來襲 - Lua語法分析(1)
歡迎關注微信訂閱號
"Lua探索之旅"
Lua語法短小精悍,簡單易學,非開發人員也可以編寫一些簡單的指令碼,有開發經驗的人更是分分鐘上手。但麻雀雖小五臟俱全,要探索lua內部實現,必須全面掌握其語法。
推薦閱讀《lua5。1參考手冊》,該手冊為官方文件,由
雲風
(遊戲圈的大神,夢幻西遊技術負責人)翻譯。
Lua EBNF正規化
程式碼是程式設計思想的落地,核心在於設計思想,如非必要,不貼原始碼。
lua的語法規則是透過EBNF正規化定義的,理解了EBNF,也就理解了lua的語法分析,事實上lua的語法分析模組就是lua EBNF的程式碼實現。
lua EBNF如下圖所示,從本節開始將完整介紹各個推導式的含義。
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型別和產生式的對應關係如下:
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,敬請後續!