国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

littleGrow / 2848人閱讀

摘要:上一章提到我要手寫詞法分析器這個狀態機,嗯,那就讓我們開始吧。實際上,在狀態機不斷接受字符的過程中,會先調用將其緩存,并在適當的時機調用生成。一個典型的狀態機,處于不同狀態,對于接受的參數進行不同的操作。

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

    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 狀態機邏輯...
        }
    }

于是我又添加了一點代碼。可以看到,我放著 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 == "