從零開始寫個編譯器吧 開始寫詞法分析器(1)

2021-09-20 06:06:04 字數 2583 閱讀 2111

上一章提到我要手寫詞法分析器這個狀態機,嗯,那就讓我們開始吧。

public class lexicalanalysis 

public lexicalanalysis(reader reader)

token read() throws ioexception, lexicalanalysi***ception

private state state;

private final linkedlisttokenbuffer = new linkedlist<>();

private stringbuilder readbuffer = null;

private void refreshbuffer(char c)

private void createtoken(type type)

private boolean readchar(char c) throws lexicalanalysi***ception

}

於是我又新增了一點**。可以看到,我放著 readchar() 函式的 todo 不管,又新增了乙個 readchar(char c) 的函式。因為我有這麼乙個考慮:對於readchar()方法而言,這個是乙個被動呼叫的函式,外部呼叫一下,就讀到乙個token。這樣設計,今後寫 parser 時讀取 token 會要簡單許多。而readchar(char c)是乙個主動呼叫的函式,它的實現部分直接處理接受的引數 char 就行了,而且也不必立即返回 token。這樣我在寫 readchar(char c) 本身的時候會簡單許多。

至此,readchar(char c) 就變成了乙個純粹用於實現狀態機功能的函式了。讓我們開始寫這個函式吧。

private state state;

private boolean readchar(char c) throws lexicalanalysi***ception else if(state == state.identifier) else if(state == state.sign) else if(state == state.annotation) else if(state == state.string) else if(state == state.regex) else if(state == state.space)

if(createtype != null)

return movecursor;

}

乙個典型的狀態機,處於不同狀態,對於接受的引數 char 進行不同的操作。同時,我可以通過 state = ?; 來改變狀態機的狀態。

這個函式會返回乙個 boolean 型別的變數,即 movecursor,這個變數表示,在讀完當前 char 之後,是否移動游標讀取下乙個字元。如果為 false,則該函式的下一次呼叫的引數與前一次呼叫的引數會一模一樣(因為游標沒有移動嘛)。

最自然的情況下 movecursor == true,就是讀了乙個字元以後再讀下乙個字元嘛。

嗯,然後開始填充這些 if-else 之間的空白吧,先從 normal 狀態開始。

private boolean readchar(char c) throws lexicalanalysi***ception 

else if(signparser.incharset(c))

else if(c == '#')

else if(c == '\"' | c == '\'')

else if(c == '`')

else if(include(space, c))

else if(c == '\n')

else if(c == '\0')

else

refreshbuffer(c);

} else if(state == state.identifier) else if(state == state.sign) else if(state == state.annotation) else if(state == state.string) else if(state == state.regex) else if(state == state.space)

if(createtype != null)

return movecursor;

}

填充的****現了一些新函式和變數,我將補充在下面。

private static final char space = new char ;

private boolean inidentifiersetbutnotrear(char c)

private boolean include(char range, char c) {

boolean include = false;

for(int i=0; i其中 include 函式用於判斷字元是否歸屬於某個集合。而 inidentifiersetbutnotrear 用於判定字元是否屬於標示符字元。這裡的標示符不僅限於使用者定義的標示符,還包括關鍵字和數字(第5章有提及)。

當然,還有乙個 signparser.incharset(c) ,不過這個我想推遲到今後的章節說明,此處就不多做展開。

從零開始寫個編譯器吧 編譯器的結構

自然,我們還是先從 tao 語言的編譯器下手吧。在動手寫編譯器之前,得容我將編譯器的結構進行進一步的劃分。編譯器可視為乙個黑盒,從其一端輸入源 另一端產出目標 此過程進一步拆分便有了如下形式。首先是 tokenizer 詞法分析器 它讀入乙個乙個字元,並將其合併成乙個乙個token 單詞 這些 to...

從零開始編譯安裝 Extcalc

extcalc 是個非常強大的計算器,同時也是乙個非常老的軟體包,已經不再更新。希望使用 apt get 或是 dnf 安裝已經不可能,更何況我是在 mips 的龍芯 3a 上。唯一的方法就是編譯安裝。我是在龍芯 3a4000 debian10 上完成安裝的,其他系統可能有些許差別。從它的 sour...

如何從零開始寫shell指令碼

如何從零開始寫shell指令碼 一 前言 為什麼我們需要使用shell 指令碼?難道我們之前學習的c c 不能夠完成shell指令碼語言的功能嗎,為什麼我們還需要學習shell指令碼?學習shell指令碼最大的好處是能夠輕易處理檔案與目錄之類的物件,如果同樣此類任務,利用c 或者c,則編寫程式很麻煩...