当前位置:   article > 正文

[编译原理-词法分析(一)] 输入缓冲 双缓冲区方案_词法分析前的缓冲区实现

词法分析前的缓冲区实现

前言

在实践中, 通常需要向前看一个字符. 
比如, 当读到一个 非字母或数字的字符 时才能确定已经读到一个标识符的结尾. 因此, 这个字符不是id词素的一部分. 
采用双缓冲区方案能够安全地处理向前看多个符号的问题. 然后, 将考虑一种改进方案, 使用"哨兵标记"来节约用于检查缓冲区末端的时间. {P72}
  • 1
  • 2
  • 3

前情提要

一、缓冲区对
二、哨兵标记
三、实现双缓冲区
  • 1
  • 2
  • 3

正文

一、缓冲区对
描述:
    两个交替读入的缓冲区, 容量为N个字符, 使用系统命令一次性将N个字符读入到缓冲区;
    如果输入字符不足N个, 则有特殊字符EOF来标记文件结尾;
    程序维护两个指针lexemeBegin和forward;
    lexemeBegin指向当前词素的开始处, 当前正试图确定这个词素的结尾;
    forward向前扫描, 直到与某个模式匹配为止;
    当确定该词素时, forward指向该词素结尾的字符;
    将词素作为摸个返回给语法分析器的词法单元的属性值记录;
    lexemeBegin指向该词素后的第一个字符, 然后将forward左移一个字符;
    在forward不断扫描中, 检查是否扫描到EOF, 如果是则将N个新字符读入另外一个缓冲区, 且将forward指向缓冲区头部;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
二、哨兵标记
当采用双缓冲区方案, 那么每次向前移动forward指针时, 都需要检查是否到缓冲区结尾, 若是则加载另外一个缓冲区.
如果扩展每个缓冲区, 使它们在末尾包含一个哨兵(sentinel)字符, 就可以把缓冲区末尾的测试和当前字符的测试结合在一起, 这个字符选择不会出现在源程序中的 EOF标记.
  • 1
  • 2

三、实现双缓冲区

将使用<~> 标记来自哪个文件

<~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; // 是否读到结尾
    };
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
<~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;
    }

};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116

尾记

只要从不需要越过实际词素向前看很远, 以至于这个词素的长度加上向前看的距离大于N,就决不会识别这个词素之前覆盖尚在缓冲区的词素 {P72}

lexemeBegin指针在第一个缓冲区, 而forward指针已经指向第二个缓冲区的EOF. 当forward向前移动一个字符时, 需要切换缓冲区, 这样会导致将第一个缓冲区覆盖.
  • 1
  • 2
  • 3
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/278216
推荐阅读
相关标签
  

闽ICP备14008679号