成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從零開(kāi)始寫(xiě)個(gè)編譯器吧 - 開(kāi)始寫(xiě)詞法分析器(1)

littleGrow / 2851人閱讀

摘要:上一章提到我要手寫(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 LinkedList tokenBuffer = 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 == "