摘要:上一章提到我要手寫詞法分析器這個狀態機,嗯,那就讓我們開始吧。實際上,在狀態機不斷接受字符的過程中,會先調用將其緩存,并在適當的時機調用生成。一個典型的狀態機,處于不同狀態,對于接受的參數進行不同的操作。
上一章提到我要手寫詞法分析器這個狀態機,嗯,那就讓我們開始吧。
public class LexicalAnalysis { private static enum State { Normal, Identifier, Sign, Annotation, String, RegEx, Space; } public LexicalAnalysis(Reader reader) { //TODO } Token read() throws IOException, LexicalAnalysisException { //TODO return null; } private State state; private final LinkedListtokenBuffer = new LinkedList<>(); private StringBuilder readBuffer = null; private void refreshBuffer(char c) { readBuffer = new StringBuilder(); readBuffer.append(c); } private void createToken(Type type) { Token token = new Token(type, readBuffer.toString()); tokenBuffer.addFirst(token); readBuffer = null; } private boolean readChar(char c) throws LexicalAnalysisException { //TODO 狀態機邏輯... } }
于是我又添加了一點代碼。可以看到,我放著 readChar() 函數的 TODO 不管,又添加了一個 readChar(char c) 的函數。因為我有這么一個考慮:對于readChar()方法而言,這個是一個被動調用的函數,外部調用一下,就讀到一個Token。這樣設計,今后寫 Parser 時讀取 Token 會要簡單許多。而readChar(char c)是一個主動調用的函數,它的實現部分直接處理接受的參數 char 就行了,而且也不必立即返回 Token。這樣我在寫 readChar(char c) 本身的時候會簡單許多。
實際上,在狀態機不斷接受字符的過程中,會先調用 readBuffer.append(...) 將其緩存,并在適當的時機調用 createToken(...) 生成 Token。
至此,readChar(char c) 就變成了一個純粹用于實現狀態機功能的函數了。讓我們開始寫這個函數吧。
private State state; private boolean readChar(char c) throws LexicalAnalysisException { boolean moveCursor = true; Type createType = null; if(state == State.Normal) { } 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) { createToken(createType); } return moveCursor; }
一個典型的狀態機,處于不同狀態,對于接受的參數 char 進行不同的操作。同時,我可以通過 state = ?; 來改變狀態機的狀態。
這個函數會返回一個 boolean 類型的變量,即 moveCursor,這個變量表示,在讀完當前 char 之后,是否移動游標讀取下一個字符。如果為 false,則該函數的下一次調用的參數與前一次調用的參數會一模一樣(因為游標沒有移動嘛)。
最自然的情況下 moveCursor == true,就是讀了一個字符以后再讀下一個字符嘛。
嗯,然后開始填充這些 if-else 之間的空白吧,先從 Normal 狀態開始。
private boolean readChar(char c) throws LexicalAnalysisException { boolean moveCursor = true; Type createType = null; if(state == State.Normal) { if(inIdentifierSetButNotRear(c)) { state = State.Identifier; } else if(SignParser.inCharSet(c)) { state = State.Sign; } else if(c == "#") { state = State.Annotation; } else if(c == """ | c == """) { state = State.String; } else if(c == "`") { state = State.RegEx; } else if(include(Space, c)) { state = State.Space; } else if(c == " ") { createType = Type.NewLine; } else if(c == "