警告??:這將是一個又臭又長的系列教程骇塘,教程結(jié)束的時(shí)候蘸朋,你將擁有一個除了性能差勁核无、擴(kuò)展性差、標(biāo)準(zhǔn)庫不完善之外藕坯,其他方面都和官方相差無幾的 Lua 語言解釋器团南。說白了,這個系列的教程實(shí)現(xiàn)的是一個玩具語言炼彪,僅供學(xué)習(xí)吐根,無實(shí)用性。請謹(jǐn)慎 Follow霹购,請謹(jǐn)慎 Follow佑惠,請謹(jǐn)慎 Follow。
這是本系列教程的第六篇齐疙,如果你沒有看過之前的文章膜楷,請從頭觀看。
前言
語法分析算是 SLua 解釋器中相對復(fù)雜的一個模塊了贞奋,為了避免篇幅過長赌厅,我們將分成多篇來講解,本文是第二部分轿塔。
上一節(jié)說到了 parseStatement 的實(shí)現(xiàn)特愿,現(xiàn)在我們就來分析一下 SLua 中到底有幾種類型的 Statement:
- Do Statament,這個語句的作用是將一個 Block 封裝成一個單獨(dú)的語句勾缭。類似于 C 語言中將一段代碼放在一對大括號中揍障,主要用于控制局部變量的作用域。
- While Statement
- If Statement
- Local Statement俩由,局部變量聲明語句毒嫡。
- Other Statement,全局變量聲明及變量賦值語句幻梯,沒錯兜畸,SLua 中的全局變量聲明語句和變量賦值語句的形式是一樣的努释。
解析 Do Statement
Do Statement 的解析代碼如下:
func (p *Parser) parseDoStatement() syntax.SyntaxTree {
p.nextToken()
assert(p.currentToken.Category == scanner.TokenDo, "not a do statement")
block := p.parseBlock()
if p.nextToken().Category != scanner.TokenEnd {
panic(&Error{
module: p.module,
token: p.currentToken,
str: "expect 'end' for 'do' statement",
})
}
return &syntax.DoStatement{Block: block}
}
首先,我們使用 assert 函數(shù)來確保當(dāng)前 Token 的類型為 TokenDo咬摇;然后調(diào)用 parseBlock 函數(shù)來解析 Do Statement 內(nèi)部的 Block伐蒂;緊接著,在解析完成后肛鹏,我們檢查下一個 Token 的類型是否為 TokenEnd逸邦,如果不是,則拋出錯誤龄坪。
怎么樣昭雌,很簡單吧。其他的語句的解析也跟這個類似健田,就像流水線一樣烛卧,一步接一步,直到解析完成妓局,將各部分的結(jié)果組裝起來总放,得到我們需要的東西。
解析 While Statement
在解析之前好爬,我們先來看一下之前在介紹抽象語法樹時(shí)提到過的 WhileStatement 的定義:
type WhileStatement struct {
Exp SyntaxTree
Block SyntaxTree
}
可以看到局雄,While Statement 包含兩部分,Exp 為用于判斷循環(huán)是否繼續(xù)的表達(dá)式存炮,Block 則是循環(huán)體炬搭。解析代碼如下:
func (p *Parser) parseWhileStatement() syntax.SyntaxTree {
p.nextToken()
assert(p.currentToken.Category == scanner.TokenWhile,
"not a while statement")
exp := p.parseExp()
if p.nextToken().Category != scanner.TokenDo {
panic(&Error{
module: p.module,
token: p.currentToken,
str: "expect 'do' for 'while' statement",
})
}
block := p.parseBlock()
if p.nextToken().Category != scanner.TokenEnd {
panic(&Error{
module: p.module,
token: p.currentToken,
str: "expect 'end' for 'while' statement",
})
}
return &syntax.WhileStatement {
Exp: exp,
Block: block,
}
}
首先,與解析 Do Statement 相同穆桂,首先我們通過調(diào)用 assert 保證當(dāng)前 Token 的類型為 TokenWhile宫盔;然后調(diào)用 parseExp 解析緊接著 TokenWhile 后面的循環(huán)表達(dá)式;正常情況下享完,表達(dá)式后面會跟著 TokenDo灼芭,所以,如果不是的話般又,我們就需要報(bào)告語法錯誤然后退出程序彼绷;緊跟在 TokenDo 之后的是一個做為函數(shù)體的 Block,我們調(diào)用之前定義好的 parseBlock 函數(shù)來解析它茴迁;最后寄悯,我們需要判斷緊跟在 Block 之后的是否為 TokenEnd,如果不是堕义,則需要報(bào)告語法錯誤热某。
parseExp 函數(shù)的實(shí)現(xiàn)是整個語法分析最復(fù)雜的部分,我會在后面的文章中詳細(xì)地講解它,目前只需要知道它用于解析表達(dá)式就行了昔馋。
解析 If Statement
相比于上面介紹的 Do Statement 和 While Statement,If Statement 的結(jié)構(gòu)較為復(fù)雜糖耸,它可以包含零到多個 Elseif 分支和零或一個 Else 分支秘遏,所以將整個解析過程放在一個函數(shù)中無疑會大大增加復(fù)雜度,所以我們將解析函數(shù)拆分成了多個函數(shù)嘉竟。
我們在第四篇文章中介紹過 If Statement 的結(jié)構(gòu):
由上圖可以看到邦危,If Statement 包含三個部分:Exp、True Branch舍扰、False Branch倦蚪,其中 True Branch 為表達(dá)式求值為真的時(shí)候執(zhí)行的 Block,而 FalseBranch 可以是 Elseif Statement 或 Else Statement 或 nil边苹。Elseif Statement 的構(gòu)成與 If Statement 相同陵且,Else Statement 則只包含一個 Block。
下面先來看一下 If Statement 的實(shí)現(xiàn):
func (p *Parser) parseIfStatement() syntax.SyntaxTree {
p.nextToken()
assert(p.currentToken.Category == scanner.TokenIf, "not a if statement")
exp := p.parseExp()
if p.nextToken().Category != scanner.TokenThen {
panic(&Error{
module: p.module,
token: p.currentToken,
str: "expect 'then' for 'if' statement",
})
}
trueBranch := p.parseBlock()
falseBranch := p.parseFalseBranchStatement()
return &syntax.IfStatement{
Exp: exp,
TrueBranch: trueBranch,
FalseBranch: falseBranch,
}
}
首先个束,自然是判斷當(dāng)前的 Token 類型是否為 TokenIf慕购;然后調(diào)用 parseExp 解析緊隨其后的表達(dá)式;然后判斷下一個 Token 的類型是否為 TokenThen茬底,如果不是沪悲,則報(bào)告語法錯誤;然后我們調(diào)用 parseBlock 來解析 True Branch阱表,調(diào)用 parseFalseBranchStatement 解析 False Branch殿如。
上面說過,F(xiàn)alse Branch 有三種情況最爬,容易想到涉馁,parseFalseBranchStatement 方法會根據(jù)下一個 Token 的類型是 TokenElseif、TokenElse 還是 TokenEnd 來執(zhí)行不同的動作烂叔,下面就來看一下具體的實(shí)現(xiàn):
func (p *Parser) parseFalseBranchStatement() syntax.SyntaxTree {
if p.lookAhead().Category == scanner.TokenElseif {
return p.parseElseifStatement()
} else if p.lookAhead().Category == scanner.TokenElse {
return p.parseElseStatement()
} else if p.lookAhead().Category == scanner.TokenEnd {
p.nextToken()
} else {
panic(&Error{
module: p.module,
token: p.lookAheadToken,
str: "expect 'end' for 'if' statement",
})
}
return nil
}
parseFalseBranchStatement 方法的實(shí)現(xiàn)很容易懂谨胞,這里就不再詳細(xì)解釋了。因?yàn)?Elseif Statement 與 If Statement 的形式完全相同蒜鸡,所以解析它的方法的實(shí)現(xiàn)也基本相同胯努,而 Else Statement 則只是簡單地調(diào)用一個 parseBlock 方法就可以了,也不再細(xì)說逢防,這兩個方法的實(shí)現(xiàn)在下面:
func (p *Parser) parseElseifStatement() syntax.SyntaxTree {
p.nextToken()
assert(p.currentToken.Category == scanner.TokenElseif,
"not a 'elseif' statement")
exp := p.parseExp()
if p.nextToken().Category != scanner.TokenThen {
panic(&Error{
module: p.module,
token: p.currentToken,
str: "expect 'then' for 'elseif' statement",
})
}
trueBranch := p.parseBlock()
falseBranch := p.parseFalseBranchStatement()
return &syntax.ElseifStatement{
Exp: exp,
TrueBranch: trueBranch,
FalseBranch: falseBranch,
}
}
func (p *Parser) parseElseStatement() syntax.SyntaxTree {
p.nextToken()
assert(p.currentToken.Category == scanner.TokenElse,
"not a 'else' statement")
block := p.parseBlock()
if p.nextToken().Category != scanner.TokenEnd {
panic(&Error{
module: p.module,
token: p.currentToken,
str: "expect 'end' for 'else' statement",
})
}
return &syntax.ElseStatement{Block: block}
}
本節(jié)就先說到這里叶沛,在下一節(jié)中,我們將繼續(xù)講解解析 Local Statement 和 Other Statement 的方法實(shí)現(xiàn)忘朝。
獲取源代碼
代碼已托管到 GitHub 上:SLua灰署,每一個階段的代碼我都會創(chuàng)建一個 release,你可以直接下載作為參照。雖然提供了源代碼溉箕,但并不建議直接復(fù)制粘貼晦墙,因?yàn)檫@樣學(xué)到的知識會很容易忘記。
如果你覺得這篇教程有幫助肴茄,請不要吝嗇給文章點(diǎn)個喜歡晌畅,我會更加有動力將這個系列寫下去。關(guān)注一下簡書和 GitHub 可以在第一時(shí)間獲得更新提醒哦寡痰。