摘要:現(xiàn)在,讓我們來動手寫編譯器的第一個個文件吧。如其名字所示,這個類實(shí)例化的對象用于表示詞法分析器的產(chǎn)物。我希望詞法分析器從源代碼中提取出語素,并根據(jù)上下文推測出單詞類型,從而構(gòu)造出對象。只需要構(gòu)造出類型即可,進(jìn)一步細(xì)分將在的構(gòu)造函數(shù)中進(jìn)行。
現(xiàn)在,讓我們來動手寫編譯器的第一個個java文件吧。本章要寫的類,是Token類。如其名字所示,這個類實(shí)例化的對象用于表示詞法分析器 Tokenizer 的產(chǎn)物。同時,也作為下一階段的語法分析器 Parser 的原料。
讓我們開始吧!先新建一個Token.java 于 src/com/taozeyu/taolan/analysis之中。
package com.taozeyu.taolan.analysis; public class Token { public static enum Type { Keyword, Number, Identifier, Sign, Annotation, String, RegEx, Space, NewLine, EndSymbol; } final Type type; final String value; Token(Type type, String value) { //TODO } }
如之前章節(jié)討論的一樣,Token對象應(yīng)該包含類型和語素兩個屬性。注意這個 Type 枚舉類型,其內(nèi)容就是我在上一章所說的 tao 語言應(yīng)該具備的10種單詞類型。
我希望詞法分析器從源代碼中提取出語素,并根據(jù)上下文推測出單詞類型,從而構(gòu)造出Token對象。但實(shí)際上,請注意Type這個枚舉類的三個類型:
Keyword, Number, Identifier
這三個類型不同之處?實(shí)際上這三個類型的形式極其類似(甚至 Keyword 和 Identifier 的形式是完全相同的),并且可以僅通過語素準(zhǔn)確判定其類型。因此,我希望對詞法分析器 Tokenizer 隱藏著三種類型的區(qū)別,將這三種類型統(tǒng)稱 Identifier,以簡化編碼。
Token(Type type, String value) { if(type == Type.Identifier) { char firstChar = value.charAt(0); if(firstChar >= "0" & firstChar < "9") { type = Type.Number; } else if(keywordsSet.contains(value)){ type = Type.Keyword; } } this.type = type; this.value = value; }
于是,Token 對 Tokenizer 隱藏了 Number、Keyword 類型。Tokenizer 只需要構(gòu)造出 Identifier 類型即可,進(jìn)一步細(xì)分將在 Token 的構(gòu)造函數(shù)中進(jìn)行。
特別的,構(gòu)造函數(shù)中引用了一個 keywordsSet 變量。實(shí)際上這個變量應(yīng)該包含所有 tao 語言的關(guān)鍵字。此處稍稍定義一下。
private static final HashSetkeywordsSet = new HashSet<>(); static { keywordsSet.add("if"); keywordsSet.add("when"); keywordsSet.add("elsif"); keywordsSet.add("else"); keywordsSet.add("while"); keywordsSet.add("begin"); keywordsSet.add("until"); keywordsSet.add("for"); keywordsSet.add("do"); keywordsSet.add("try"); keywordsSet.add("catch"); keywordsSet.add("finally"); keywordsSet.add("end"); keywordsSet.add("def"); keywordsSet.add("var"); keywordsSet.add("this"); keywordsSet.add("null"); keywordsSet.add("throw"); keywordsSet.add("break"); keywordsSet.add("continue"); keywordsSet.add("return"); keywordsSet.add("operator"); }
好吧,tao 語言我能想出的可能有的關(guān)鍵字都在這里了。如果有遺漏或者多余,其實(shí)以后再回過頭來改也沒問題。
特別的,對于 Annotation、String、RegEx ,它們在源代碼中存在的形式和具體的語素并不完全等同。
#我是注釋(回車)
"我是一個字符串"
^s+d+$
對于 Tokenizer 而言,它傾向于讀出如上一整行信息。但是僅僅只加下劃線的文字是Token的語素。因此,我還需要再構(gòu)造函數(shù)中對構(gòu)造參數(shù)value進(jìn)行進(jìn)一步提取,以得到正確的語素。
另外,EndSymbol 的語素必須為空,不管 Tokenizer 傳入什么參數(shù)都必須如此。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/64213.html
摘要:是的,這個系列將呈現(xiàn)一個完整的編譯器從無到有的過程。但在寫這個編譯器的過程中,我可不會偷工減料,該有的一定會寫上的。該語言的虛擬機(jī)將運(yùn)行于之上,同時編譯器將使用實(shí)現(xiàn)。我早有寫編譯器的想法之前沒寫過,故希望一邊寫編譯器一邊完成這個系列。 是的,這個系列將呈現(xiàn)一個完整的編譯器從無到有的過程。當(dāng)然,為了保證該系列內(nèi)容的簡潔(也為了降低難度),僅僅保證編譯器的最低要求,即僅能用。但在寫這個編譯...
摘要:對于而言,終結(jié)符與的是對應(yīng)的。這些內(nèi)容,我將其稱之為終結(jié)符的值。對于一個非終結(jié)符的產(chǎn)生式對于非終結(jié)符,其對象的字段則會表現(xiàn)成如下形式。對于里面的數(shù)組,其元素可能為終結(jié)符對象非終結(jié)符對象或表達(dá)式枚舉對象。 首先是 TerminalSymbol.java 即終結(jié)符。 package com.taozeyu.taolan.analysis; import java.util.HashSet...
摘要:即創(chuàng)建一個文件,并寫下如下內(nèi)容。然后以此緩存一個字符串,然后,通過調(diào)用來將這個字符串分割成一個一個運(yùn)算符,并生成。語言中根據(jù)運(yùn)算符長度分開儲存的。具體來說,如果運(yùn)算符對應(yīng)的字符串最短的是,最長的是。 上一章留下的那個大大的 TODO 表明,似乎還有什么東西沒寫完。沒錯,所以這一章我們來寫 Sign 狀態(tài)下的代碼。 所謂 Sign 狀態(tài),即是用來處理和生成 Sign 類型的 Token...
摘要:這樣的程序或稱工具有很多現(xiàn)成的可供選擇包括在平臺上可用的,但既然我這個系列叫做從零開始寫個編譯器吧,那顯然如果我用現(xiàn)成的工具,那是犯規(guī)行為。 Parser(語法分析器)的編寫相對于 Tokenizer (詞法分析器)要復(fù)雜得多,因此,在編寫之前可能也會鋪墊得更多一些。當(dāng)然,本系列旨在寫出一個編譯器,所以理論方面只會簡單介紹 tao 語言所涉及的部分。 之前的幾章中,我純手寫了tao 語...
摘要:在之前的章節(jié)第章從零開始寫個編譯器吧開始寫詞法分析器中我有說,我將函數(shù)設(shè)計(jì)成主動調(diào)用的形式,而則是被動調(diào)用的形式。接下來本系列將進(jìn)入編寫語法分析器的階段,不過在此之前,我將抽出一點(diǎn)時間介紹一下語言本身。 上周周末旅游去了,就沒更新了,雖然回到海拔0m的地區(qū),不過目前似乎還在缺氧,所以本次就少更點(diǎn)吧。 這章將結(jié)束詞法分析的部分。 在之前的章節(jié)(第7章從零開始寫個編譯器吧 - 開始寫詞...
閱讀 3226·2021-11-23 09:51
閱讀 3571·2021-11-09 09:46
閱讀 3679·2021-11-09 09:45
閱讀 2952·2019-08-29 17:31
閱讀 1870·2019-08-26 13:39
閱讀 2729·2019-08-26 12:12
閱讀 3627·2019-08-26 12:08
閱讀 2244·2019-08-26 11:31