[編譯原理-詞法分析(一)] 輸入緩衝 雙緩衝區方案

前言

在實踐中, 一般須要向前看一個字符. 
好比, 當讀到一個 非字母或數字的字符 時才能肯定已經讀到一個標識符的結尾. 所以, 這個字符不是id詞素的一部分. 
採用雙緩衝區方案可以安全地處理向前看多個符號的問題. 而後, 將考慮一種改進方案, 使用"哨兵標記"來節約用於檢查緩衝區末端的時間. {P72}

前情提要

1、緩衝區對
2、哨兵標記
3、實現雙緩衝區

正文

1、緩衝區對
描述:
    兩個交替讀入的緩衝區, 容量爲N個字符, 使用系統命令一次性將N個字符讀入到緩衝區;
    若是輸入字符不足N個, 則有特殊字符EOF來標記文件結尾;
    程序維護兩個指針lexemeBegin和forward;
    lexemeBegin指向當前詞素的開始處, 當前正試圖肯定這個詞素的結尾;
    forward向前掃描, 直到與某個模式匹配爲止;
    當肯定該詞素時, forward指向該詞素結尾的字符;
    將詞素做爲摸個返回給語法分析器的詞法單元的屬性值記錄;
    lexemeBegin指向該詞素後的第一個字符, 而後將forward左移一個字符;
    在forward不斷掃描中, 檢查是否掃描到EOF, 若是是則將N個新字符讀入另一個緩衝區, 且將forward指向緩衝區頭部;
2、哨兵標記
當採用雙緩衝區方案, 那麼每次向前移動forward指針時, 都須要檢查是否到緩衝區結尾, 如果則加載另一個緩衝區.
若是擴展每一個緩衝區, 使它們在末尾包含一個哨兵(sentinel)字符, 就能夠把緩衝區末尾的測試和當前字符的測試結合在一塊兒, 這個字符選擇不會出如今源程序中的 EOF標記.

3、實現雙緩衝區

將使用<~> 標記來自哪一個文件安全

<~Buffer.h>

namespace Lexical_Analysis {

    template <int size = 1024>
    class Buffer {
    private:
        enum Tag { ONE, TWO }; // 緩衝區標號
    public:
        explicit Buffer(std::string _fileStr);
        ~Buffer() noexcept;

    public:

        char* lexemeBegin = nullptr;
        char* forward = nullptr;

        /**
         * @return 返回lexemeBegin 與 forward 的字符序列
         */
        std::string getString();

        /**
         * forward向前移動一個字符
         * @return 返回當前字符
         */
        char next();

        /**
         * @return 返回當前forward所指字符
         */
        char cur();

        /**
         * forward向後移動一個字符
         * @return 返回當前字符
         */
        char pre();

    private:
        std::string fileStr; // 文件路徑
        std::ifstream fileStream; // 文件流

        char buffer_1[size];
        char buffer_2[size];

        Buffer::Tag bufferTag = Tag::ONE; // 哪一個緩衝區

    private:

        /**
         * 從fileStream流讀取字符序列
         */
        void read();

    public:
        bool is_end = false; // 是否讀到結尾
    };
};
<~Buffer_TailAffix.h>

namespace Lexical_Analysis {


    template<int size>
    Buffer<size>::Buffer(std::string _fileStr):fileStr(std::move(_fileStr)) {
        fileStream.open(fileStr);

        buffer_1[size - 1] = EOF;
        fileStream.read(buffer_1, size - 1);

        lexemeBegin = forward = &buffer_1[0];
    }

    template<int size>
    Buffer<size>::~Buffer() noexcept {
        if (fileStream) {
            fileStream.close();
        }
    }

    template<int size>
    std::string Buffer<size>::getString() {
        std::stringstream ss;

        char* current = lexemeBegin;
        while (current != forward) {

            if (*current == EOF) {
                if (bufferTag == Tag::ONE) {
                    current = &buffer_1[0];
                } else if (bufferTag == Tag::TWO) {
                    current = &buffer_2[0];
                }
            }
            ss << *current++;
        }

        return ss.str();
    }

    template<int size>
    void Buffer<size>::read() {
        if (!fileStream) return ;

        /**
         * bufferTag 爲當前從文件流讀入的緩衝區標號
         * 將每一個緩衝區的末尾設置爲 哨兵標記
         */
        if (bufferTag == Tag::ONE) {
            // 當前在第一個緩衝區末尾, 裝載第二個緩衝區
            buffer_2[size - 1] = EOF;
            fileStream.read(buffer_2, size - 1);
            // 設置Tag爲第二個緩衝區, 而且設置forward爲第二個緩衝區的開頭
            bufferTag = Tag::TWO;
            forward = &buffer_2[0];
        } else if (bufferTag == Tag::TWO) {
            // 當前在第二個緩衝區末尾, 裝載第一個緩衝區
            buffer_1[size - 1] = EOF;
            fileStream.read(buffer_1, size - 1);
            // 設置Tag爲第一個緩衝區, 而且設置forward爲第一個緩衝區的開頭
            bufferTag = Tag::ONE;
            forward = &buffer_1[0];
        }
    }

    template<int size>
    char Buffer<size>::next() {
        char c = *forward;

        if (c == '\0') {
            // 終止詞法分析
            is_end = true;
            return '\0';
        }

        if (c == EOF) {
            // 已到緩衝區末尾標記
            read();
        }

        return *forward++;
    }

    template<int size>
    char Buffer<size>::cur() {
        return *forward;
    }

    template<int size>
    char Buffer<size>::pre() {
        /**
         * 會出現forward在某個緩衝區的第一個字符
         * 當回退時, 須要判斷是否切換過緩衝區, 若是是則回退到另外一個緩衝區的EOF
         * 若是沒有, 則不執行任何操做
         */
        char c = *forward;
        if (forward == &buffer_2[0]) {
            // 若是當前爲第二個緩衝區, 則一定切換過
            forward = &buffer_1[size - 1];
            return c;
        } else if (forward == &buffer_1[0] && buffer_2[0] != '\0') {
            // 當前爲第一個緩衝區, 而且第二個緩衝區有數據, 則認爲當前切換過緩衝區, 執行回退
            forward = &buffer_2[size - 1];
            return c;
        } else if (forward == &buffer_1[0] && buffer_2[0] == '\0') {
            // 當前爲第一個緩衝區, 而且第二個緩衝區沒有數據, 則認爲當前沒有切換過緩衝區, 不執行回退
            return c;
        }

        forward--;
        return c;
    }

};

尾記

只要從不須要越過實際詞素向前看很遠, 以致於這個詞素的長度加上向前看的距離大於N,就決不會識別這個詞素以前覆蓋尚在緩衝區的詞素 {P72}

lexemeBegin指針在第一個緩衝區, 而forward指針已經指向第二個緩衝區的EOF. 當forward向前移動一個字符時, 須要切換緩衝區, 這樣會致使將第一個緩衝區覆蓋.
相關文章
相關標籤/搜索