注释解析:

  1. 枚举类型 SymbolType 定义了 TERMINALNONTERMINAL 两个符号类型。

  2. 结构体 Symbol 定义了文法符号的类型和名称,重载了 < 运算符以便于使用 set 存储。

  3. 结构体 Item 定义了 SLR(1) 项,包括了产生式左部、右部、"." 的位置和向前看符号集合,重载了 < 运算符以便于使用 set 存储。

  4. 结构体 Grammar 定义了文法,包括了终结符集合、非终结符集合、产生式集合和开始符号。

  5. 结构体 SLR1Table 定义了 SLR(1) 分析表,包括了 Action 表和 Goto 表。

  6. 函数 first 计算了给定符号的 FIRST 集合。

  7. 函数 closure 计算了给定项集的 CLOSURE 集合。

  8. 函数 goto_ 计算了给定项集和符号的 GOTO 集合。

  9. 函数 lr0 构造了 LR(0) 自动机,并返回其 SLR(1) 分析表。

以上是代码中的注释解析,下面对代码中的函数 lr0 进行详细解析。

函数 lr0 的实现:

SLR1Table lr0(const Grammar& grammar) {
    SLR1Table table;  // 定义 SLR(1) 分析表
    set<set<Item>> states;  // 定义项集族
    set<Item> init_state = closure(grammar, {Item{grammar.start, {Symbol{NONTERMINAL, "S"}}, 0, {Symbol{TERMINAL, ""}}}});  // 构造初始项集
    states.insert(init_state);  // 将初始项集加入项集族
    map<set<Item>, int> state_id;  // 定义项集到状态编号的映射
    state_id[init_state] = 0;  // 初始项集的编号为 0
    int current_id = 1;  // 当前状态编号从 1 开始
    vector<set<Item>> state_list{init_state};  // 定义状态列表,初始项集为第一个状态
    for (size_t i = 0; i < state_list.size(); i++) {  // 遍历状态列表中的每一个状态
        const set<Item>& state = state_list[i];
        for (const Symbol& sym : grammar.terminals) {  // 对于每个终结符
            set<Item> next_state = goto_(grammar, state, sym);  // 计算 GOTO 集合
            if (!next_state.empty()) {  // 如果 GOTO 集合不为空
                if (!state_id.count(next_state)) {  // 如果 GOTO 集合对应的状态编号还没有分配
                    state_id[next_state] = current_id++;  // 分配一个新的状态编号
                    state_list.push_back(next_state);  // 将 GOTO 集合加入状态列表
                }
                table.action[{i, sym}] = state_id[next_state];  // 在 Action 表中记录移进动作
            }
        }
        for (const Symbol& sym : grammar.nonterminals) {  // 对于每个非终结符
            set<Item> next_state = goto_(grammar, state, sym);  // 计算 GOTO 集合
            if (!next_state.empty()) {  // 如果 GOTO 集合不为空
                if (!state_id.count(next_state)) {  // 如果 GOTO 集合对应的状态编号还没有分配
                    state_id[next_state] = current_id++;  // 分配一个新的状态编号
                    state_list.push_back(next_state);  // 将 GOTO 集合加入状态列表
                }
                table.goto_[{i, sym}] = state_id[next_state];  // 在 Goto 表中记录转移动作
            }
        }
        for (const Item& item : state) {  // 对于状态中的每个项
            if (item.dot == item.right.size()) {  // 如果 "." 在产生式右部的末尾
                if (item.left == grammar.start) {  // 如果产生式左部是开始符号
                    table.action[{i, Symbol{TERMINAL, "$"}}] = -1;  // 在 Action 表中记录接受动作
                } else {  // 如果产生式左部不是开始符号
                    for (const Symbol& lookahead : item.lookahead) {  // 对于每个向前看符号
                        table.action[{i, lookahead}] = -state_id[state];  // 在 Action 表中记录规约动作
                    }
                }
            }
        }
    }
    return table;  // 返回 SLR(1) 分析表
}

函数 lr0 的主要思路是构造 LR(0) 自动机并生成 SLR(1) 分析表。首先,根据文法和开始符号构造初始项集,并将其加入项集族。然后,遍历项集族中的每一个项集,对于每个终结符和非终结符,计算 GOTO 集合,并将其加入状态列表和项集族,并在 Action 表和 Goto 表中记录移进动作和转移动作。最后,对于每个状态中的每个项,如果 "." 在产生式右部的末尾,则在 Action 表中记录接受动作或规约动作。最终返回 SLR(1) 分析表

#include iostream#include map#include set#include vector#include stringusing namespace std; 终结符和非终结符的类型enum SymbolType TERMINAL 终结符 NONTERMINAL 非终结符; 文法符号struct Symbol SymbolType type;

原文地址: https://www.cveoy.top/t/topic/huBx 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录