摘要:從展開(kāi)式中,可以看出,除了這個(gè)非終結(jié)符,還有其他一些非終結(jié)符。是可能展開(kāi)的形式之一,在語(yǔ)言中,如下代碼就是一行典型的從表達(dá)式來(lái)看,它是由一個(gè)級(jí)表達(dá)式和一個(gè)類型的非終結(jié)符組成。但特別注意結(jié)尾的數(shù)量詞表明,整個(gè)非終結(jié)符都是可選的。
目前為止我們創(chuàng)建的文件列表:
|- com.taozeyu.taolan.analysis |- FirstSetConstructor |- LexicalAnalysis |- LexicalAnalysisException |- NonTerminalSymbol |- SignParser |- SyntacticDefine |- TerminalSymbol |- Token
我們來(lái)看看 SyntacticDefine.java 文件(142~159 行):
node(Exp.StartChunk).or(Exp.Chunk), node(Exp.Chunk).or( Exp.SpaceOrEnter, node().or(Exp.Line, Exp.Space, node().or(token(Type.NewLine), Exp.SpaceOrEnter) .or(token(Type.EndSymbol))).sign("?") ).sign("*"), node(Exp.Line).or(Exp.Command) .or(Exp.Operate) .or(Exp.DefineVariable) .or(Exp.DefineFunction) .or(Exp.IfElseChunk) .or(Exp.WhileChunk) .or(Exp.DoUntilChunk) .or(Exp.ForEachChunk) .or(Exp.TryCatch)
我們定義了三個(gè)命名了的非終結(jié)符:StartChunk、Chunk、Line。
其中 StartChunk 是我們展開(kāi)式的所有起點(diǎn),每一個(gè) tao 語(yǔ)言的源代碼文件都從 StartChunk 開(kāi)始展開(kāi)。這里我簡(jiǎn)單的將其展開(kāi)為 Chunk。
而 Chunk 表示一個(gè)語(yǔ)法塊,它由許多行(Line)構(gòu)成。但是在文法定義中,我必須為其每行的首位定義許多必要的雜項(xiàng)。從展開(kāi)式中,可以看出,除了 Line 這個(gè)非終結(jié)符,還有其他一些非終結(jié)符:SpaceOrEnter、Space、NewLine、EndSymbol。
這些非終結(jié)符的含義,可以在 SyntacticDefine.java 文件的對(duì)應(yīng)位置找到(可以使用 ctrl+F 搜索),只要找到它們對(duì)應(yīng)的展開(kāi)式,相信不難理機(jī)它們的含義。特別的 EndSymbol,即是 Tokenizer 代表源代碼解析結(jié)束的終止符。
Chunk 是語(yǔ)言文法定義中最重要的非終結(jié)符。
再看看 Line,這個(gè)非終結(jié)符可以簡(jiǎn)單的理解為“一行代碼”。自然,Chunk 就是由很多行代碼組成的。特定的一行代碼可以寫(xiě)很多東西,可能用于定義一個(gè)變量,也可能是一行賦值語(yǔ)句,也可能是在調(diào)用一個(gè)方法??傊?,它可以是很多很多種東西。從之前這段代碼中,可以看到,它有很多種展開(kāi)式。
大家可以通過(guò) GitHub 自行查看 Line 展開(kāi)后對(duì)應(yīng)的非終結(jié)符具體還能再如何展開(kāi),但受限于篇幅,我只會(huì)對(duì)其中一部分進(jìn)行講解。
node(Exp.Operate).or(Exp.L0Expression, Exp.When)
Operate 是 Line 可能展開(kāi)的形式之一,在 tao 語(yǔ)言中,如下代碼就是一行典型的 Operate:
count = size * 2 + 1
從表達(dá)式來(lái)看,它是由一個(gè) 0 級(jí)表達(dá)式(L0Expression)和一個(gè) When 類型的非終結(jié)符組成。
其中 L0Expression 代表一個(gè)表達(dá)式,它可能是一行賦值語(yǔ)句,也可能是一個(gè)函數(shù)調(diào)用,它是我們文法定義中一個(gè)比較復(fù)雜,但卻到處都會(huì)出現(xiàn)的東西,我會(huì)在之后的章節(jié)進(jìn)行簡(jiǎn)單介紹。
隨后,緊接而來(lái)的是 When 非終結(jié)符。我們來(lái)看看 When 的定義是怎么樣的:
node(Exp.When).or(node().or(Exp.SplitSpaceSign).sign("?"), node().or(token(Type.Keyword, "when"), Exp.SplitSpaceSign, Exp.L0Expression).sign("?"))
它主要由一個(gè) when 關(guān)鍵字緊接令一個(gè) 0 級(jí)表達(dá)式組成。但特別注意結(jié)尾的 .sign("?") 數(shù)量詞表明,整個(gè)非終結(jié)符 When 都是可選的。也就是說(shuō),Operate 后面不寫(xiě) when 也沒(méi)有關(guān)系。
一行寫(xiě)了 when 的 tao 語(yǔ)言代碼可能是如下形式:
count = size * 2 + 1 when size > 1
這是一種簡(jiǎn)寫(xiě)形式,在 tao 語(yǔ)言中等價(jià)于如下寫(xiě)法:
if size > 1 count = size * 2 + 1 end
接下來(lái),我們來(lái)看看 DefineVariable 非終結(jié)符:
node(Exp.DefineVariable).or(token(Type.Keyword, "var"), Exp.Space, Exp.DefineVariableElement, node().or(Exp.Space, token(Type.Sign, ","), Exp.SpaceOrEnter, Exp.DefineVariableElement).sign("*"))
注意到 DefineVariableElement 似乎還可以繼續(xù)展開(kāi):
node(Exp.DefineVariableElement).or(token(Type.Identifier), Exp.Space, node().or(token(Type.Sign, "="), Exp.Space, Exp.L0Expression).sign("?"))
這表明 tao 語(yǔ)言中定義局部變量的形式如下:
var cat = take_cat(), dog = Dog.alloc().init()
當(dāng)然,從 DefineVariableElement 的展開(kāi)式中的 .sign("?") 量詞可以知道,去掉等號(hào)以及等號(hào)后面的表達(dá)式,也是合法的局部變量定義:
var cat, dog
甚至,一次只定義一個(gè)變量,當(dāng)然也是可以的:
var cat
在接下來(lái),就是 IfElseChunk 了:
node(Exp.IfElseChunk).or( token(Type.Keyword, "if"), Exp.Space, Exp.L0Expression, Exp.SpaceOrEnter, Exp.Chunk, node().or(token(Type.Keyword, "elsif"), Exp.Space, Exp.L0Expression, Exp.SpaceOrEnter, Exp.Chunk).sign("*"), node().or(token(Type.Keyword, "else"), Exp.SpaceOrEnter, Exp.Chunk).sign("?"), Exp.SpaceOrEnter, token(Type.Keyword, "end"))
注意到,這里展開(kāi)出現(xiàn)了四種關(guān)鍵字:if、elsif、else、end。
tao 語(yǔ)言中,一個(gè)典型的 if-else-chunk 是如下這樣子的:
if check_condition(100) a = 1 + 1 elsif a > 2 a += 3 else a = 0 end
當(dāng)然,elsif 可以重復(fù)出現(xiàn)0~N 次:
if check_condition(100) a = 1 + 1 elsif a > 2 a += 3 elsif a > 3 a += 4 elsif a > 4 a += 7 else a = 0 end
而 else 是可選的,可以沒(méi)有:
if check_condition(100) a = 1 + 1 elsif a > 2 a += 3 end
也可以只剩下 if 和 end:
if check_condition(100) a = 1 + 1 end
從文法定義的角度來(lái)看,if-else-chunk 這種形式的變化,我通過(guò)數(shù)量詞 sign("*") 和 sign("?") 來(lái)控制。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/65512.html
摘要:一個(gè)非終結(jié)符可以被展開(kāi)稱為一個(gè)串,如上定義便是將這個(gè)非終結(jié)符展開(kāi)稱為一個(gè)又終結(jié)符和非終結(jié)符混合而成的串。特別注意我定義的方法僅僅用于修飾非終結(jié)符,而非展開(kāi)式,雖然這個(gè)例子中我的方法更靠近方法,但并意味著用于修飾展開(kāi)式。 各位久等了,本系列在新一年的連載中,形式會(huì)加入少許變化。首先,我會(huì)將 tao 語(yǔ)言編譯器(以及運(yùn)行環(huán)境)的全部?jī)?nèi)容貼在 GitHub 上,在閱讀本系列的時(shí)候,需要對(duì)照 ...
摘要:作為本系列的第一章,將考慮從何開(kāi)始下手。運(yùn)行環(huán)境解釋執(zhí)行編譯器產(chǎn)生的目標(biāo)代碼。從零開(kāi)始寫(xiě)個(gè)編譯器吧從何處下手的博客 作為本系列的第一章,將考慮從何開(kāi)始下手。既然寫(xiě)的是編譯器,那在此得明確編譯器長(zhǎng)什么樣子,進(jìn)一步,編譯器由哪幾部分構(gòu)成,其工作原理大概是怎樣的。了解了這些,才好下手。 簡(jiǎn)單來(lái)說(shuō),編譯器本身是一個(gè)程序,這個(gè)程序能將一種代碼(源代碼)翻譯成另一種代碼(目標(biāo)代碼)。簡(jiǎn)而言之就是如...
摘要:是的,這個(gè)系列將呈現(xiàn)一個(gè)完整的編譯器從無(wú)到有的過(guò)程。但在寫(xiě)這個(gè)編譯器的過(guò)程中,我可不會(huì)偷工減料,該有的一定會(huì)寫(xiě)上的。該語(yǔ)言的虛擬機(jī)將運(yùn)行于之上,同時(shí)編譯器將使用實(shí)現(xiàn)。我早有寫(xiě)編譯器的想法之前沒(méi)寫(xiě)過(guò),故希望一邊寫(xiě)編譯器一邊完成這個(gè)系列。 是的,這個(gè)系列將呈現(xiàn)一個(gè)完整的編譯器從無(wú)到有的過(guò)程。當(dāng)然,為了保證該系列內(nèi)容的簡(jiǎn)潔(也為了降低難度),僅僅保證編譯器的最低要求,即僅能用。但在寫(xiě)這個(gè)編譯...
摘要:這樣的程序或稱工具有很多現(xiàn)成的可供選擇包括在平臺(tái)上可用的,但既然我這個(gè)系列叫做從零開(kāi)始寫(xiě)個(gè)編譯器吧,那顯然如果我用現(xiàn)成的工具,那是犯規(guī)行為。 Parser(語(yǔ)法分析器)的編寫(xiě)相對(duì)于 Tokenizer (詞法分析器)要復(fù)雜得多,因此,在編寫(xiě)之前可能也會(huì)鋪墊得更多一些。當(dāng)然,本系列旨在寫(xiě)出一個(gè)編譯器,所以理論方面只會(huì)簡(jiǎn)單介紹 tao 語(yǔ)言所涉及的部分。 之前的幾章中,我純手寫(xiě)了tao 語(yǔ)...
摘要:詞法分析器本身就是一個(gè)狀態(tài)機(jī),生成這個(gè)狀態(tài)機(jī)有很多種方法,而我打算采取手寫(xiě)的方式。狀態(tài)機(jī)不斷從源代碼即一個(gè)字符串中讀入一個(gè)一個(gè)字符,讀到不同的字符將使?fàn)顟B(tài)機(jī)的狀態(tài)從一個(gè)狀態(tài)變化到另外一個(gè)狀態(tài)。 詞法分析器 Tokenizer 本身就是一個(gè)狀態(tài)機(jī),生成這個(gè)狀態(tài)機(jī)有很多種方法,而我打算采取手寫(xiě)的方式。因?yàn)?tao 語(yǔ)言的詞法還是相對(duì)比較簡(jiǎn)單的,手寫(xiě)不成問(wèn)題。 先新建一個(gè)LexicalAna...
閱讀 1412·2021-11-04 16:11
閱讀 3083·2021-10-12 10:11
閱讀 3014·2021-09-29 09:47
閱讀 1641·2021-09-22 15:40
閱讀 1046·2019-08-29 15:43
閱讀 2831·2019-08-29 13:50
閱讀 1610·2019-08-29 13:28
閱讀 2717·2019-08-29 12:54