摘要:上一章提到我要手寫(xiě)詞法分析器這個(gè)狀態(tài)機(jī),嗯,那就讓我們開(kāi)始吧。實(shí)際上,在狀態(tài)機(jī)不斷接受字符的過(guò)程中,會(huì)先調(diào)用將其緩存,并在適當(dāng)?shù)臅r(shí)機(jī)調(diào)用生成。一個(gè)典型的狀態(tài)機(jī),處于不同狀態(tài),對(duì)于接受的參數(shù)進(jìn)行不同的操作。
上一章提到我要手寫(xiě)詞法分析器這個(gè)狀態(tài)機(jī),嗯,那就讓我們開(kāi)始吧。
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 狀態(tài)機(jī)邏輯... } }
于是我又添加了一點(diǎn)代碼??梢钥吹剑曳胖?readChar() 函數(shù)的 TODO 不管,又添加了一個(gè) readChar(char c) 的函數(shù)。因?yàn)槲矣羞@么一個(gè)考慮:對(duì)于readChar()方法而言,這個(gè)是一個(gè)被動(dòng)調(diào)用的函數(shù),外部調(diào)用一下,就讀到一個(gè)Token。這樣設(shè)計(jì),今后寫(xiě) Parser 時(shí)讀取 Token 會(huì)要簡(jiǎn)單許多。而readChar(char c)是一個(gè)主動(dòng)調(diào)用的函數(shù),它的實(shí)現(xiàn)部分直接處理接受的參數(shù) char 就行了,而且也不必立即返回 Token。這樣我在寫(xiě) readChar(char c) 本身的時(shí)候會(huì)簡(jiǎn)單許多。
實(shí)際上,在狀態(tài)機(jī)不斷接受字符的過(guò)程中,會(huì)先調(diào)用 readBuffer.append(...) 將其緩存,并在適當(dāng)?shù)臅r(shí)機(jī)調(diào)用 createToken(...) 生成 Token。
至此,readChar(char c) 就變成了一個(gè)純粹用于實(shí)現(xiàn)狀態(tài)機(jī)功能的函數(shù)了。讓我們開(kāi)始寫(xiě)這個(gè)函數(shù)吧。
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; }
一個(gè)典型的狀態(tài)機(jī),處于不同狀態(tài),對(duì)于接受的參數(shù) char 進(jìn)行不同的操作。同時(shí),我可以通過(guò) state = ?; 來(lái)改變狀態(tài)機(jī)的狀態(tài)。
這個(gè)函數(shù)會(huì)返回一個(gè) boolean 類型的變量,即 moveCursor,這個(gè)變量表示,在讀完當(dāng)前 char 之后,是否移動(dòng)游標(biāo)讀取下一個(gè)字符。如果為 false,則該函數(shù)的下一次調(diào)用的參數(shù)與前一次調(diào)用的參數(shù)會(huì)一模一樣(因?yàn)橛螛?biāo)沒(méi)有移動(dòng)嘛)。
最自然的情況下 moveCursor == true,就是讀了一個(gè)字符以后再讀下一個(gè)字符嘛。
嗯,然后開(kāi)始填充這些 if-else 之間的空白吧,先從 Normal 狀態(tài)開(kāi)始。
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 == "