LL(1)分析法浅理解

LL(1)文法是一种形式文法,其预测分析表每个单元格最多有一个产生式,分析时每次看一个符号。First集合代表非终结符推导出的所有可能序列的第一个字符集合,Follow集合则包含可能跟在非终结符后面的终结符。算法通过迭代更新First和Follow集合,直到稳定。此过程用于编译器的语法分析阶段。
摘要由CSDN通过智能技术生成

在这里插入图片描述

L L ( 1 ) LL(1) LL(1)文法后面两个两个条件都是为了保证预测分析表每个单元格中至多有一个产生式,这样预测分析时的工作每一步都是确定的。名称中的‘1’表示分析时每一步只需向右查看一个符号,实际上是通过 F o l l o w ( A ) Follow(A) Follow(A)实现的。第二个L表示最左推导,因此,在预测分析程序中应用某个产生式之后,将产生式的右部符号反序入栈,优先推导栈顶元素,即最左推导。

请添加图片描述

考虑 X X X为非终结符号的情形即可,终结符号的 F i r s t First First是只有自己作为元素构成的集合,进一步,候选式 α = A B \alpha=AB α=AB F i r s t ( α ) = F i r s t ( A ) First(\alpha)=First(A) First(α)=First(A),因此,只需要考虑非终结符号即可。前面三点执行一次,最后两点需迭代执行,因为 Y Y Y集合也是不断变化的,具体说是某个 Y i Y_i Yi变化,会通过产生式 X ⟶ Y . . . X\longrightarrow Y... XY...或者 X ⟶ Y 1 Y 2 . . . Y k X\longrightarrow Y_1Y_2...Y_k XY1Y2...Yk 引起 X X X的变化。

F i r s t ( X ) First(X) First(X)本质上是 X X X推导到最后,产生所有式子的第一个字符集合,因此 Y 1 Y 2 . . . Y i − 1 Y_1Y_2...Y_{i-1} Y1Y2...Yi1均包含 ϵ \epsilon ϵ的话,在它们均产生 ϵ \epsilon ϵ的式子中 Y i Y_{i} Yi的首符集也可以称为 X X X推导式子的首符,因此将 Y i Y_{i} Yi的首符集加入 X X X的首符集,此前 Y j ≤ ( i − 1 ) Y_{j\leq(i-1)} Yj(i1)已经执行了该操作。

算法执行过程,首先执行①②③,之后,反复对所有产生式不断执行④⑤(循环遍历),直到所有 F i r s t First First集合不再变化。

请添加图片描述

这里重复步骤中的①实际上也只执行一次,因为 F i r s t ( β ) First(\beta) First(β)不会变化,这里 β \beta β可以是终结/非终结符号。可以移至初始化步骤中。

F e l l o w ( A ) Fellow(A) Fellow(A)是可以紧跟着 A A A之后出现的终结字符集合, F i r s t , F e l l o w First,Fellow First,Fellow均是终结字符的集合,而且可以简化为仅对非终结字符进行求取。

请添加图片描述

非常明确,执行该产生式,要么出现 α \alpha α首符集中的字符,要么首符集中含有空字 ϵ \epsilon ϵ,且是当前非终结符 A A A F o l l o w Follow Follow中含有的某个终结符 b b b,自动匹配( )。

这里 α \alpha α首符集中含有 ϵ \epsilon ϵ实际上有 A → α → ϵ A\rightarrow \alpha \rightarrow\epsilon Aαϵ,因为只有某个字符可以推导出 ϵ \epsilon ϵ时,才将其加入首符集。因此,执行该产生式必须保证紧跟着 A A A的字符可以和当前输入 b b b匹配(字符 b b b F o l l o w ( A ) Follow(A) Follow(A)中出现)。

在这里插入图片描述
reference
山东大学编译原理郑艳伟老师ppt

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
LL(1)分析法是一种自顶向下的语法分析方法,它使用一个分析表来帮助确定应该使用哪个产生式来扩展当前的非终结符。下面是一个简单的LL(1)分析法的C++实现: ```cpp #include <iostream> #include <stack> #include <unordered_map> #include <vector> using namespace std; // 定义文法符号类型 enum SymbolType { TERMINAL, NONTERMINAL }; // 定义文法符号类 class Symbol { public: SymbolType type; string name; Symbol(SymbolType t, const string& n) : type(t), name(n) {} }; // 定义产生式类 class Production { public: Symbol nonterminal; // 非终结符 vector<Symbol> rhs; // 产生式右部 Production(const Symbol& n, const vector<Symbol>& r) : nonterminal(n), rhs(r) {} }; // 定义LL(1)分析表类 class LL1Table { public: unordered_map<string, unordered_map<string, Production>> table; // 添加一条表项 void addEntry(const string& nonterminal, const string& terminal, const Production& p) { table[nonterminal][terminal] = p; } // 获取一条表项 Production getEntry(const string& nonterminal, const string& terminal) const { auto it1 = table.find(nonterminal); if (it1 == table.end()) { throw "Invalid nonterminal symbol."; } auto it2 = it1->second.find(terminal); if (it2 == it1->second.end()) { throw "Invalid terminal symbol."; } return it2->second; } }; // 定义LL(1)分析器类 class LL1Parser { public: vector<Symbol> input; // 输入串 stack<Symbol> stack; // 符号栈 LL1Table table; // LL(1)分析LL1Parser(const vector<Symbol>& i, const LL1Table& t) : input(i), table(t) { stack.push(Symbol(TERMINAL, "$")); // 栈底 stack.push(Symbol(NONTERMINAL, "S")); // 初始状态 } // LL(1)分析 void parse() { int ip = 0; while (!stack.empty()) { Symbol s = stack.top(); if (s.type == TERMINAL) { // 终结符 if (s.name == input[ip].name) { stack.pop(); ip++; } else { throw "Syntax error."; } } else { // 非终结符 Production p = table.getEntry(s.name, input[ip].name); stack.pop(); for (auto it = p.rhs.rbegin(); it != p.rhs.rend(); ++it) { stack.push(*it); } } } cout << "Syntax is correct." << endl; } }; int main() { // 构造LL(1)分析LL1Table table; table.addEntry("S", "a", Production(Symbol(NONTERMINAL, "A"), {Symbol(TERMINAL, "a")})); table.addEntry("S", "b", Production(Symbol(NONTERMINAL, "B"), {Symbol(TERMINAL, "b")})); table.addEntry("A", "b", Production(Symbol(NONTERMINAL, "S"), {Symbol(TERMINAL, "b")})); table.addEntry("B", "a", Production(Symbol(NONTERMINAL, "S"), {Symbol(TERMINAL, "a")})); // 构造输入串 vector<Symbol> input = {Symbol(TERMINAL, "a"), Symbol(TERMINAL, "b"), Symbol(TERMINAL, "$")}; // 构造LL(1)分析LL1Parser parser(input, table); // 进行LL(1)分析 parser.parse(); return 0; } ``` 这个例子是对文法S->aA | bB, A->bS, B->aS的LL(1)分析,在代码中构造了LL(1)分析表,并使用输入串进行了分析。在实际应用中,需要根据具体的文法和输入串,构造相应的LL(1)分析表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

u小鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值